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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 560 - (hide annotations) (download)
Sat May 21 11:47:55 2005 UTC (18 years, 11 months ago) by schoenebeck
File size: 15916 byte(s)
sync of LSCPTest.cpp with latest LSCP specification

1 schoenebeck 211 #include "LSCPTest.h"
2    
3 schoenebeck 217 #include "../common/global.h"
4     #include "../common/optional.h"
5    
6 schoenebeck 211 #include <iostream>
7     #include <stdio.h>
8     #include <stdlib.h>
9 schoenebeck 217 #include <unistd.h>
10 schoenebeck 218 #include <fcntl.h>
11     #include <sys/types.h>
12     #include <sys/socket.h>
13 schoenebeck 211
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 schoenebeck 217 /// 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 schoenebeck 211 // 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 schoenebeck 218 // set non-blocking mode
136     int socket_flags = fcntl(hSocket, F_GETFL, 0);
137     fcntl(hSocket, F_SETFL, socket_flags | O_NONBLOCK);
138 schoenebeck 211
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 schoenebeck 224 // 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 schoenebeck 211 // remove the line feed at the end
168 schoenebeck 224 static const string linedelimiter = "\n";
169 schoenebeck 211 string::size_type pos = msg.rfind(linedelimiter);
170     return msg.substr(0, pos);
171     }
172    
173 schoenebeck 217 /// 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 schoenebeck 211 return __ConvertMultiLineMessage(msg);
177     }
178    
179 schoenebeck 224 void LSCPTest::clearInputBuffer() {
180     char c;
181     while (recv(hSocket, &c, 1, 0) > 0);
182     }
183    
184 schoenebeck 217 /// 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 schoenebeck 211 string message;
187     char c;
188 schoenebeck 217 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 schoenebeck 218 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 schoenebeck 217 }
206    
207     // there's something to read, so read one character
208 schoenebeck 218 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 schoenebeck 217 }
235 schoenebeck 218
236 schoenebeck 211 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 schoenebeck 224 clearInputBuffer(); // to avoid that the next test reads an answer from a previous test
253 schoenebeck 211 }
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 schoenebeck 217 // 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 schoenebeck 560 sendCommandToLSCPServer("LIST AVAILABLE_AUDIO_OUTPUT_DRIVERS");
351 schoenebeck 217 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 schoenebeck 224 answer = receiveSingleLineAnswerFromLSCPServer(120); // wait 2 minutes for an answer
361 schoenebeck 217 } 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 schoenebeck 224 // 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 schoenebeck 211 // 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