MediaWiki
REL1_24
|
00001 <?php 00002 00006 class FormatJsonTest extends MediaWikiTestCase { 00007 00008 public static function provideEncoderPrettyPrinting() { 00009 return array( 00010 // Four spaces 00011 array( true, ' ' ), 00012 array( ' ', ' ' ), 00013 // Two spaces 00014 array( ' ', ' ' ), 00015 // One tab 00016 array( "\t", "\t" ), 00017 ); 00018 } 00019 00023 public function testEncoderPrettyPrinting( $pretty, $expectedIndent ) { 00024 $obj = array( 00025 'emptyObject' => new stdClass, 00026 'emptyArray' => array(), 00027 'string' => 'foobar\\', 00028 'filledArray' => array( 00029 array( 00030 123, 00031 456, 00032 ), 00033 // Nested json works without problems 00034 '"7":["8",{"9":"10"}]', 00035 // Whitespace clean up doesn't touch strings that look alike 00036 "{\n\t\"emptyObject\": {\n\t},\n\t\"emptyArray\": [ ]\n}", 00037 ), 00038 ); 00039 00040 // No trailing whitespace, no trailing linefeed 00041 $json = '{ 00042 "emptyObject": {}, 00043 "emptyArray": [], 00044 "string": "foobar\\\\", 00045 "filledArray": [ 00046 [ 00047 123, 00048 456 00049 ], 00050 "\"7\":[\"8\",{\"9\":\"10\"}]", 00051 "{\n\t\"emptyObject\": {\n\t},\n\t\"emptyArray\": [ ]\n}" 00052 ] 00053 }'; 00054 00055 $json = str_replace( "\r", '', $json ); // Windows compat 00056 $json = str_replace( "\t", $expectedIndent, $json ); 00057 $this->assertSame( $json, FormatJson::encode( $obj, $pretty ) ); 00058 } 00059 00060 public static function provideEncodeDefault() { 00061 return self::getEncodeTestCases( array() ); 00062 } 00063 00067 public function testEncodeDefault( $from, $to ) { 00068 $this->assertSame( $to, FormatJson::encode( $from ) ); 00069 } 00070 00071 public static function provideEncodeUtf8() { 00072 return self::getEncodeTestCases( array( 'unicode' ) ); 00073 } 00074 00078 public function testEncodeUtf8( $from, $to ) { 00079 $this->assertSame( $to, FormatJson::encode( $from, false, FormatJson::UTF8_OK ) ); 00080 } 00081 00082 public static function provideEncodeXmlMeta() { 00083 return self::getEncodeTestCases( array( 'xmlmeta' ) ); 00084 } 00085 00089 public function testEncodeXmlMeta( $from, $to ) { 00090 $this->assertSame( $to, FormatJson::encode( $from, false, FormatJson::XMLMETA_OK ) ); 00091 } 00092 00093 public static function provideEncodeAllOk() { 00094 return self::getEncodeTestCases( array( 'unicode', 'xmlmeta' ) ); 00095 } 00096 00100 public function testEncodeAllOk( $from, $to ) { 00101 $this->assertSame( $to, FormatJson::encode( $from, false, FormatJson::ALL_OK ) ); 00102 } 00103 00104 public function testEncodePhpBug46944() { 00105 $this->assertNotEquals( 00106 '\ud840\udc00', 00107 strtolower( FormatJson::encode( "\xf0\xa0\x80\x80" ) ), 00108 'Test encoding an broken json_encode character (U+20000)' 00109 ); 00110 } 00111 00112 public function testDecodeReturnType() { 00113 $this->assertInternalType( 00114 'object', 00115 FormatJson::decode( '{"Name": "Cheeso", "Rank": 7}' ), 00116 'Default to object' 00117 ); 00118 00119 $this->assertInternalType( 00120 'array', 00121 FormatJson::decode( '{"Name": "Cheeso", "Rank": 7}', true ), 00122 'Optional array' 00123 ); 00124 } 00125 00126 public static function provideParse() { 00127 return array( 00128 array( null ), 00129 array( true ), 00130 array( false ), 00131 array( 0 ), 00132 array( 1 ), 00133 array( 1.2 ), 00134 array( '' ), 00135 array( 'str' ), 00136 array( array( 0, 1, 2 ) ), 00137 array( array( 'a' => 'b' ) ), 00138 array( array( 'a' => 'b' ) ), 00139 array( array( 'a' => 'b', 'x' => array( 'c' => 'd' ) ) ), 00140 ); 00141 } 00142 00148 public static function toObject( $value ) { 00149 return !is_array( $value ) ? $value : (object) array_map( __METHOD__, $value ); 00150 } 00151 00156 public function testParse( $value ) { 00157 $expected = self::toObject( $value ); 00158 $json = FormatJson::encode( $expected, false, FormatJson::ALL_OK ); 00159 $this->assertJson( $json ); 00160 00161 $st = FormatJson::parse( $json ); 00162 $this->assertType( 'Status', $st ); 00163 $this->assertTrue( $st->isGood() ); 00164 $this->assertEquals( $expected, $st->getValue() ); 00165 00166 $st = FormatJson::parse( $json, FormatJson::FORCE_ASSOC ); 00167 $this->assertType( 'Status', $st ); 00168 $this->assertTrue( $st->isGood() ); 00169 $this->assertEquals( $value, $st->getValue() ); 00170 } 00171 00172 public static function provideParseTryFixing() { 00173 return array( 00174 array( "[,]", '[]' ), 00175 array( "[ , ]", '[]' ), 00176 array( "[ , }", false ), 00177 array( '[1],', false ), 00178 array( "[1,]", '[1]' ), 00179 array( "[1\n,]", '[1]' ), 00180 array( "[1,\n]", '[1]' ), 00181 array( "[1,]\n", '[1]' ), 00182 array( "[1\n,\n]\n", '[1]' ), 00183 array( '["a,",]', '["a,"]' ), 00184 array( "[[1,]\n,[2,\n],[3\n,]]", '[[1],[2],[3]]' ), 00185 array( '[[1,],[2,],[3,]]', false ), // I wish we could parse this, but would need quote parsing 00186 array( '[1,,]', false ), 00187 ); 00188 } 00189 00195 public function testParseTryFixing( $value, $expected ) { 00196 $st = FormatJson::parse( $value, FormatJson::TRY_FIXING ); 00197 $this->assertType( 'Status', $st ); 00198 if ( $expected === false ) { 00199 $this->assertFalse( $st->isOK() ); 00200 } else { 00201 $this->assertFalse( $st->isGood() ); 00202 $this->assertTrue( $st->isOK() ); 00203 $val = FormatJson::encode( $st->getValue(), false, FormatJson::ALL_OK ); 00204 $this->assertEquals( $expected, $val ); 00205 } 00206 } 00207 00208 public static function provideParseErrors() { 00209 return array( 00210 array( 'aaa' ), 00211 array( '{"j": 1 ] }' ), 00212 ); 00213 } 00214 00219 public function testParseErrors( $value ) { 00220 $st = FormatJson::parse( $value ); 00221 $this->assertType( 'Status', $st ); 00222 $this->assertFalse( $st->isOK() ); 00223 } 00224 00231 private static function getEncodeTestCases( array $unescapedGroups ) { 00232 $groups = array( 00233 'always' => array( 00234 // Forward slash (always unescaped) 00235 '/' => '/', 00236 00237 // Control characters 00238 "\0" => '\u0000', 00239 "\x08" => '\b', 00240 "\t" => '\t', 00241 "\n" => '\n', 00242 "\r" => '\r', 00243 "\f" => '\f', 00244 "\x1f" => '\u001f', // representative example 00245 00246 // Double quotes 00247 '"' => '\"', 00248 00249 // Backslashes 00250 '\\' => '\\\\', 00251 '\\\\' => '\\\\\\\\', 00252 '\\u00e9' => '\\\u00e9', // security check for Unicode unescaping 00253 00254 // Line terminators 00255 "\xe2\x80\xa8" => '\u2028', 00256 "\xe2\x80\xa9" => '\u2029', 00257 ), 00258 'unicode' => array( 00259 "\xc3\xa9" => '\u00e9', 00260 "\xf0\x9d\x92\x9e" => '\ud835\udc9e', // U+1D49E, outside the BMP 00261 ), 00262 'xmlmeta' => array( 00263 '<' => '\u003C', // JSON_HEX_TAG uses uppercase hex digits 00264 '>' => '\u003E', 00265 '&' => '\u0026', 00266 ), 00267 ); 00268 00269 $cases = array(); 00270 foreach ( $groups as $name => $rules ) { 00271 $leaveUnescaped = in_array( $name, $unescapedGroups ); 00272 foreach ( $rules as $from => $to ) { 00273 $cases[] = array( $from, '"' . ( $leaveUnescaped ? $from : $to ) . '"' ); 00274 } 00275 } 00276 00277 return $cases; 00278 } 00279 }