fix test coverage calculations
[plcapi.git] / tests / 3LocalhostTest.php
1 <?php
2
3 include_once __DIR__ . '/../lib/xmlrpc.inc';
4 include_once __DIR__ . '/../lib/xmlrpc_wrappers.inc';
5
6 include_once __DIR__ . '/parse_args.php';
7
8 include_once __DIR__ . '/PolyfillTestCase.php';
9
10 use PHPUnit\Framework\TestResult;
11
12 /**
13  * Tests which involve interaction between the client and the server.
14  * They are run against the server found in demo/server.php
15  */
16 class LocalhostTest extends PhpXmlRpc_PolyfillTestCase
17 {
18     /** @var xmlrpc_client $client */
19     protected $client = null;
20     protected $method = 'http';
21     protected $timeout = 10;
22     protected $request_compression = null;
23     protected $accepted_compression = '';
24     protected $args = array();
25
26     protected static $failed_tests = array();
27
28     protected $testId;
29     /** @var boolean $collectCodeCoverageInformation */
30     protected $collectCodeCoverageInformation;
31     protected $coverageScriptUrl;
32
33     /**
34      * @todo instead of overriding run via _fail, implement PHPUnit_Framework_TestListener - IFF there is such an API portable across PHPUnit 5 to 9...
35      */
36     public static function _fail($message = '')
37     {
38         // save in a static var that this particular test has failed
39         // (but only if not called from subclass objects / multitests)
40         if (function_exists('debug_backtrace') && strtolower(get_called_class()) == 'localhosttests') {
41             $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
42             for ($i = 0; $i < count($trace); $i++) {
43                 if (strpos($trace[$i]['function'], 'test') === 0) {
44                     self::$failed_tests[$trace[$i]['function']] = true;
45                     break;
46                 }
47             }
48         }
49
50         parent::_fail($message);
51     }
52
53     /**
54      * Reimplemented to allow us to collect code coverage info from the target server.
55      * Code taken from PHPUnit_Extensions_Selenium2TestCase
56      *
57      * @param TestResult $result
58      * @return TestResult
59      * @throws Exception
60      *
61      * @todo instead of overriding run via _run, subclass PHPUnit_Extensions_TestDecorator - IFF there is such an API portable across PHPUnit 5 to 9...
62      */
63     public function _run($result = NULL)
64     {
65         $this->testId = get_class($this) . '__' . $this->getName();
66
67         if ($result === NULL) {
68             $result = $this->createResult();
69         }
70
71         $this->collectCodeCoverageInformation = $result->getCollectCodeCoverageInformation();
72
73         parent::_run($result);
74
75         if ($this->collectCodeCoverageInformation) {
76             $coverage = new PHPUnit_Extensions_SeleniumCommon_RemoteCoverage(
77                 $this->coverageScriptUrl,
78                 $this->testId
79             );
80             $result->getCodeCoverage()->append(
81                 $coverage->get(), $this
82             );
83         }
84
85         // do not call this before to give the time to the Listeners to run
86         //$this->getStrategy()->endOfTest($this->session);
87
88         return $result;
89     }
90
91     public function set_up()
92     {
93         $this->args = argParser::getArgs();
94
95         $server = explode(':', $this->args['HTTPSERVER']);
96         if (count($server) > 1) {
97             $this->client = new xmlrpc_client($this->args['HTTPURI'], $server[0], $server[1]);
98         } else {
99             $this->client = new xmlrpc_client($this->args['HTTPURI'], $this->args['HTTPSERVER']);
100         }
101
102         $this->client->setDebug($this->args['DEBUG']);
103         $this->client->request_compression = $this->request_compression;
104         $this->client->accepted_compression = $this->accepted_compression;
105
106         $this->coverageScriptUrl = 'http://' . $this->args['HTTPSERVER'] . '/' . str_replace( '/demo/server/server.php', 'tests/phpunit_coverage.php', $this->args['HTTPURI'] );
107
108         if ($this->args['DEBUG'] == 1)
109             ob_start();
110     }
111
112     protected function tear_down()
113     {
114         if ($this->args['DEBUG'] != 1)
115             return;
116         $out = ob_get_clean();
117         $status = $this->getStatus();
118         if ($status == PHPUnit_Runner_BaseTestRunner::STATUS_ERROR
119             || $status == PHPUnit_Runner_BaseTestRunner::STATUS_FAILURE) {
120             echo $out;
121         }
122     }
123
124     /**
125      * @param PhpXmlRpc\Request|array $msg
126      * @param int|array $errorCode
127      * @param bool $returnResponse
128      * @return mixed|\PhpXmlRpc\Response|\PhpXmlRpc\Response[]|\PhpXmlRpc\Value|string|null
129      */
130     protected function send($msg, $errorCode = 0, $returnResponse = false)
131     {
132         if ($this->collectCodeCoverageInformation) {
133             $this->client->setCookie('PHPUNIT_SELENIUM_TEST_ID', $this->testId);
134         }
135
136         $r = $this->client->send($msg, $this->timeout, $this->method);
137         // for multicall, return directly array of responses
138         if (is_array($r)) {
139             return $r;
140         }
141         if (is_array($errorCode)) {
142             $this->assertContains($r->faultCode(), $errorCode, 'Error ' . $r->faultCode() . ' connecting to server: ' . $r->faultString());
143         } else {
144             $this->assertEquals($errorCode, $r->faultCode(), 'Error ' . $r->faultCode() . ' connecting to server: ' . $r->faultString());
145         }
146         if (!$r->faultCode()) {
147             if ($returnResponse) {
148                 return $r;
149             } else {
150                 return $r->value();
151             }
152         } else {
153             return null;
154         }
155     }
156
157     /**
158      * Adds (and replaces) query params to the url currently used by the client
159      * @param array $data
160      */
161     protected function addQueryParams($data)
162     {
163         $query = parse_url($this->client->path, PHP_URL_QUERY);
164         parse_str($query, $vars);
165         $query = http_build_query(array_merge($vars, $data));
166         $this->client->path = parse_url($this->client->path, PHP_URL_PATH) . '?' . $query;
167     }
168
169     public function testString()
170     {
171         $sendString = "here are 3 \"entities\": < > & " .
172             "and here's a dollar sign: \$pretendvarname and a backslash too: " . chr(92) .
173             " - isn't that great? \\\"hackery\\\" at it's best " .
174             " also don't want to miss out on \$item[0]. " .
175             "The real weird stuff follows: CRLF here" . chr(13) . chr(10) .
176             "a simple CR here" . chr(13) .
177             "a simple LF here" . chr(10) .
178             "and then LFCR" . chr(10) . chr(13) .
179             "last but not least weird names: G" . chr(252) . "nter, El" . chr(232) . "ne, and an xml comment closing tag: -->";
180         $m = new xmlrpcmsg('examples.stringecho', array(
181             new xmlrpcval($sendString, 'string'),
182         ));
183         $v = $this->send($m);
184         if ($v) {
185             // when sending/receiving non-US-ASCII encoded strings, XML says cr-lf can be normalized.
186             // so we relax our tests...
187             $l1 = strlen($sendString);
188             $l2 = strlen($v->scalarval());
189             if ($l1 == $l2) {
190                 $this->assertEquals($sendString, $v->scalarval());
191             } else {
192                 $this->assertEquals(str_replace(array("\r\n", "\r"), array("\n", "\n"), $sendString), $v->scalarval());
193             }
194         }
195     }
196
197     public function testLatin1String()
198     {
199         $sendString =
200             "last but not least weird names: G" . chr(252) . "nter, El" . chr(232) . "ne";
201         $x = '<?xml version="1.0" encoding="ISO-8859-1"?><methodCall><methodName>examples.stringecho</methodName><params><param><value>'.
202             $sendString.
203             '</value></param></params></methodCall>';
204         $v = $this->send($x);
205         if ($v) {
206             $this->assertEquals($sendString, $v->scalarval());
207         }
208     }
209
210     public function testExoticCharsetsRequests()
211     {
212         // note that we should disable this call also when mbstring is missing server-side
213         if (!function_exists('mb_convert_encoding')) {
214             $this->markTestSkipped('Miss mbstring extension to test exotic charsets');
215             return;
216         }
217         $sendString = 'κόσμε'; // Greek word 'kosme'. NB: NOT a valid ISO8859 string!
218         $str = '<?xml version="1.0" encoding="_ENC_"?>
219 <methodCall>
220     <methodName>examples.stringecho</methodName>
221     <params>
222         <param>
223         <value><string>'.$sendString.'</string></value>
224         </param>
225     </params>
226 </methodCall>';
227
228         PhpXmlRpc\PhpXmlRpc::$xmlrpc_internalencoding = 'UTF-8';
229         // we have to set the encoding declaration either in the http header or xml prolog, as mb_detect_encoding
230         // (used on the server side) will fail recognizing these 2 charsets
231         $v = $this->send(mb_convert_encoding(str_replace('_ENC_', 'UCS-4', $str), 'UCS-4', 'UTF-8'));
232         $this->assertEquals($sendString, $v->scalarval());
233         $v = $this->send(mb_convert_encoding(str_replace('_ENC_', 'UTF-16', $str), 'UTF-16', 'UTF-8'));
234         $this->assertEquals($sendString, $v->scalarval());
235         PhpXmlRpc\PhpXmlRpc::$xmlrpc_internalencoding = 'ISO-8859-1';
236     }
237
238     public function testExoticCharsetsRequests2()
239     {
240         // note that we should disable this call also when mbstring is missing server-side
241         if (!function_exists('mb_convert_encoding')) {
242             $this->markTestSkipped('Miss mbstring extension to test exotic charsets');
243             return;
244         }
245         $sendString = '安室奈美恵'; // No idea what this means :-) NB: NOT a valid ISO8859 string!
246         $str = '<?xml version="1.0"?>
247 <methodCall>
248     <methodName>examples.stringecho</methodName>
249     <params>
250         <param>
251         <value><string>'.$sendString.'</string></value>
252         </param>
253     </params>
254 </methodCall>';
255
256         PhpXmlRpc\PhpXmlRpc::$xmlrpc_internalencoding = 'UTF-8';
257         // no encoding declaration either in the http header or xml prolog, let mb_detect_encoding
258         // (used on the server side) sort it out
259         $this->addQueryParams(array('DETECT_ENCODINGS' => array('EUC-JP', 'UTF-8')));
260         $v = $this->send(mb_convert_encoding($str, 'EUC-JP', 'UTF-8'));
261         $this->assertEquals($sendString, $v->scalarval());
262         PhpXmlRpc\PhpXmlRpc::$xmlrpc_internalencoding = 'ISO-8859-1';
263     }
264
265     public function testExoticCharsetsRequests3()
266     {
267         // note that we should disable this call also when mbstring is missing server-side
268         if (!function_exists('mb_convert_encoding')) {
269             $this->markTestSkipped('Miss mbstring extension to test exotic charsets');
270             return;
271         }
272         $sendString = utf8_decode('élève');
273         $str = '<?xml version="1.0"?>
274 <methodCall>
275     <methodName>examples.stringecho</methodName>
276     <params>
277         <param>
278         <value><string>'.$sendString.'</string></value>
279         </param>
280     </params>
281 </methodCall>';
282
283         // no encoding declaration either in the http header or xml prolog, let mb_detect_encoding
284         // (used on the server side) sort it out
285         $this->addQueryParams(array('DETECT_ENCODINGS' => array('ISO-8859-1', 'UTF-8')));
286         $v = $this->send($str);
287         $this->assertEquals($sendString, $v->scalarval());
288     }
289
290     /*public function testLatin1Method()
291     {
292         $f = new xmlrpcmsg("tests.iso88591methodname." . chr(224) . chr(252) . chr(232), array(
293             new xmlrpcval('hello')
294         ));
295         $v = $this->send($f);
296         if ($v) {
297             $this->assertEquals('hello', $v->scalarval());
298         }
299     }*/
300
301     public function testUtf8Method()
302     {
303         PhpXmlRpc\PhpXmlRpc::$xmlrpc_internalencoding = 'UTF-8';
304         $m = new xmlrpcmsg("tests.utf8methodname." . 'κόσμε', array(
305             new xmlrpcval('hello')
306         ));
307         $v = $this->send($m);
308         if ($v) {
309             $this->assertEquals('hello', $v->scalarval());
310         }
311         PhpXmlRpc\PhpXmlRpc::$xmlrpc_internalencoding = 'ISO-8859-1';
312     }
313
314     public function testAddingDoubles()
315     {
316         // note that rounding errors mean we
317         // keep precision to sensible levels here ;-)
318         $a = 12.13;
319         $b = -23.98;
320         $m = new xmlrpcmsg('examples.addtwodouble', array(
321             new xmlrpcval($a, 'double'),
322             new xmlrpcval($b, 'double'),
323         ));
324         $v = $this->send($m);
325         if ($v) {
326             $this->assertEquals($a + $b, $v->scalarval());
327         }
328     }
329
330     public function testAdding()
331     {
332         $m = new xmlrpcmsg('examples.addtwo', array(
333             new xmlrpcval(12, 'int'),
334             new xmlrpcval(-23, 'int'),
335         ));
336         $v = $this->send($m);
337         if ($v) {
338             $this->assertEquals(12 - 23, $v->scalarval());
339         }
340     }
341
342     public function testInvalidNumber()
343     {
344         $m = new xmlrpcmsg('examples.addtwo', array(
345             new xmlrpcval('fred', 'int'),
346             new xmlrpcval("\"; exec('ls')", 'int'),
347         ));
348         $v = $this->send($m);
349         /// @todo a fault condition should be generated here
350         /// by the server, which we pick up on
351         if ($v) {
352             $this->assertEquals(0, $v->scalarval());
353         }
354     }
355
356     public function testBoolean()
357     {
358         $m = new xmlrpcmsg('examples.invertBooleans', array(
359             new xmlrpcval(array(
360                 new xmlrpcval(true, 'boolean'),
361                 new xmlrpcval(false, 'boolean'),
362                 new xmlrpcval(1, 'boolean'),
363                 new xmlrpcval(0, 'boolean')
364             ),
365                 'array'
366             ),));
367         $answer = '0101';
368         $v = $this->send($m);
369         if ($v) {
370             $sz = $v->arraysize();
371             $got = '';
372             for ($i = 0; $i < $sz; $i++) {
373                 $b = $v->arraymem($i);
374                 if ($b->scalarval()) {
375                     $got .= '1';
376                 } else {
377                     $got .= '0';
378                 }
379             }
380             $this->assertEquals($answer, $got);
381         }
382     }
383
384     public function testBase64()
385     {
386         $sendString = 'Mary had a little lamb,
387 Whose fleece was white as snow,
388 And everywhere that Mary went
389 the lamb was sure to go.
390
391 Mary had a little lamb
392 She tied it to a pylon
393 Ten thousand volts went down its back
394 And turned it into nylon';
395         $m = new xmlrpcmsg('examples.decode64', array(
396             new xmlrpcval($sendString, 'base64'),
397         ));
398         $v = $this->send($m);
399         if ($v) {
400             if (strlen($sendString) == strlen($v->scalarval())) {
401                 $this->assertEquals($sendString, $v->scalarval());
402             } else {
403                 $this->assertEquals(str_replace(array("\r\n", "\r"), array("\n", "\n"), $sendString), $v->scalarval());
404             }
405         }
406     }
407
408     public function testDateTime()
409     {
410         $time = time();
411         $t1 = new xmlrpcval($time, 'dateTime.iso8601');
412         $t2 = new xmlrpcval(iso8601_encode($time), 'dateTime.iso8601');
413         $this->assertEquals($t1->serialize(), $t2->serialize());
414         if (class_exists('DateTime')) {
415             $datetime = new DateTime();
416             // skip this test for php 5.2. It is a bit harder there to build a DateTime from unix timestamp with proper TZ info
417             if (is_callable(array($datetime, 'setTimestamp'))) {
418                 $t3 = new xmlrpcval($datetime->setTimestamp($time), 'dateTime.iso8601');
419                 $this->assertEquals($t1->serialize(), $t3->serialize());
420             }
421         }
422     }
423
424     public function testCountEntities()
425     {
426         $sendString = "h'fd>onc>>l>>rw&bpu>q>e<v&gxs<ytjzkami<";
427         $m = new xmlrpcmsg('validator1.countTheEntities', array(
428             new xmlrpcval($sendString, 'string'),
429         ));
430         $v = $this->send($m);
431         if ($v) {
432             $got = '';
433             $expected = '37210';
434             $expect_array = array('ctLeftAngleBrackets', 'ctRightAngleBrackets', 'ctAmpersands', 'ctApostrophes', 'ctQuotes');
435             foreach($expect_array as $val) {
436                 $b = $v->structmem($val);
437                 $got .= $b->me['int'];
438             }
439             $this->assertEquals($expected, $got);
440         }
441     }
442
443     public function _multicall_msg($method, $params)
444     {
445         $struct['methodName'] = new xmlrpcval($method, 'string');
446         $struct['params'] = new xmlrpcval($params, 'array');
447
448         return new xmlrpcval($struct, 'struct');
449     }
450
451     public function testServerMulticall()
452     {
453         // We manually construct a system.multicall() call to ensure
454         // that the server supports it.
455
456         // NB: This test will NOT pass if server does not support system.multicall.
457
458         // Based on http://xmlrpc-c.sourceforge.net/hacks/test_multicall.py
459         $good1 = $this->_multicall_msg(
460             'system.methodHelp',
461             array(php_xmlrpc_encode('system.listMethods')));
462         $bad = $this->_multicall_msg(
463             'test.nosuch',
464             array(php_xmlrpc_encode(1), php_xmlrpc_encode(2)));
465         $recursive = $this->_multicall_msg(
466             'system.multicall',
467             array(new xmlrpcval(array(), 'array')));
468         $good2 = $this->_multicall_msg(
469             'system.methodSignature',
470             array(php_xmlrpc_encode('system.listMethods')));
471         $arg = new xmlrpcval(
472             array($good1, $bad, $recursive, $good2),
473             'array'
474         );
475
476         $m = new xmlrpcmsg('system.multicall', array($arg));
477         $v = $this->send($m);
478         if ($v) {
479             //$this->assertTrue($r->faultCode() == 0, "fault from system.multicall");
480             $this->assertTrue($v->arraysize() == 4, "bad number of return values");
481
482             $r1 = $v->arraymem(0);
483             $this->assertTrue(
484                 $r1->kindOf() == 'array' && $r1->arraysize() == 1,
485                 "did not get array of size 1 from good1"
486             );
487
488             $r2 = $v->arraymem(1);
489             $this->assertTrue(
490                 $r2->kindOf() == 'struct',
491                 "no fault from bad"
492             );
493
494             $r3 = $v->arraymem(2);
495             $this->assertTrue(
496                 $r3->kindOf() == 'struct',
497                 "recursive system.multicall did not fail"
498             );
499
500             $r4 = $v->arraymem(3);
501             $this->assertTrue(
502                 $r4->kindOf() == 'array' && $r4->arraysize() == 1,
503                 "did not get array of size 1 from good2"
504             );
505         }
506     }
507
508     public function testClientMulticall1()
509     {
510         // NB: This test will NOT pass if server does not support system.multicall.
511
512         $noMultiCall = $this->client->no_multicall;
513         $this->client->no_multicall = false;
514
515         $good1 = new xmlrpcmsg('system.methodHelp',
516             array(php_xmlrpc_encode('system.listMethods')));
517         $bad = new xmlrpcmsg('test.nosuch',
518             array(php_xmlrpc_encode(1), php_xmlrpc_encode(2)));
519         $recursive = new xmlrpcmsg('system.multicall',
520             array(new xmlrpcval(array(), 'array')));
521         $good2 = new xmlrpcmsg('system.methodSignature',
522             array(php_xmlrpc_encode('system.listMethods'))
523         );
524
525         $r = $this->send(array($good1, $bad, $recursive, $good2));
526         if ($r) {
527             $this->assertTrue(count($r) == 4, "wrong number of return values");
528         }
529
530         $this->assertTrue($r[0]->faultCode() == 0, "fault from good1");
531         if (!$r[0]->faultCode()) {
532             $val = $r[0]->value();
533             $this->assertTrue(
534                 $val->kindOf() == 'scalar' && $val->scalartyp() == 'string',
535                 "good1 did not return string"
536             );
537         }
538         $this->assertTrue($r[1]->faultCode() != 0, "no fault from bad");
539         $this->assertTrue($r[2]->faultCode() != 0, "no fault from recursive system.multicall");
540         $this->assertTrue($r[3]->faultCode() == 0, "fault from good2");
541         if (!$r[3]->faultCode()) {
542             $val = $r[3]->value();
543             $this->assertTrue($val->kindOf() == 'array', "good2 did not return array");
544         }
545         // This is the only assert in this test which should fail
546         // if the test server does not support system.multicall.
547         $this->assertTrue($this->client->no_multicall == false,
548             "server does not support system.multicall"
549         );
550
551         $this->client->no_multicall = $noMultiCall;
552     }
553
554     public function testClientMulticall2()
555     {
556         // NB: This test will NOT pass if server does not support system.multicall.
557
558         $noMultiCall = $this->client->no_multicall;
559         $this->client->no_multicall = true;
560
561         $good1 = new xmlrpcmsg('system.methodHelp',
562             array(php_xmlrpc_encode('system.listMethods')));
563         $bad = new xmlrpcmsg('test.nosuch',
564             array(php_xmlrpc_encode(1), php_xmlrpc_encode(2)));
565         $recursive = new xmlrpcmsg('system.multicall',
566             array(new xmlrpcval(array(), 'array')));
567         $good2 = new xmlrpcmsg('system.methodSignature',
568             array(php_xmlrpc_encode('system.listMethods'))
569         );
570
571         $r = $this->send(array($good1, $bad, $recursive, $good2));
572         if ($r) {
573             $this->assertTrue(count($r) == 4, "wrong number of return values");
574         }
575
576         $this->assertTrue($r[0]->faultCode() == 0, "fault from good1");
577         if (!$r[0]->faultCode()) {
578             $val = $r[0]->value();
579             $this->assertTrue(
580                 $val->kindOf() == 'scalar' && $val->scalartyp() == 'string',
581                 "good1 did not return string");
582         }
583         $this->assertTrue($r[1]->faultCode() != 0, "no fault from bad");
584         $this->assertTrue($r[2]->faultCode() == 0, "fault from (non recursive) system.multicall");
585         $this->assertTrue($r[3]->faultCode() == 0, "fault from good2");
586         if (!$r[3]->faultCode()) {
587             $val = $r[3]->value();
588             $this->assertTrue($val->kindOf() == 'array', "good2 did not return array");
589         }
590
591         $this->client->no_multicall = $noMultiCall;
592     }
593
594     public function testClientMulticall3()
595     {
596         // NB: This test will NOT pass if server does not support system.multicall.
597
598         $noMultiCall = $this->client->no_multicall;
599         $returnType = $this->client->return_type;
600
601         $this->client->return_type = 'phpvals';
602         $this->client->no_multicall = false;
603
604         $good1 = new xmlrpcmsg('system.methodHelp',
605             array(php_xmlrpc_encode('system.listMethods')));
606         $bad = new xmlrpcmsg('test.nosuch',
607             array(php_xmlrpc_encode(1), php_xmlrpc_encode(2)));
608         $recursive = new xmlrpcmsg('system.multicall',
609             array(new xmlrpcval(array(), 'array')));
610         $good2 = new xmlrpcmsg('system.methodSignature',
611             array(php_xmlrpc_encode('system.listMethods'))
612         );
613
614         $r = $this->send(array($good1, $bad, $recursive, $good2));
615         if ($r) {
616             $this->assertTrue(count($r) == 4, "wrong number of return values");
617         }
618         $this->assertTrue($r[0]->faultCode() == 0, "fault from good1");
619         if (!$r[0]->faultCode()) {
620             $val = $r[0]->value();
621             $this->assertTrue(
622                 is_string($val), "good1 did not return string");
623         }
624         $this->assertTrue($r[1]->faultCode() != 0, "no fault from bad");
625         $this->assertTrue($r[2]->faultCode() != 0, "no fault from recursive system.multicall");
626         $this->assertTrue($r[3]->faultCode() == 0, "fault from good2");
627         if (!$r[3]->faultCode()) {
628             $val = $r[3]->value();
629             $this->assertTrue(is_array($val), "good2 did not return array");
630         }
631
632         $this->client->return_type = $returnType;
633         $this->client->no_multicall = $noMultiCall;
634     }
635
636     public function testCatchWarnings()
637     {
638         $m = new xmlrpcmsg('tests.generatePHPWarning', array(
639             new xmlrpcval('whatever', 'string'),
640         ));
641         $v = $this->send($m);
642         if ($v) {
643             $this->assertEquals(true, $v->scalarval());
644         }
645     }
646
647     public function testCatchExceptions()
648     {
649         $m = new xmlrpcmsg('tests.raiseException', array(
650             new xmlrpcval('whatever', 'string'),
651         ));
652         $v = $this->send($m, $GLOBALS['xmlrpcerr']['server_error']);
653         $this->addQueryParams(array('EXCEPTION_HANDLING' => 1));
654         $v = $this->send($m, 1); // the error code of the expected exception
655         $this->addQueryParams(array('EXCEPTION_HANDLING' => 2));
656         // depending on whether display_errors is ON or OFF on the server, we will get back a different error here,
657         // as php will generate an http status code of either 200 or 500...
658         $v = $this->send($m, array($GLOBALS['xmlrpcerr']['invalid_return'], $GLOBALS['xmlrpcerr']['http_error']));
659     }
660
661     public function testZeroParams()
662     {
663         $m = new xmlrpcmsg('system.listMethods');
664         $v = $this->send($m);
665     }
666
667     public function testNullParams()
668     {
669         $m = new xmlrpcmsg('tests.getStateName.12', array(
670             new xmlrpcval('whatever', 'null'),
671             new xmlrpcval(23, 'int'),
672         ));
673         $v = $this->send($m);
674         if ($v) {
675             $this->assertEquals('Michigan', $v->scalarval());
676         }
677         $m = new xmlrpcmsg('tests.getStateName.12', array(
678             new xmlrpcval(23, 'int'),
679             new xmlrpcval('whatever', 'null'),
680         ));
681         $v = $this->send($m);
682         if ($v) {
683             $this->assertEquals('Michigan', $v->scalarval());
684         }
685         $m = new xmlrpcmsg('tests.getStateName.12', array(
686             new xmlrpcval(23, 'int')
687         ));
688         $v = $this->send($m, array($GLOBALS['xmlrpcerr']['incorrect_params']));
689     }
690
691     public function testCodeInjectionServerSide()
692     {
693         $m = new xmlrpcmsg('system.MethodHelp');
694         $m->payload = "<?xml version=\"1.0\"?><methodCall><methodName>validator1.echoStructTest</methodName><params><param><value><struct><member><name>','')); echo('gotcha!'); die(); //</name></member></struct></value></param></params></methodCall>";
695         $v = $this->send($m);
696         if ($v) {
697             $this->assertEquals(0, $v->structsize());
698         }
699     }
700
701     public function testServerWrappedFunction()
702     {
703         $m = new xmlrpcmsg('tests.getStateName.2', array(
704             new xmlrpcval(23, 'int'),
705         ));
706         $v = $this->send($m);
707         $this->assertEquals('Michigan', $v->scalarval());
708
709         // this generates an exception in the function which was wrapped, which is by default wrapped in a known error response
710         $m = new xmlrpcmsg('tests.getStateName.2', array(
711             new xmlrpcval(0, 'int'),
712         ));
713         $v = $this->send($m, $GLOBALS['xmlrpcerr']['server_error']);
714
715         // check if the generated function dispatch map is fine, by checking if the server registered it
716         $m = new xmlrpcmsg('system.methodSignature', array(
717             new xmlrpcval('tests.getStateName.2'),
718         ));
719         $v = $this->send($m);
720         $encoder = new \PhpXmlRpc\Encoder();
721         $this->assertEquals(array(array('string', 'int')), $encoder->decode($v));
722     }
723
724     public function testServerWrappedFunctionAsSource()
725     {
726         $m = new xmlrpcmsg('tests.getStateName.6', array(
727             new xmlrpcval(23, 'int'),
728         ));
729         $v = $this->send($m);
730         $this->assertEquals('Michigan', $v->scalarval());
731
732         // this generates an exception in the function which was wrapped, which is by default wrapped in a known error response
733         $m = new xmlrpcmsg('tests.getStateName.6', array(
734             new xmlrpcval(0, 'int'),
735         ));
736         $v = $this->send($m, $GLOBALS['xmlrpcerr']['server_error']);
737     }
738
739     public function testServerWrappedObjectMethods()
740     {
741         $m = new xmlrpcmsg('tests.getStateName.3', array(
742             new xmlrpcval(23, 'int'),
743         ));
744         $v = $this->send($m);
745         $this->assertEquals('Michigan', $v->scalarval());
746
747         $m = new xmlrpcmsg('tests.getStateName.4', array(
748             new xmlrpcval(23, 'int'),
749         ));
750         $v = $this->send($m);
751         $this->assertEquals('Michigan', $v->scalarval());
752
753         $m = new xmlrpcmsg('tests.getStateName.5', array(
754             new xmlrpcval(23, 'int'),
755         ));
756         $v = $this->send($m);
757         $this->assertEquals('Michigan', $v->scalarval());
758
759         $m = new xmlrpcmsg('tests.getStateName.7', array(
760             new xmlrpcval(23, 'int'),
761         ));
762         $v = $this->send($m);
763         $this->assertEquals('Michigan', $v->scalarval());
764
765         $m = new xmlrpcmsg('tests.getStateName.8', array(
766             new xmlrpcval(23, 'int'),
767         ));
768         $v = $this->send($m);
769         $this->assertEquals('Michigan', $v->scalarval());
770
771         $m = new xmlrpcmsg('tests.getStateName.9', array(
772             new xmlrpcval(23, 'int'),
773         ));
774         $v = $this->send($m);
775         $this->assertEquals('Michigan', $v->scalarval());
776     }
777
778     public function testServerWrappedObjectMethodsAsSource()
779     {
780         $m = new xmlrpcmsg('tests.getStateName.7', array(
781             new xmlrpcval(23, 'int'),
782         ));
783         $v = $this->send($m);
784         $this->assertEquals('Michigan', $v->scalarval());
785
786         $m = new xmlrpcmsg('tests.getStateName.8', array(
787             new xmlrpcval(23, 'int'),
788         ));
789         $v = $this->send($m);
790         $this->assertEquals('Michigan', $v->scalarval());
791
792         $m = new xmlrpcmsg('tests.getStateName.9', array(
793             new xmlrpcval(23, 'int'),
794         ));
795         $v = $this->send($m);
796         $this->assertEquals('Michigan', $v->scalarval());
797     }
798
799     public function testServerClosure()
800     {
801         $m = new xmlrpcmsg('tests.getStateName.10', array(
802             new xmlrpcval(23, 'int'),
803         ));
804         $v = $this->send($m);
805         $this->assertEquals('Michigan', $v->scalarval());
806     }
807
808     public function testServerWrappedClosure()
809     {
810         $m = new xmlrpcmsg('tests.getStateName.11', array(
811             new xmlrpcval(23, 'int'),
812         ));
813         $v = $this->send($m);
814         $this->assertEquals('Michigan', $v->scalarval());
815     }
816
817     public function testServerWrappedClass()
818     {
819         $m = new xmlrpcmsg('tests.xmlrpcServerMethodsContainer.findState', array(
820             new xmlrpcval(23, 'int'),
821         ));
822         $v = $this->send($m);
823         $this->assertEquals('Michigan', $v->scalarval());
824     }
825
826     public function testWrappedMethod()
827     {
828         // make a 'deep client copy' as the original one might have many properties set
829         $func = wrap_xmlrpc_method($this->client, 'examples.getStateName', array('simple_client_copy' => 0));
830         if ($func == false) {
831             $this->fail('Registration of examples.getStateName failed');
832         } else {
833             $v = $func(23);
834             // work around bug in current (or old?) version of phpunit when reporting the error
835             /*if (is_object($v)) {
836                 $v = var_export($v, true);
837             }*/
838             $this->assertEquals('Michigan', $v);
839         }
840     }
841
842     public function testWrappedMethodAsSource()
843     {
844         // make a 'deep client copy' as the original one might have many properties set
845         $func = wrap_xmlrpc_method($this->client, 'examples.getStateName', array('simple_client_copy' => 0, 'return_source' => true));
846         if ($func == false) {
847             $this->fail('Registration of examples.getStateName failed');
848         } else {
849             eval($func['source']);
850             $func = $func['function'];
851             $v = $func(23);
852             // work around bug in current (or old?) version of phpunit when reporting the error
853             /*if (is_object($v)) {
854                 $v = var_export($v, true);
855             }*/
856             $this->assertEquals('Michigan', $v);
857         }
858     }
859
860     public function testWrappedClass()
861     {
862         // make a 'deep client copy' as the original one might have many properties set
863         // also for speed only wrap one method of the whole server
864         $class = wrap_xmlrpc_server($this->client, array('simple_client_copy' => 0, 'method_filter' => '/examples\.getStateName/' ));
865         if ($class == '') {
866             $this->fail('Registration of remote server failed');
867         } else {
868             $obj = new $class();
869             if (!is_callable(array($obj, 'examples_getStateName'))) {
870                 $this->fail('Registration of remote server failed to import method "examples_getStateName"');
871             } else {
872                 $v = $obj->examples_getStateName(23);
873                 // work around bug in current (or old?) version of phpunit when reporting the error
874                 /*if (is_object($v)) {
875                     $v = var_export($v, true);
876                 }*/
877                 $this->assertEquals('Michigan', $v);
878             }
879         }
880     }
881
882     public function testTransferOfObjectViaWrapping()
883     {
884         // make a 'deep client copy' as the original one might have many properties set
885         $func = wrap_xmlrpc_method($this->client, 'tests.returnPhpObject', array('simple_client_copy' => 0,
886             'decode_php_objs' => true));
887         if ($func == false) {
888             $this->fail('Registration of tests.returnPhpObject failed');
889         } else {
890             $v = $func();
891             $obj = new stdClass();
892             $obj->hello = 'world';
893             $this->assertEquals($obj, $v);
894         }
895     }
896
897     public function testGetCookies()
898     {
899         // let server set to us some cookies we tell it
900         $cookies = array(
901             //'c1' => array(),
902             'c2' => array('value' => 'c2'),
903             'c3' => array('value' => 'c3', 'expires' => time() + 60 * 60 * 24 * 30),
904             'c4' => array('value' => 'c4', 'expires' => time() + 60 * 60 * 24 * 30, 'path' => '/'),
905             'c5' => array('value' => 'c5', 'expires' => time() + 60 * 60 * 24 * 30, 'path' => '/', 'domain' => 'localhost'),
906         );
907         $cookiesval = php_xmlrpc_encode($cookies);
908         $m = new xmlrpcmsg('examples.setcookies', array($cookiesval));
909         $r = $this->send($m, 0, true);
910         if ($r) {
911             $v = $r->value();
912             $this->assertEquals(1, $v->scalarval());
913             // now check if we decoded the cookies as we had set them
914             $rcookies = $r->cookies();
915             // remove extra cookies which might have been set by proxies
916             foreach ($rcookies as $c => $v) {
917                 if (!in_array($c, array('c2', 'c3', 'c4', 'c5'))) {
918                     unset($rcookies[$c]);
919                 }
920                 // Seems like we get this when using php-fpm and php 5.5+ ...
921                 if (isset($rcookies[$c]['Max-Age'])) {
922                     unset($rcookies[$c]['Max-Age']);
923                 }
924             }
925             foreach ($cookies as $c => $v) {
926                 // format for date string in cookies: 'Mon, 31 Oct 2005 13:50:56 GMT'
927                 // but PHP versions differ on that, some use 'Mon, 31-Oct-2005 13:50:56 GMT'...
928                 if (isset($v['expires'])) {
929                     if (isset($rcookies[$c]['expires']) && strpos($rcookies[$c]['expires'], '-')) {
930                         $cookies[$c]['expires'] = gmdate('D, d\-M\-Y H:i:s \G\M\T', $cookies[$c]['expires']);
931                     } else {
932                         $cookies[$c]['expires'] = gmdate('D, d M Y H:i:s \G\M\T', $cookies[$c]['expires']);
933                     }
934                 }
935             }
936
937             $this->assertEquals($cookies, $rcookies);
938         }
939     }
940
941     public function testSetCookies()
942     {
943         // let server set to us some cookies we tell it
944         $cookies = array(
945             'c0' => null,
946             'c1' => 1,
947             'c2' => '2 3',
948             'c3' => '!@#$%^&*()_+|}{":?><,./\';[]\\=-',
949         );
950         $m = new xmlrpcmsg('examples.getcookies', array());
951         foreach ($cookies as $cookie => $val) {
952             $this->client->setCookie($cookie, $val);
953             $cookies[$cookie] = (string)$cookies[$cookie];
954         }
955         $r = $this->client->send($m, $this->timeout, $this->method);
956         $this->assertEquals(0, $r->faultCode(), 'Error ' . $r->faultCode() . ' connecting to server: ' . $r->faultString());
957         if (!$r->faultCode()) {
958             $v = $r->value();
959             $v = php_xmlrpc_decode($v);
960
961             // take care for the extra cookie used for coverage collection
962             if (isset($v['PHPUNIT_SELENIUM_TEST_ID'])) {
963                 unset($v['PHPUNIT_SELENIUM_TEST_ID']);
964             }
965
966             // on IIS and Apache getallheaders returns something slightly different...
967             $this->assertEquals($cookies, $v);
968         }
969     }
970
971     public function testServerComments()
972     {
973         $m = new xmlrpcmsg('tests.xmlrpcServerMethodsContainer.debugMessageGenerator', array(
974             new xmlrpcval('hello world', 'string'),
975         ));
976         $r = $this->send($m, 0, true);
977         $this->assertStringContainsString('hello world', $r->raw_data);
978     }
979
980     public function testSendTwiceSameMsg()
981     {
982         $m = new xmlrpcmsg('examples.stringecho', array(
983             new xmlrpcval('hello world', 'string'),
984         ));
985         $v1 = $this->send($m);
986         $v2 = $this->send($m);
987         if ($v1 && $v2) {
988             $this->assertEquals($v1, $v2);
989         }
990     }
991 }