/[svn]/linuxsampler/trunk/src/testcases/LSCPTest.cpp
ViewVC logotype

Contents of /linuxsampler/trunk/src/testcases/LSCPTest.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 218 - (show annotations) (download)
Sun Aug 15 20:18:55 2004 UTC (19 years, 8 months ago) by schoenebeck
File size: 14436 byte(s)
bugfix in client receive code

1 #include "LSCPTest.h"
2
3 #include "../common/global.h"
4 #include "../common/optional.h"
5
6 #include <iostream>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <fcntl.h>
11 #include <sys/types.h>
12 #include <sys/socket.h>
13
14 CPPUNIT_TEST_SUITE_REGISTRATION(LSCPTest);
15
16 // Note:
17 // we have to declare all those variables which we want to use for all
18 // tests within this test suite static because there are side effects which
19 // occur on transition to the next test which would change the values of our
20 // variables
21 static Sampler* pSampler = NULL;
22 static LSCPServer* pLSCPServer = NULL;
23 static int hSocket = -1;
24
25 /// Returns first token from \a sentence and removes that token (and evtl. delimiter) from \a sentence.
26 static optional<string> __ExtractFirstToken(string* pSentence, const string Delimiter) {
27 if (*pSentence == "") return optional<string>::nothing;
28
29 string::size_type pos = pSentence->find(Delimiter);
30
31 // if sentence has only one token
32 if (pos == string::npos) {
33 string token = *pSentence;
34 *pSentence = "";
35 return token;
36 }
37
38 // sentence has more than one token, so extract the first token
39 string token = pSentence->substr(0, pos);
40 *pSentence = pSentence->replace(0, pos + 1, "");
41 return token;
42 }
43
44 // split the multi line response string into the individual lines and remove the last (delimiter) line and the line feed characters in all lines
45 static vector<string> __ConvertMultiLineMessage(string msg) {
46 vector<string> res;
47
48 // erase the (dot) delimiter line
49 static const string dotlinedelimiter = ".\r\n";
50 string::size_type pos = msg.rfind(dotlinedelimiter);
51 msg = msg.replace(pos, dotlinedelimiter.length(), "");
52
53 // now split the lines
54 static const string linedelimiter = "\r\n";
55 while (true) {
56 pos = msg.find(linedelimiter, 0);
57
58 if (pos == string::npos) break; // if we're done
59
60 // get the line without the line feed and put it at the end of the vector
61 string line = msg.substr(0, pos);
62 res.push_back(line);
63
64 // remove the line from the input string
65 pos += linedelimiter.length();
66 msg = msg.substr(pos, msg.length() - pos);
67 }
68
69 return res;
70 }
71
72
73 // LSCPTest
74
75 // returns false if the server could not be launched
76 bool LSCPTest::launchLSCPServer() {
77 const long timeout_seconds = 10; // we give the server max. 10 seconds to startup, otherwise we declare the startup as failed
78 try {
79 pSampler = new Sampler;
80 pLSCPServer = new LSCPServer(pSampler);
81 pLSCPServer->StartThread();
82 int res = pLSCPServer->WaitUntilInitialized(timeout_seconds);
83 if (res < 0) throw;
84
85 return true; // success
86 }
87 catch (...) {
88 pSampler = NULL;
89 pLSCPServer = NULL;
90 return false; // failure
91 }
92 }
93
94 // returns false if the server could not be destroyed without problems
95 bool LSCPTest::shutdownLSCPServer() {
96 try {
97 pLSCPServer->StopThread();
98 if (pLSCPServer) {
99 delete pLSCPServer;
100 pLSCPServer = NULL;
101 }
102 if (pSampler) {
103 delete pSampler;
104 pSampler = NULL;
105 }
106 return true; // success
107 }
108 catch (...) {
109 return false; // failure
110 }
111 }
112
113 // returns false if client connection to the LSCP server could not be established
114 bool LSCPTest::connectToLSCPServer() {
115 const int iPort = LSCP_PORT; // LSCP server listening port (from lscpserver.h)
116 hSocket = -1;
117
118 hostent* pHost = gethostbyname("localhost");
119 if (pHost == NULL) return false;
120
121 hSocket = socket(AF_INET, SOCK_STREAM, 0);
122 if (hSocket < 0) return false;
123
124 sockaddr_in addr;
125 memset((char*) &addr, 0, sizeof(sockaddr_in));
126 addr.sin_family = pHost->h_addrtype;
127 memmove((char*) &(addr.sin_addr), pHost->h_addr, pHost->h_length);
128 addr.sin_port = htons((short) iPort);
129
130 if (connect(hSocket, (sockaddr*) &addr, sizeof(sockaddr_in)) < 0) {
131 close(hSocket);
132 return false;
133 }
134
135 // set non-blocking mode
136 int socket_flags = fcntl(hSocket, F_GETFL, 0);
137 fcntl(hSocket, F_SETFL, socket_flags | O_NONBLOCK);
138
139 return true;
140 }
141
142 bool LSCPTest::closeConnectionToLSCPServer() {
143 //cout << "closeConnectionToLSCPServer()\n" << flush;
144 if (hSocket >= 0) {
145 close(hSocket);
146 hSocket = -1;
147 }
148 return true;
149 }
150
151 // send a command to the LSCP server
152 void LSCPTest::sendCommandToLSCPServer(string cmd) {
153 if (hSocket < 0) {
154 cout << "sendCommandToLSCPServer() error: client socket not ready\n" << flush;
155 return;
156 }
157 cmd += "\r\n";
158 send(hSocket, cmd.c_str(), cmd.length(), 0);
159 }
160
161 // wait until LSCP server answers with a single line answer
162 string LSCPTest::receiveSingleLineAnswerFromLSCPServer() {
163 string msg = receiveAnswerFromLSCPServer("\n");
164 // remove the line feed at the end
165 static const string linedelimiter = "\r\n";
166 string::size_type pos = msg.rfind(linedelimiter);
167 return msg.substr(0, pos);
168 }
169
170 /// wait until LSCP server answers with a multi line answer (throws LinuxSamplerException if optional timeout exceeded)
171 vector<string> LSCPTest::receiveMultiLineAnswerFromLSCPServer(uint timeout_seconds) throw (LinuxSamplerException) {
172 string msg = receiveAnswerFromLSCPServer("\n.\r\n", timeout_seconds);
173 return __ConvertMultiLineMessage(msg);
174 }
175
176 /// wait until LSCP server answers with the given \a delimiter token at the end (throws LinuxSamplerException if optional timeout exceeded or socket error occured)
177 string LSCPTest::receiveAnswerFromLSCPServer(string delimiter, uint timeout_seconds) throw (LinuxSamplerException) {
178 string message;
179 char c;
180 fd_set sockSet;
181 timeval timeout;
182
183 while (true) {
184 if (timeout_seconds) {
185 FD_ZERO(&sockSet);
186 FD_SET(hSocket, &sockSet);
187 timeout.tv_sec = timeout_seconds;
188 timeout.tv_usec = 0;
189 int res = select(hSocket + 1, &sockSet, NULL, NULL, &timeout);
190 if (!res) { // if timeout exceeded
191 if (!message.size()) throw LinuxSamplerException("LSCPTest::receiveAnswerFromLSCPServer(): timeout (" + ToString(timeout_seconds) + "s) exceeded, no answer received");
192 else throw LinuxSamplerException("LSCPTest::receiveAnswerFromLSCPServer(): timeout (" + ToString(timeout_seconds) + "s) exceeded waiting for expected answer (end), received answer: \'" + message + "\'");
193 }
194 else if (res < 0) {
195 throw LinuxSamplerException("LSCPTest::receiveAnswerFromLSCPServer(): select error");
196 }
197 }
198
199 // there's something to read, so read one character
200 int res = recv(hSocket, &c, 1, 0);
201 if (!res) throw LinuxSamplerException("LSCPTest::receiveAnswerFromLSCPServer(): connection to LSCP server closed");
202 else if (res < 0) {
203 switch(errno) {
204 case EBADF:
205 throw LinuxSamplerException("The argument s is an invalid descriptor");
206 case ECONNREFUSED:
207 throw LinuxSamplerException("A remote host refused to allow the network connection (typically because it is not running the requested service).");
208 case ENOTCONN:
209 throw LinuxSamplerException("The socket is associated with a connection-oriented protocol and has not been connected (see connect(2) and accept(2)).");
210 case ENOTSOCK:
211 throw LinuxSamplerException("The argument s does not refer to a socket.");
212 case EAGAIN:
213 continue;
214 //throw LinuxSamplerException("The socket is marked non-blocking and the receive operation would block, or a receive timeout had been set and the timeout expired before data was received.");
215 case EINTR:
216 throw LinuxSamplerException("The receive was interrupted by delivery of a signal before any data were available.");
217 case EFAULT:
218 throw LinuxSamplerException("The receive buffer pointer(s) point outside the process's address space.");
219 case EINVAL:
220 throw LinuxSamplerException("Invalid argument passed.");
221 case ENOMEM:
222 throw LinuxSamplerException("Could not allocate memory for recvmsg.");
223 default:
224 throw LinuxSamplerException("Unknown recv() error.");
225 }
226 }
227
228 message += c;
229 string::size_type pos = message.rfind(delimiter); // ouch, but this is only a test case, right? ;)
230 if (pos != string::npos) return message;
231 }
232 }
233
234
235
236 void LSCPTest::printTestSuiteName() {
237 cout << "\b \nRunning LSCP Tests: " << flush;
238 }
239
240 void LSCPTest::setUp() {
241 }
242
243 void LSCPTest::tearDown() {
244 }
245
246
247
248 // Check if we can launch the LSCP Server (implies that there's no other instance running at the moment).
249 void LSCPTest::testLaunchLSCPServer() {
250 //cout << "testLaunchLSCPServer()\n" << flush;
251 CPPUNIT_ASSERT(launchLSCPServer());
252 }
253
254 // Check if we can connect a client connection to the LSCP server and close that connection without problems.
255 void LSCPTest::testConnectToLSCPServer() {
256 //cout << "testConnectToLSCPServer()\n" << flush;
257 sleep(1); // wait 1s
258 CPPUNIT_ASSERT(connectToLSCPServer());
259 sleep(2); // wait 2s
260 CPPUNIT_ASSERT(closeConnectionToLSCPServer());
261 }
262
263 // Check "ADD CHANNEL" LSCP command.
264 void LSCPTest::test_ADD_CHANNEL() {
265 sleep(1); // wait 1s
266 CPPUNIT_ASSERT(connectToLSCPServer());
267
268 sendCommandToLSCPServer("ADD CHANNEL");
269 CPPUNIT_ASSERT(receiveSingleLineAnswerFromLSCPServer() == "OK[0]");
270
271 sendCommandToLSCPServer("ADD CHANNEL");
272 CPPUNIT_ASSERT(receiveSingleLineAnswerFromLSCPServer() == "OK[1]");
273
274 sendCommandToLSCPServer("ADD CHANNEL");
275 CPPUNIT_ASSERT(receiveSingleLineAnswerFromLSCPServer() == "OK[2]");
276 }
277
278 // Check "GET CHANNELS" LSCP command.
279 void LSCPTest::test_GET_CHANNELS() {
280 sendCommandToLSCPServer("GET CHANNELS");
281 string answer = receiveSingleLineAnswerFromLSCPServer();
282 int initial_channels = atoi(answer.c_str());
283
284 // add sampler channels and check if the count increases
285 for (uint trial = 1; trial <= 3; trial++) {
286 sendCommandToLSCPServer("ADD CHANNEL");
287 answer = receiveSingleLineAnswerFromLSCPServer();
288 sendCommandToLSCPServer("GET CHANNELS");
289 answer = receiveSingleLineAnswerFromLSCPServer();
290 int channels = atoi(answer.c_str());
291 CPPUNIT_ASSERT(channels == initial_channels + trial);
292 }
293 }
294
295 // Check "REMOVE CHANNEL" LSCP command.
296 void LSCPTest::test_REMOVE_CHANNEL() {
297 // how many channels do we have at the moment?
298 sendCommandToLSCPServer("GET CHANNELS");
299 string answer = receiveSingleLineAnswerFromLSCPServer();
300 int initial_channels = atoi(answer.c_str());
301
302 // if there are no sampler channels yet, create some
303 if (!initial_channels) {
304 const uint create_channels = 4;
305 for (uint i = 0; i < create_channels; i++) {
306 sendCommandToLSCPServer("ADD CHANNEL");
307 answer = receiveSingleLineAnswerFromLSCPServer();
308 }
309 initial_channels = create_channels;
310 }
311
312 // now remove the channels until there is no one left and check if we really need 'initial_channels' times to achieve that
313 for (uint channels = initial_channels; channels; channels--) {
314 sendCommandToLSCPServer("LIST CHANNELS");
315 answer = receiveSingleLineAnswerFromLSCPServer();
316 if (answer == "") CPPUNIT_ASSERT(false); // no sampler channel left already? -> failure
317
318 // take the last channel number in the list which we will take to remove that sampler channel
319 string::size_type pos = answer.rfind(",");
320 string channel_to_remove = (pos != string::npos) ? answer.substr(pos + 1, answer.length() - (pos + 1)) /* "m,n,...,t */
321 : answer; /* "k" */
322
323 //cout << " channel_to_remove: \"" << channel_to_remove << "\"\n" << flush;
324
325 // remove that channel
326 sendCommandToLSCPServer("REMOVE CHANNEL " + channel_to_remove);
327 answer = receiveSingleLineAnswerFromLSCPServer();
328 CPPUNIT_ASSERT(answer == "OK");
329 }
330 CPPUNIT_ASSERT(true); // success
331 }
332
333 // Check "GET AUDIO_OUTPUT_CHANNEL_PARAMETER INFO" LSCP command.
334 void LSCPTest::test_GET_AUDIO_OUTPUT_CHANNEL_PARAMETER_INFO() {
335 // first check if there's already an audio output device created
336 sendCommandToLSCPServer("GET AUDIO_OUTPUT_DEVICES");
337 string answer = receiveSingleLineAnswerFromLSCPServer();
338 int devices = atoi(answer.c_str());
339 CPPUNIT_ASSERT(devices >= 0);
340 if (!devices) { // if there's no audio output device yet, try to create one
341 sendCommandToLSCPServer("GET AVAILABLE_AUDIO_OUTPUT_DRIVERS");
342 string drivers = receiveSingleLineAnswerFromLSCPServer();
343 CPPUNIT_ASSERT(drivers.size());
344
345 // iterate through all available drivers until device creation was successful
346 do {
347 optional<string> driver = __ExtractFirstToken(&drivers, ",");
348 CPPUNIT_ASSERT(driver);
349
350 sendCommandToLSCPServer("CREATE AUDIO_OUTPUT_DEVICE " + *driver);
351 answer = receiveSingleLineAnswerFromLSCPServer();
352 } while (answer != "OK[0]");
353 }
354
355 // now we can check the "GET AUDIO_OUTPUT_CHANNEL_PARAMETER INFO" command
356 const uint timeout_seconds = 2;
357 sendCommandToLSCPServer("GET AUDIO_OUTPUT_CHANNEL_PARAMETER INFO 0 0 NAME");
358 vector<string> vAnswer = receiveMultiLineAnswerFromLSCPServer(timeout_seconds);
359 CPPUNIT_ASSERT(vAnswer.size() >= 4); // should at least contain tags TYPE, DESCRIPTION, FIX and MULTIPLICITY
360
361 sendCommandToLSCPServer("GET AUDIO_OUTPUT_CHANNEL_PARAMETER INFO 0 0 IS_MIX_CHANNEL");
362 vAnswer = receiveMultiLineAnswerFromLSCPServer(timeout_seconds);
363 CPPUNIT_ASSERT(vAnswer.size() >= 4); // should at least contain tags TYPE, DESCRIPTION, FIX and MULTIPLICITY
364 }
365
366 // Check if we can shutdown the LSCP Server without problems.
367 void LSCPTest::testShutdownLSCPServer() {
368 //cout << "testShutdownLSCPServer()\n" << flush;
369 sleep(2); // wait 2s
370 CPPUNIT_ASSERT(closeConnectionToLSCPServer());
371 sleep(3); // wait 3s
372 CPPUNIT_ASSERT(shutdownLSCPServer());
373 }

  ViewVC Help
Powered by ViewVC