1<?php 2 3/* 4 * This file is part of the Symfony package. 5 * 6 * (c) Fabien Potencier <fabien@symfony.com> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Symfony\Component\Intl\Tests\Data\Bundle\Reader; 13 14use PHPUnit\Framework\MockObject\MockObject; 15use PHPUnit\Framework\TestCase; 16use Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReader; 17use Symfony\Component\Intl\Exception\ResourceBundleNotFoundException; 18 19/** 20 * @author Bernhard Schussek <bschussek@gmail.com> 21 */ 22class BundleEntryReaderTest extends TestCase 23{ 24 const RES_DIR = '/res/dir'; 25 26 /** 27 * @var BundleEntryReader 28 */ 29 private $reader; 30 31 /** 32 * @var MockObject 33 */ 34 private $readerImpl; 35 36 private static $data = [ 37 'Entries' => [ 38 'Foo' => 'Bar', 39 'Bar' => 'Baz', 40 ], 41 'Foo' => 'Bar', 42 'Version' => '2.0', 43 ]; 44 45 private static $fallbackData = [ 46 'Entries' => [ 47 'Foo' => 'Foo', 48 'Bam' => 'Lah', 49 ], 50 'Baz' => 'Foo', 51 'Version' => '1.0', 52 ]; 53 54 private static $mergedData = [ 55 // no recursive merging -> too complicated 56 'Entries' => [ 57 'Foo' => 'Bar', 58 'Bar' => 'Baz', 59 ], 60 'Baz' => 'Foo', 61 'Version' => '2.0', 62 'Foo' => 'Bar', 63 ]; 64 65 protected function setUp() 66 { 67 $this->readerImpl = $this->getMockBuilder('Symfony\Component\Intl\Data\Bundle\Reader\BundleEntryReaderInterface')->getMock(); 68 $this->reader = new BundleEntryReader($this->readerImpl); 69 } 70 71 public function testForwardCallToRead() 72 { 73 $this->readerImpl->expects($this->once()) 74 ->method('read') 75 ->with(self::RES_DIR, 'root') 76 ->willReturn(self::$data); 77 78 $this->assertSame(self::$data, $this->reader->read(self::RES_DIR, 'root')); 79 } 80 81 public function testReadEntireDataFileIfNoIndicesGiven() 82 { 83 $this->readerImpl->expects($this->exactly(2)) 84 ->method('read') 85 ->withConsecutive( 86 [self::RES_DIR, 'en'], 87 [self::RES_DIR, 'root'] 88 ) 89 ->willReturnOnConsecutiveCalls(self::$data, self::$fallbackData); 90 91 $this->assertSame(self::$mergedData, $this->reader->readEntry(self::RES_DIR, 'en', [])); 92 } 93 94 public function testReadExistingEntry() 95 { 96 $this->readerImpl->expects($this->once()) 97 ->method('read') 98 ->with(self::RES_DIR, 'root') 99 ->willReturn(self::$data); 100 101 $this->assertSame('Bar', $this->reader->readEntry(self::RES_DIR, 'root', ['Entries', 'Foo'])); 102 } 103 104 public function testReadNonExistingEntry() 105 { 106 $this->expectException('Symfony\Component\Intl\Exception\MissingResourceException'); 107 $this->readerImpl->expects($this->once()) 108 ->method('read') 109 ->with(self::RES_DIR, 'root') 110 ->willReturn(self::$data); 111 112 $this->reader->readEntry(self::RES_DIR, 'root', ['Entries', 'NonExisting']); 113 } 114 115 public function testFallbackIfEntryDoesNotExist() 116 { 117 $this->readerImpl->expects($this->exactly(2)) 118 ->method('read') 119 ->withConsecutive( 120 [self::RES_DIR, 'en_GB'], 121 [self::RES_DIR, 'en'] 122 ) 123 ->willReturnOnConsecutiveCalls(self::$data, self::$fallbackData); 124 125 $this->assertSame('Lah', $this->reader->readEntry(self::RES_DIR, 'en_GB', ['Entries', 'Bam'])); 126 } 127 128 public function testDontFallbackIfEntryDoesNotExistAndFallbackDisabled() 129 { 130 $this->expectException('Symfony\Component\Intl\Exception\MissingResourceException'); 131 $this->readerImpl->expects($this->once()) 132 ->method('read') 133 ->with(self::RES_DIR, 'en_GB') 134 ->willReturn(self::$data); 135 136 $this->reader->readEntry(self::RES_DIR, 'en_GB', ['Entries', 'Bam'], false); 137 } 138 139 public function testFallbackIfLocaleDoesNotExist() 140 { 141 $this->readerImpl->expects($this->exactly(2)) 142 ->method('read') 143 ->withConsecutive( 144 [self::RES_DIR, 'en_GB'], 145 [self::RES_DIR, 'en'] 146 ) 147 ->willReturnOnConsecutiveCalls( 148 $this->throwException(new ResourceBundleNotFoundException()), 149 self::$fallbackData 150 ); 151 152 $this->assertSame('Lah', $this->reader->readEntry(self::RES_DIR, 'en_GB', ['Entries', 'Bam'])); 153 } 154 155 public function testDontFallbackIfLocaleDoesNotExistAndFallbackDisabled() 156 { 157 $this->expectException('Symfony\Component\Intl\Exception\MissingResourceException'); 158 $this->readerImpl->expects($this->once()) 159 ->method('read') 160 ->with(self::RES_DIR, 'en_GB') 161 ->willThrowException(new ResourceBundleNotFoundException()); 162 163 $this->reader->readEntry(self::RES_DIR, 'en_GB', ['Entries', 'Bam'], false); 164 } 165 166 public function provideMergeableValues() 167 { 168 return [ 169 ['foo', null, 'foo'], 170 [null, 'foo', 'foo'], 171 [['foo', 'bar'], null, ['foo', 'bar']], 172 [['foo', 'bar'], [], ['foo', 'bar']], 173 [null, ['baz'], ['baz']], 174 [[], ['baz'], ['baz']], 175 [['foo', 'bar'], ['baz'], ['baz', 'foo', 'bar']], 176 ]; 177 } 178 179 /** 180 * @dataProvider provideMergeableValues 181 */ 182 public function testMergeDataWithFallbackData($childData, $parentData, $result) 183 { 184 if (null === $childData || \is_array($childData)) { 185 $this->readerImpl->expects($this->exactly(2)) 186 ->method('read') 187 ->withConsecutive( 188 [self::RES_DIR, 'en'], 189 [self::RES_DIR, 'root'] 190 ) 191 ->willReturnOnConsecutiveCalls($childData, $parentData); 192 } else { 193 $this->readerImpl->expects($this->once()) 194 ->method('read') 195 ->with(self::RES_DIR, 'en') 196 ->willReturn($childData); 197 } 198 199 $this->assertSame($result, $this->reader->readEntry(self::RES_DIR, 'en', [], true)); 200 } 201 202 /** 203 * @dataProvider provideMergeableValues 204 */ 205 public function testDontMergeDataIfFallbackDisabled($childData, $parentData, $result) 206 { 207 $this->readerImpl->expects($this->once()) 208 ->method('read') 209 ->with(self::RES_DIR, 'en_GB') 210 ->willReturn($childData); 211 212 $this->assertSame($childData, $this->reader->readEntry(self::RES_DIR, 'en_GB', [], false)); 213 } 214 215 /** 216 * @dataProvider provideMergeableValues 217 */ 218 public function testMergeExistingEntryWithExistingFallbackEntry($childData, $parentData, $result) 219 { 220 if (null === $childData || \is_array($childData)) { 221 $this->readerImpl->expects($this->exactly(2)) 222 ->method('read') 223 ->withConsecutive( 224 [self::RES_DIR, 'en'], 225 [self::RES_DIR, 'root'] 226 ) 227 ->willReturnOnConsecutiveCalls( 228 ['Foo' => ['Bar' => $childData]], 229 ['Foo' => ['Bar' => $parentData]] 230 ); 231 } else { 232 $this->readerImpl->expects($this->once()) 233 ->method('read') 234 ->with(self::RES_DIR, 'en') 235 ->willReturn(['Foo' => ['Bar' => $childData]]); 236 } 237 238 $this->assertSame($result, $this->reader->readEntry(self::RES_DIR, 'en', ['Foo', 'Bar'], true)); 239 } 240 241 /** 242 * @dataProvider provideMergeableValues 243 */ 244 public function testMergeNonExistingEntryWithExistingFallbackEntry($childData, $parentData, $result) 245 { 246 $this->readerImpl 247 ->method('read') 248 ->withConsecutive( 249 [self::RES_DIR, 'en_GB'], 250 [self::RES_DIR, 'en'] 251 ) 252 ->willReturnOnConsecutiveCalls(['Foo' => 'Baz'], ['Foo' => ['Bar' => $parentData]]); 253 254 $this->assertSame($parentData, $this->reader->readEntry(self::RES_DIR, 'en_GB', ['Foo', 'Bar'], true)); 255 } 256 257 /** 258 * @dataProvider provideMergeableValues 259 */ 260 public function testMergeExistingEntryWithNonExistingFallbackEntry($childData, $parentData, $result) 261 { 262 if (null === $childData || \is_array($childData)) { 263 $this->readerImpl 264 ->method('read') 265 ->withConsecutive( 266 [self::RES_DIR, 'en_GB'], 267 [self::RES_DIR, 'en'] 268 ) 269 ->willReturnOnConsecutiveCalls(['Foo' => ['Bar' => $childData]], ['Foo' => 'Bar']); 270 } else { 271 $this->readerImpl->expects($this->once()) 272 ->method('read') 273 ->with(self::RES_DIR, 'en_GB') 274 ->willReturn(['Foo' => ['Bar' => $childData]]); 275 } 276 277 $this->assertSame($childData, $this->reader->readEntry(self::RES_DIR, 'en_GB', ['Foo', 'Bar'], true)); 278 } 279 280 public function testFailIfEntryFoundNeitherInParentNorChild() 281 { 282 $this->expectException('Symfony\Component\Intl\Exception\MissingResourceException'); 283 $this->readerImpl 284 ->method('read') 285 ->withConsecutive( 286 [self::RES_DIR, 'en_GB'], 287 [self::RES_DIR, 'en'] 288 ) 289 ->willReturnOnConsecutiveCalls(['Foo' => 'Baz'], ['Foo' => 'Bar']); 290 291 $this->reader->readEntry(self::RES_DIR, 'en_GB', ['Foo', 'Bar'], true); 292 } 293 294 /** 295 * @dataProvider provideMergeableValues 296 */ 297 public function testMergeTraversables($childData, $parentData, $result) 298 { 299 $parentData = \is_array($parentData) ? new \ArrayObject($parentData) : $parentData; 300 $childData = \is_array($childData) ? new \ArrayObject($childData) : $childData; 301 302 if (null === $childData || $childData instanceof \ArrayObject) { 303 $this->readerImpl 304 ->method('read') 305 ->withConsecutive( 306 [self::RES_DIR, 'en_GB'], 307 [self::RES_DIR, 'en'] 308 ) 309 ->willReturnOnConsecutiveCalls(['Foo' => ['Bar' => $childData]], ['Foo' => ['Bar' => $parentData]]); 310 } else { 311 $this->readerImpl->expects($this->once()) 312 ->method('read') 313 ->with(self::RES_DIR, 'en_GB') 314 ->willReturn(['Foo' => ['Bar' => $childData]]); 315 } 316 317 $this->assertSame($result, $this->reader->readEntry(self::RES_DIR, 'en_GB', ['Foo', 'Bar'], true)); 318 } 319 320 /** 321 * @dataProvider provideMergeableValues 322 */ 323 public function testFollowLocaleAliases($childData, $parentData, $result) 324 { 325 $this->reader->setLocaleAliases(['mo' => 'ro_MD']); 326 327 if (null === $childData || \is_array($childData)) { 328 $this->readerImpl 329 ->method('read') 330 ->withConsecutive( 331 [self::RES_DIR, 'ro_MD'], 332 // Read fallback locale of aliased locale ("ro_MD" -> "ro") 333 [self::RES_DIR, 'ro'] 334 ) 335 ->willReturnOnConsecutiveCalls(['Foo' => ['Bar' => $childData]], ['Foo' => ['Bar' => $parentData]]); 336 } else { 337 $this->readerImpl->expects($this->once()) 338 ->method('read') 339 ->with(self::RES_DIR, 'ro_MD') 340 ->willReturn(['Foo' => ['Bar' => $childData]]); 341 } 342 343 $this->assertSame($result, $this->reader->readEntry(self::RES_DIR, 'mo', ['Foo', 'Bar'], true)); 344 } 345} 346