MediaWiki  REL1_24
FormatJsonTest.php
Go to the documentation of this file.
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 }