/[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 676 - (show annotations) (download)
Wed Jun 22 22:13:08 2005 UTC (18 years, 10 months ago) by schoenebeck
File size: 15952 byte(s)
updated to latest LSCPServer constructor change
(IP address and TCP port as argument)

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, htonl(LSCP_ADDR), htons(LSCP_PORT));
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 (throws LinuxSamplerException if optional timeout exceeded)
162 string LSCPTest::receiveSingleLineAnswerFromLSCPServer(uint timeout_seconds) throw (LinuxSamplerException) {
163 string msg = receiveAnswerFromLSCPServer("\n", timeout_seconds);
164 // remove carriage return characters
165 string::size_type p = msg.find('\r');
166 for (; p != string::npos; p = msg.find(p, '\r')) msg.erase(p, 1);
167 // remove the line feed at the end
168 static const string linedelimiter = "\n";
169 string::size_type pos = msg.rfind(linedelimiter);
170 return msg.substr(0, pos);
171 }
172
173 /// wait until LSCP server answers with a multi line answer (throws LinuxSamplerException if optional timeout exceeded)
174 vector<string> LSCPTest::receiveMultiLineAnswerFromLSCPServer(uint timeout_seconds) throw (LinuxSamplerException) {
175 string msg = receiveAnswerFromLSCPServer("\n.\r\n", timeout_seconds);
176 return __ConvertMultiLineMessage(msg);
177 }
178
179 void LSCPTest::clearInputBuffer() {
180 char c;
181 while (recv(hSocket, &c, 1, 0) > 0);
182 }
183
184 /// wait until LSCP server answers with the given \a delimiter token at the end (throws LinuxSamplerException if optional timeout exceeded or socket error occured)
185 string LSCPTest::receiveAnswerFromLSCPServer(string delimiter, uint timeout_seconds) throw (LinuxSamplerException) {
186 string message;
187 char c;
188 fd_set sockSet;
189 timeval timeout;
190
191 while (true) {
192 if (timeout_seconds) {
193 FD_ZERO(&sockSet);
194 FD_SET(hSocket, &sockSet);
195 timeout.tv_sec = timeout_seconds;
196 timeout.tv_usec = 0;
197 int res = select(hSocket + 1, &sockSet, NULL, NULL, &timeout);
198 if (!res) { // if timeout exceeded
199 if (!message.size()) throw LinuxSamplerException("LSCPTest::receiveAnswerFromLSCPServer(): timeout (" + ToString(timeout_seconds) + "s) exceeded, no answer received");
200 else throw LinuxSamplerException("LSCPTest::receiveAnswerFromLSCPServer(): timeout (" + ToString(timeout_seconds) + "s) exceeded waiting for expected answer (end), received answer: \'" + message + "\'");
201 }
202 else if (res < 0) {
203 throw LinuxSamplerException("LSCPTest::receiveAnswerFromLSCPServer(): select error");
204 }
205 }
206
207 // there's something to read, so read one character
208 int res = recv(hSocket, &c, 1, 0);
209 if (!res) throw LinuxSamplerException("LSCPTest::receiveAnswerFromLSCPServer(): connection to LSCP server closed");
210 else if (res < 0) {
211 switch(errno) {
212 case EBADF:
213 throw LinuxSamplerException("The argument s is an invalid descriptor");
214 case ECONNREFUSED:
215 throw LinuxSamplerException("A remote host refused to allow the network connection (typically because it is not running the requested service).");
216 case ENOTCONN:
217 throw LinuxSamplerException("The socket is associated with a connection-oriented protocol and has not been connected (see connect(2) and accept(2)).");
218 case ENOTSOCK:
219 throw LinuxSamplerException("The argument s does not refer to a socket.");
220 case EAGAIN:
221 continue;
222 //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.");
223 case EINTR:
224 throw LinuxSamplerException("The receive was interrupted by delivery of a signal before any data were available.");
225 case EFAULT:
226 throw LinuxSamplerException("The receive buffer pointer(s) point outside the process's address space.");
227 case EINVAL:
228 throw LinuxSamplerException("Invalid argument passed.");
229 case ENOMEM:
230 throw LinuxSamplerException("Could not allocate memory for recvmsg.");
231 default:
232 throw LinuxSamplerException("Unknown recv() error.");
233 }
234 }
235
236 message += c;
237 string::size_type pos = message.rfind(delimiter); // ouch, but this is only a test case, right? ;)
238 if (pos != string::npos) return message;
239 }
240 }
241
242
243
244 void LSCPTest::printTestSuiteName() {
245 cout << "\b \nRunning LSCP Tests: " << flush;
246 }
247
248 void LSCPTest::setUp() {
249 }
250
251 void LSCPTest::tearDown() {
252 clearInputBuffer(); // to avoid that the next test reads an answer from a previous test
253 }
254
255
256
257 // Check if we can launch the LSCP Server (implies that there's no other instance running at the moment).
258 void LSCPTest::testLaunchLSCPServer() {
259 //cout << "testLaunchLSCPServer()\n" << flush;
260 CPPUNIT_ASSERT(launchLSCPServer());
261 }
262
263 // Check if we can connect a client connection to the LSCP server and close that connection without problems.
264 void LSCPTest::testConnectToLSCPServer() {
265 //cout << "testConnectToLSCPServer()\n" << flush;
266 sleep(1); // wait 1s
267 CPPUNIT_ASSERT(connectToLSCPServer());
268 sleep(2); // wait 2s
269 CPPUNIT_ASSERT(closeConnectionToLSCPServer());
270 }
271
272 // Check "ADD CHANNEL" LSCP command.
273 void LSCPTest::test_ADD_CHANNEL() {
274 sleep(1); // wait 1s
275 CPPUNIT_ASSERT(connectToLSCPServer());
276
277 sendCommandToLSCPServer("ADD CHANNEL");
278 CPPUNIT_ASSERT(receiveSingleLineAnswerFromLSCPServer() == "OK[0]");
279
280 sendCommandToLSCPServer("ADD CHANNEL");
281 CPPUNIT_ASSERT(receiveSingleLineAnswerFromLSCPServer() == "OK[1]");
282
283 sendCommandToLSCPServer("ADD CHANNEL");
284 CPPUNIT_ASSERT(receiveSingleLineAnswerFromLSCPServer() == "OK[2]");
285 }
286
287 // Check "GET CHANNELS" LSCP command.
288 void LSCPTest::test_GET_CHANNELS() {
289 sendCommandToLSCPServer("GET CHANNELS");
290 string answer = receiveSingleLineAnswerFromLSCPServer();
291 int initial_channels = atoi(answer.c_str());
292
293 // add sampler channels and check if the count increases
294 for (uint trial = 1; trial <= 3; trial++) {
295 sendCommandToLSCPServer("ADD CHANNEL");
296 answer = receiveSingleLineAnswerFromLSCPServer();
297 sendCommandToLSCPServer("GET CHANNELS");
298 answer = receiveSingleLineAnswerFromLSCPServer();
299 int channels = atoi(answer.c_str());
300 CPPUNIT_ASSERT(channels == initial_channels + trial);
301 }
302 }
303
304 // Check "REMOVE CHANNEL" LSCP command.
305 void LSCPTest::test_REMOVE_CHANNEL() {
306 // how many channels do we have at the moment?
307 sendCommandToLSCPServer("GET CHANNELS");
308 string answer = receiveSingleLineAnswerFromLSCPServer();
309 int initial_channels = atoi(answer.c_str());
310
311 // if there are no sampler channels yet, create some
312 if (!initial_channels) {
313 const uint create_channels = 4;
314 for (uint i = 0; i < create_channels; i++) {
315 sendCommandToLSCPServer("ADD CHANNEL");
316 answer = receiveSingleLineAnswerFromLSCPServer();
317 }
318 initial_channels = create_channels;
319 }
320
321 // now remove the channels until there is no one left and check if we really need 'initial_channels' times to achieve that
322 for (uint channels = initial_channels; channels; channels--) {
323 sendCommandToLSCPServer("LIST CHANNELS");
324 answer = receiveSingleLineAnswerFromLSCPServer();
325 if (answer == "") CPPUNIT_ASSERT(false); // no sampler channel left already? -> failure
326
327 // take the last channel number in the list which we will take to remove that sampler channel
328 string::size_type pos = answer.rfind(",");
329 string channel_to_remove = (pos != string::npos) ? answer.substr(pos + 1, answer.length() - (pos + 1)) /* "m,n,...,t */
330 : answer; /* "k" */
331
332 //cout << " channel_to_remove: \"" << channel_to_remove << "\"\n" << flush;
333
334 // remove that channel
335 sendCommandToLSCPServer("REMOVE CHANNEL " + channel_to_remove);
336 answer = receiveSingleLineAnswerFromLSCPServer();
337 CPPUNIT_ASSERT(answer == "OK");
338 }
339 CPPUNIT_ASSERT(true); // success
340 }
341
342 // Check "GET AUDIO_OUTPUT_CHANNEL_PARAMETER INFO" LSCP command.
343 void LSCPTest::test_GET_AUDIO_OUTPUT_CHANNEL_PARAMETER_INFO() {
344 // first check if there's already an audio output device created
345 sendCommandToLSCPServer("GET AUDIO_OUTPUT_DEVICES");
346 string answer = receiveSingleLineAnswerFromLSCPServer();
347 int devices = atoi(answer.c_str());
348 CPPUNIT_ASSERT(devices >= 0);
349 if (!devices) { // if there's no audio output device yet, try to create one
350 sendCommandToLSCPServer("LIST AVAILABLE_AUDIO_OUTPUT_DRIVERS");
351 string drivers = receiveSingleLineAnswerFromLSCPServer();
352 CPPUNIT_ASSERT(drivers.size());
353
354 // iterate through all available drivers until device creation was successful
355 do {
356 optional<string> driver = __ExtractFirstToken(&drivers, ",");
357 CPPUNIT_ASSERT(driver);
358
359 sendCommandToLSCPServer("CREATE AUDIO_OUTPUT_DEVICE " + *driver);
360 answer = receiveSingleLineAnswerFromLSCPServer(120); // wait 2 minutes for an answer
361 } while (answer != "OK[0]");
362 }
363
364 // now we can check the "GET AUDIO_OUTPUT_CHANNEL_PARAMETER INFO" command
365 const uint timeout_seconds = 2;
366 sendCommandToLSCPServer("GET AUDIO_OUTPUT_CHANNEL_PARAMETER INFO 0 0 NAME");
367 vector<string> vAnswer = receiveMultiLineAnswerFromLSCPServer(timeout_seconds);
368 CPPUNIT_ASSERT(vAnswer.size() >= 4); // should at least contain tags TYPE, DESCRIPTION, FIX and MULTIPLICITY
369
370 sendCommandToLSCPServer("GET AUDIO_OUTPUT_CHANNEL_PARAMETER INFO 0 0 IS_MIX_CHANNEL");
371 vAnswer = receiveMultiLineAnswerFromLSCPServer(timeout_seconds);
372 CPPUNIT_ASSERT(vAnswer.size() >= 4); // should at least contain tags TYPE, DESCRIPTION, FIX and MULTIPLICITY
373 }
374
375 // Check "SET ECHO" LSCP command.
376 void LSCPTest::test_SET_ECHO() {
377 // enable echo mode
378 sendCommandToLSCPServer("SET ECHO 1");
379 CPPUNIT_ASSERT(receiveSingleLineAnswerFromLSCPServer() == "OK");
380
381 // check if commands will actually be echoed now
382 sendCommandToLSCPServer("GET CHANNELS"); // send an arbitrary command
383 CPPUNIT_ASSERT(receiveSingleLineAnswerFromLSCPServer(2) == "GET CHANNELS");
384 receiveSingleLineAnswerFromLSCPServer(2); // throws exception if no answer received after 2s (usually we expect the answer from our command here)
385
386 // disable echo mode
387 sendCommandToLSCPServer("SET ECHO 0");
388 CPPUNIT_ASSERT(receiveSingleLineAnswerFromLSCPServer() == "SET ECHO 0"); // this will be echoed though
389 CPPUNIT_ASSERT(receiveSingleLineAnswerFromLSCPServer() == "OK");
390
391 // check if commands will not be echoed now
392 sendCommandToLSCPServer("GET CHANNELS");
393 CPPUNIT_ASSERT(receiveSingleLineAnswerFromLSCPServer() != "GET CHANNELS");
394 }
395
396 // Check if we can shutdown the LSCP Server without problems.
397 void LSCPTest::testShutdownLSCPServer() {
398 //cout << "testShutdownLSCPServer()\n" << flush;
399 sleep(2); // wait 2s
400 CPPUNIT_ASSERT(closeConnectionToLSCPServer());
401 sleep(3); // wait 3s
402 CPPUNIT_ASSERT(shutdownLSCPServer());
403 }

  ViewVC Help
Powered by ViewVC