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