Merge "OutputPage: Use PageViewLanguage instead of PageLanguage for mw.config"
[lhc/web/wiklou.git] / tests / phpunit / includes / media / XMPTest.php
1 <?php
2
3 /**
4 * @group Media
5 * @covers XMPReader
6 */
7 class XMPTest extends MediaWikiTestCase {
8
9 protected function setUp() {
10 parent::setUp();
11 $this->checkPHPExtension( 'exif' ); # Requires libxml to do XMP parsing
12 }
13
14 /**
15 * Put XMP in, compare what comes out...
16 *
17 * @param string $xmp The actual xml data.
18 * @param array $expected Expected result of parsing the xmp.
19 * @param string $info Short sentence on what's being tested.
20 *
21 * @throws Exception
22 * @dataProvider provideXMPParse
23 *
24 * @covers XMPReader::parse
25 */
26 public function testXMPParse( $xmp, $expected, $info ) {
27 if ( !is_string( $xmp ) || !is_array( $expected ) ) {
28 throw new Exception( "Invalid data provided to " . __METHOD__ );
29 }
30 $reader = new XMPReader;
31 $reader->parse( $xmp );
32 $this->assertEquals( $expected, $reader->getResults(), $info, 0.0000000001 );
33 }
34
35 public static function provideXMPParse() {
36 $xmpPath = __DIR__ . '/../../data/xmp/';
37 $data = [];
38
39 // $xmpFiles format: array of arrays with first arg file base name,
40 // with the actual file having .xmp on the end for the xmp
41 // and .result.php on the end for a php file containing the result
42 // array. Second argument is some info on what's being tested.
43 $xmpFiles = [
44 [ '1', 'parseType=Resource test' ],
45 [ '2', 'Structure with mixed attribute and element props' ],
46 [ '3', 'Extra qualifiers (that should be ignored)' ],
47 [ '3-invalid', 'Test ignoring qualifiers that look like normal props' ],
48 [ '4', 'Flash as qualifier' ],
49 [ '5', 'Flash as qualifier 2' ],
50 [ '6', 'Multiple rdf:Description' ],
51 [ '7', 'Generic test of several property types' ],
52 [ 'flash', 'Test of Flash property' ],
53 [ 'invalid-child-not-struct', 'Test child props not in struct or ignored' ],
54 [ 'no-recognized-props', 'Test namespace and no recognized props' ],
55 [ 'no-namespace', 'Test non-namespaced attributes are ignored' ],
56 [ 'bag-for-seq', "Allow bag's instead of seq's. (bug 27105)" ],
57 [ 'utf16BE', 'UTF-16BE encoding' ],
58 [ 'utf16LE', 'UTF-16LE encoding' ],
59 [ 'utf32BE', 'UTF-32BE encoding' ],
60 [ 'utf32LE', 'UTF-32LE encoding' ],
61 [ 'xmpExt', 'Extended XMP missing second part' ],
62 [ 'gps', 'Handling of exif GPS parameters in XMP' ],
63 ];
64
65 $xmpFiles[] = [ 'doctype-included', 'XMP includes doctype' ];
66
67 foreach ( $xmpFiles as $file ) {
68 $xmp = file_get_contents( $xmpPath . $file[0] . '.xmp' );
69 // I'm not sure if this is the best way to handle getting the
70 // result array, but it seems kind of big to put directly in the test
71 // file.
72 $result = null;
73 include $xmpPath . $file[0] . '.result.php';
74 $data[] = [ $xmp, $result, '[' . $file[0] . '.xmp] ' . $file[1] ];
75 }
76
77 return $data;
78 }
79
80 /** Test ExtendedXMP block support. (Used when the XMP has to be split
81 * over multiple jpeg segments, due to 64k size limit on jpeg segments.
82 *
83 * @todo This is based on what the standard says. Need to find a real
84 * world example file to double check the support for this is right.
85 *
86 * @covers XMPReader::parseExtended
87 */
88 public function testExtendedXMP() {
89 $xmpPath = __DIR__ . '/../../data/xmp/';
90 $standardXMP = file_get_contents( $xmpPath . 'xmpExt.xmp' );
91 $extendedXMP = file_get_contents( $xmpPath . 'xmpExt2.xmp' );
92
93 $md5sum = '28C74E0AC2D796886759006FBE2E57B7'; // of xmpExt2.xmp
94 $length = pack( 'N', strlen( $extendedXMP ) );
95 $offset = pack( 'N', 0 );
96 $extendedPacket = $md5sum . $length . $offset . $extendedXMP;
97
98 $reader = new XMPReader();
99 $reader->parse( $standardXMP );
100 $reader->parseExtended( $extendedPacket );
101 $actual = $reader->getResults();
102
103 $expected = [
104 'xmp-exif' => [
105 'DigitalZoomRatio' => '0/10',
106 'Flash' => 9,
107 'FNumber' => '2/10',
108 ]
109 ];
110
111 $this->assertEquals( $expected, $actual );
112 }
113
114 /**
115 * This test has an extended XMP block with a wrong guid (md5sum)
116 * and thus should only return the StandardXMP, not the ExtendedXMP.
117 *
118 * @covers XMPReader::parseExtended
119 */
120 public function testExtendedXMPWithWrongGUID() {
121 $xmpPath = __DIR__ . '/../../data/xmp/';
122 $standardXMP = file_get_contents( $xmpPath . 'xmpExt.xmp' );
123 $extendedXMP = file_get_contents( $xmpPath . 'xmpExt2.xmp' );
124
125 $md5sum = '28C74E0AC2D796886759006FBE2E57B9'; // Note last digit.
126 $length = pack( 'N', strlen( $extendedXMP ) );
127 $offset = pack( 'N', 0 );
128 $extendedPacket = $md5sum . $length . $offset . $extendedXMP;
129
130 $reader = new XMPReader();
131 $reader->parse( $standardXMP );
132 $reader->parseExtended( $extendedPacket );
133 $actual = $reader->getResults();
134
135 $expected = [
136 'xmp-exif' => [
137 'DigitalZoomRatio' => '0/10',
138 'Flash' => 9,
139 ]
140 ];
141
142 $this->assertEquals( $expected, $actual );
143 }
144
145 /**
146 * Have a high offset to simulate a missing packet,
147 * which should cause it to ignore the ExtendedXMP packet.
148 *
149 * @covers XMPReader::parseExtended
150 */
151 public function testExtendedXMPMissingPacket() {
152 $xmpPath = __DIR__ . '/../../data/xmp/';
153 $standardXMP = file_get_contents( $xmpPath . 'xmpExt.xmp' );
154 $extendedXMP = file_get_contents( $xmpPath . 'xmpExt2.xmp' );
155
156 $md5sum = '28C74E0AC2D796886759006FBE2E57B7'; // of xmpExt2.xmp
157 $length = pack( 'N', strlen( $extendedXMP ) );
158 $offset = pack( 'N', 2048 );
159 $extendedPacket = $md5sum . $length . $offset . $extendedXMP;
160
161 $reader = new XMPReader();
162 $reader->parse( $standardXMP );
163 $reader->parseExtended( $extendedPacket );
164 $actual = $reader->getResults();
165
166 $expected = [
167 'xmp-exif' => [
168 'DigitalZoomRatio' => '0/10',
169 'Flash' => 9,
170 ]
171 ];
172
173 $this->assertEquals( $expected, $actual );
174 }
175
176 /**
177 * Test for multi-section, hostile XML
178 * @covers XMPReader::checkParseSafety
179 */
180 public function testCheckParseSafety() {
181
182 // Test for detection
183 $xmpPath = __DIR__ . '/../../data/xmp/';
184 $file = fopen( $xmpPath . 'doctype-included.xmp', 'rb' );
185 $valid = false;
186 $reader = new XMPReader();
187 do {
188 $chunk = fread( $file, 10 );
189 $valid = $reader->parse( $chunk, feof( $file ) );
190 } while ( !feof( $file ) );
191 $this->assertFalse( $valid, 'Check that doctype is detected in fragmented XML' );
192 $this->assertEquals(
193 [],
194 $reader->getResults(),
195 'Check that doctype is detected in fragmented XML'
196 );
197 fclose( $file );
198 unset( $reader );
199
200 // Test for false positives
201 $file = fopen( $xmpPath . 'doctype-not-included.xmp', 'rb' );
202 $valid = false;
203 $reader = new XMPReader();
204 do {
205 $chunk = fread( $file, 10 );
206 $valid = $reader->parse( $chunk, feof( $file ) );
207 } while ( !feof( $file ) );
208 $this->assertTrue(
209 $valid,
210 'Check for false-positive detecting doctype in fragmented XML'
211 );
212 $this->assertEquals(
213 [
214 'xmp-exif' => [
215 'DigitalZoomRatio' => '0/10',
216 'Flash' => '9'
217 ]
218 ],
219 $reader->getResults(),
220 'Check that doctype is detected in fragmented XML'
221 );
222 }
223 }