diff --git a/webservice/rest/locallib.php b/webservice/rest/locallib.php
index 1ead1513413..f0bd340b166 100644
--- a/webservice/rest/locallib.php
+++ b/webservice/rest/locallib.php
@@ -224,7 +224,8 @@ class webservice_rest_server extends webservice_base_server {
} else if ($desc instanceof external_single_structure) {
$single = ''."\n";
foreach ($desc->keys as $key=>$subdesc) {
- $single .= ''.self::xmlize_result($returns[$key], $subdesc).''."\n";
+ $value = isset($returns[$key]) ? $returns[$key] : null;
+ $single .= ''.self::xmlize_result($value, $subdesc).''."\n";
}
$single .= ''."\n";
return $single;
diff --git a/webservice/rest/tests/server_test.php b/webservice/rest/tests/server_test.php
new file mode 100644
index 00000000000..354fc6048f4
--- /dev/null
+++ b/webservice/rest/tests/server_test.php
@@ -0,0 +1,217 @@
+.
+
+/**
+ * Rest server tests.
+ *
+ * @package webservice_rest
+ * @copyright 2016 Frédéric Massart - FMCorz.net
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+global $CFG;
+
+require_once($CFG->libdir . '/externallib.php');
+require_once($CFG->dirroot . '/webservice/rest/locallib.php');
+
+/**
+ * Rest server testcase.
+ *
+ * @package webservice_rest
+ * @copyright 2016 Frédéric Massart - FMCorz.net
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class webservice_rest_server_testcase extends advanced_testcase {
+
+ /**
+ * Data provider for test_xmlize.
+ * @return array
+ */
+ public function xmlize_provider() {
+ $data = [];
+ $data[] = [null, null, ''];
+ $data[] = [new external_value(PARAM_BOOL), false, "0\n"];
+ $data[] = [new external_value(PARAM_BOOL), true, "1\n"];
+ $data[] = [new external_value(PARAM_ALPHA), null, "\n"];
+ $data[] = [new external_value(PARAM_ALPHA), 'a', "a\n"];
+ $data[] = [new external_value(PARAM_INT), 123, "123\n"];
+ $data[] = [
+ new external_multiple_structure(new external_value(PARAM_INT)),
+ [1, 2, 3],
+ "\n" .
+ "1\n" .
+ "2\n" .
+ "3\n" .
+ "\n"
+ ];
+ $data[] = [ // Multiple structure with null value.
+ new external_multiple_structure(new external_value(PARAM_ALPHA)),
+ ['A', null, 'C'],
+ "\n" .
+ "A\n" .
+ "\n" .
+ "C\n" .
+ "\n"
+ ];
+ $data[] = [ // Multiple structure without values.
+ new external_multiple_structure(new external_value(PARAM_ALPHA)),
+ [],
+ "\n" .
+ "\n"
+ ];
+ $data[] = [
+ new external_single_structure([
+ 'one' => new external_value(PARAM_INT),
+ 'two' => new external_value(PARAM_INT),
+ 'three' => new external_value(PARAM_INT),
+ ]),
+ ['one' => 1, 'two' => 2, 'three' => 3],
+ "\n" .
+ "1\n\n" .
+ "2\n\n" .
+ "3\n\n" .
+ "\n"
+ ];
+ $data[] = [ // Single structure with null value.
+ new external_single_structure([
+ 'one' => new external_value(PARAM_INT),
+ 'two' => new external_value(PARAM_INT),
+ 'three' => new external_value(PARAM_INT),
+ ]),
+ ['one' => 1, 'two' => null, 'three' => 3],
+ "\n" .
+ "1\n\n" .
+ "\n\n" .
+ "3\n\n" .
+ "\n"
+ ];
+ $data[] = [ // Single structure missing keys.
+ new external_single_structure([
+ 'one' => new external_value(PARAM_INT),
+ 'two' => new external_value(PARAM_INT),
+ 'three' => new external_value(PARAM_INT),
+ ]),
+ ['two' => null, 'three' => 3],
+ "\n" .
+ "\n\n" .
+ "\n\n" .
+ "3\n\n" .
+ "\n"
+ ];
+ $data[] = [ // Nested structure.
+ new external_single_structure([
+ 'one' => new external_multiple_structure(
+ new external_value(PARAM_INT)
+ ),
+ 'two' => new external_multiple_structure(
+ new external_single_structure([
+ 'firstname' => new external_value(PARAM_RAW),
+ 'lastname' => new external_value(PARAM_RAW),
+ ])
+ ),
+ 'three' => new external_single_structure([
+ 'firstname' => new external_value(PARAM_RAW),
+ 'lastname' => new external_value(PARAM_RAW),
+ ]),
+ ]),
+ [
+ 'one' => [2, 3, 4],
+ 'two' => [
+ ['firstname' => 'Louis', 'lastname' => 'Armstrong'],
+ ['firstname' => 'Neil', 'lastname' => 'Armstrong'],
+ ],
+ 'three' => ['firstname' => 'Neil', 'lastname' => 'Armstrong'],
+ ],
+ "\n" .
+ "\n".
+ "2\n" .
+ "3\n" .
+ "4\n" .
+ "\n\n" .
+ "\n".
+ "\n" .
+ "Louis\n\n" .
+ "Armstrong\n\n" .
+ "\n" .
+ "\n" .
+ "Neil\n\n" .
+ "Armstrong\n\n" .
+ "\n" .
+ "\n\n" .
+ "\n" .
+ "Neil\n\n" .
+ "Armstrong\n\n" .
+ "\n\n" .
+ "\n"
+ ];
+ $data[] = [ // Nested structure with missing keys.
+ new external_single_structure([
+ 'one' => new external_multiple_structure(
+ new external_value(PARAM_INT)
+ ),
+ 'two' => new external_multiple_structure(
+ new external_single_structure([
+ 'firstname' => new external_value(PARAM_RAW),
+ 'lastname' => new external_value(PARAM_RAW),
+ ])
+ ),
+ 'three' => new external_single_structure([
+ 'firstname' => new external_value(PARAM_RAW),
+ 'lastname' => new external_value(PARAM_RAW),
+ ]),
+ ]),
+ [
+ 'two' => [
+ ['firstname' => 'Louis'],
+ ['lastname' => 'Armstrong'],
+ ],
+ 'three' => ['lastname' => 'Armstrong'],
+ ],
+ "\n" .
+ "\n\n\n" .
+ "\n".
+ "\n" .
+ "Louis\n\n" .
+ "\n\n" .
+ "\n" .
+ "\n" .
+ "\n\n" .
+ "Armstrong\n\n" .
+ "\n" .
+ "\n\n" .
+ "\n" .
+ "\n\n" .
+ "Armstrong\n\n" .
+ "\n\n" .
+ "\n"
+ ];
+ return $data;
+ }
+
+ /**
+ * @dataProvider xmlize_provider
+ * @param external_description $description The data structure.
+ * @param mixed $value The value to xmlise.
+ * @param mixed $expected The expected output.
+ */
+ public function test_xmlize($description, $value, $expected) {
+ $method = new ReflectionMethod('webservice_rest_server', 'xmlize_result');
+ $method->setAccessible(true);
+ $this->assertEquals($expected, $method->invoke(null, $value, $description));
+ }
+
+}