Completed
Push — fix/changelogger-in-subdir ( cf1128 )
by
unknown
44:01 queued 34:14
created

ConfigTest::testLoadBeforeSetOutput()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2
/**
3
 * Tests for the changelogger config.
4
 *
5
 * @package automattic/jetpack-changelogger
6
 */
7
8
// phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_putenv, WordPress.WP.AlternativeFunctions, WordPress.NamingConventions.ValidVariableName
9
10
namespace Automattic\Jetpack\Changelogger\Tests;
11
12
use Automattic\Jetpack\Changelogger\Config;
13
use Automattic\Jetpack\Changelogger\ConfigException;
14
use Automattic\Jetpack\Changelogger\PluginTrait;
15
use Wikimedia\TestingAccessWrapper;
16
17
/**
18
 * Tests for the changelogger config.
19
 *
20
 * @covers \Automattic\Jetpack\Changelogger\Config
21
 */
22
class ConfigTest extends TestCase {
23
	use \Yoast\PHPUnitPolyfills\Polyfills\ExpectException;
24
25
	/**
26
	 * Set up.
27
	 *
28
	 * @before
29
	 */
30
	public function set_up() {
31
		$this->useTempDir();
32
33
		file_put_contents( 'bogus.json', "bogus\n" );
34
		$this->writeComposerJson(
35
			array(
36
				'types'  => (object) array(),
37
				'foobar' => 'baz',
38
			),
39
			'no-types.json'
40
		);
41
	}
42
43
	/**
44
	 * Write a composer.json file.
45
	 *
46
	 * @param array  $config Contents for `.extra.changelogger`.
47
	 * @param string $file Filename.
48
	 */
49
	public function writeComposerJson( array $config, $file = 'composer.json' ) {
50
		file_put_contents(
51
			$file,
52
			json_encode(
53
				array( 'extra' => array( 'changelogger' => $config ) ),
54
				JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE
55
			)
56
		);
57
	}
58
59
	/**
60
	 * Test parsing composer.json, default locating.
61
	 */
62
	public function testLoad() {
63
		$this->resetConfigCache();
64
65
		$w            = TestingAccessWrapper::newFromClass( Config::class );
66
		$expectConfig = array( 'base' => getcwd() ) + $w->defaultConfig;
67
68
		putenv( 'COMPOSER' );
69
		$w->load();
70
		$this->assertEquals( $expectConfig, $w->config );
71
72
		// Second load call should do nothing.
73
		putenv( 'COMPOSER=./doesnotexist.json' );
74
		$w->load();
75
		$this->assertEquals( $expectConfig, $w->config );
76
	}
77
78
	/**
79
	 * Test parsing composer.json, from environment variable.
80
	 */
81
	public function testLoad_env() {
82
		$this->resetConfigCache();
83
84
		$w            = TestingAccessWrapper::newFromClass( Config::class );
85
		$expectConfig = array(
86
			'base'   => getcwd(),
87
			'types'  => array(),
88
			'foobar' => 'baz',
89
		) + $w->defaultConfig;
90
91
		putenv( 'COMPOSER=no-types.json' );
92
		$w->load();
93
		$this->assertEquals( $expectConfig, $w->config );
94
95
		// Second load call should do nothing.
96
		putenv( 'COMPOSER=./doesnotexist.json' );
97
		$w->load();
98
		$this->assertEquals( $expectConfig, $w->config );
99
	}
100
101
	/**
102
	 * Test parsing composer.json, explicit set.
103
	 */
104
	public function testLoad_explicit() {
105
		$this->resetConfigCache();
106
107
		$w            = TestingAccessWrapper::newFromClass( Config::class );
108
		$expectConfig = array(
109
			'base'   => getcwd(),
110
			'types'  => array(),
111
			'foobar' => 'baz',
112
		) + $w->defaultConfig;
113
114
		putenv( 'COMPOSER=./doesnotexist.json' );
115
		Config::setComposerJsonPath( 'no-types.json' );
116
		$w->load();
117
		$this->assertEquals( $expectConfig, $w->config );
118
119
		// Second load call should do nothing.
120
		putenv( 'COMPOSER=./doesnotexist.json' );
121
		Config::setComposerJsonPath( null );
122
		$w->load();
123
		$this->assertEquals( $expectConfig, $w->config );
124
	}
125
126
	/**
127
	 * Test parsing composer.json, missing file.
128
	 */
129 View Code Duplication
	public function testLoad_missing() {
130
		$this->resetConfigCache();
131
132
		$w = TestingAccessWrapper::newFromClass( Config::class );
133
		Config::setComposerJsonPath( 'missing.json' );
134
		$this->expectException( ConfigException::class );
135
		$this->expectExceptionMessage( 'File missing.json is not found.' );
136
		$w->load();
137
	}
138
139
	/**
140
	 * Test parsing composer.json, bogus file.
141
	 */
142 View Code Duplication
	public function testLoad_bogus() {
143
		$this->resetConfigCache();
144
145
		$w = TestingAccessWrapper::newFromClass( Config::class );
146
		Config::setComposerJsonPath( 'bogus.json' );
147
		$this->expectException( ConfigException::class );
148
		$this->expectExceptionMessage( 'File bogus.json could not be parsed.' );
149
		$w->load();
150
	}
151
152
	/**
153
	 * Test the base method.
154
	 */
155
	public function testBase() {
156
		$this->resetConfigCache();
157
		$this->assertSame( getcwd(), Config::base() );
158
159
		$this->resetConfigCache();
160
		putenv( 'COMPOSER=' . __DIR__ . '/../../../../composer.json' );
161
		$this->assertSame( dirname( dirname( dirname( dirname( __DIR__ ) ) ) ), Config::base() );
162
	}
163
164
	/**
165
	 * Test the changelogFile method.
166
	 */
167 View Code Duplication
	public function testChangelogFile() {
168
		$this->resetConfigCache();
169
		$this->assertSame( getcwd() . DIRECTORY_SEPARATOR . 'CHANGELOG.md', Config::changelogFile() );
170
171
		$this->resetConfigCache();
172
		$this->writeComposerJson( array( 'changelog' => 'changes.txt' ) );
173
		$this->assertSame( getcwd() . DIRECTORY_SEPARATOR . 'changes.txt', Config::changelogFile() );
174
175
		$this->resetConfigCache();
176
		$this->writeComposerJson( array( 'changelog' => '/tmp/changes.md' ) );
177
		$this->assertSame( '/tmp/changes.md', Config::changelogFile() );
178
179
		$this->resetConfigCache();
180
		$this->writeComposerJson( array( 'changelog' => 'c:\\changes.md' ) );
181
		$this->assertSame( 'c:\\changes.md', Config::changelogFile() );
182
	}
183
184
	/**
185
	 * Test the changesDir method.
186
	 */
187 View Code Duplication
	public function testChangesDir() {
188
		$this->resetConfigCache();
189
		$this->assertSame( getcwd() . DIRECTORY_SEPARATOR . 'changelog', Config::changesDir() );
190
191
		$this->resetConfigCache();
192
		$this->writeComposerJson( array( 'changes-dir' => 'changes' ) );
193
		$this->assertSame( getcwd() . DIRECTORY_SEPARATOR . 'changes', Config::changesDir() );
194
195
		$this->resetConfigCache();
196
		$this->writeComposerJson( array( 'changes-dir' => '/tmp/changes' ) );
197
		$this->assertSame( '/tmp/changes', Config::changesDir() );
198
199
		$this->resetConfigCache();
200
		$this->writeComposerJson( array( 'changes-dir' => 'c:\\changes' ) );
201
		$this->assertSame( 'c:\\changes', Config::changesDir() );
202
	}
203
204
	/**
205
	 * Test the link method.
206
	 */
207
	public function testLink() {
208
		$this->resetConfigCache();
209
		$w = TestingAccessWrapper::newFromClass( Config::class );
210
211
		$this->assertNull( Config::link( '1.2.3+A', '4.5.6+B' ) );
212
213
		$w->config = array(
214
			'link-template' => 'https://example.com/diff/${old}..${new}',
215
		);
216
		$this->assertSame(
217
			'https://example.com/diff/1.2.3%2BA..4.5.6%2BB',
218
			Config::link( '1.2.3+A', '4.5.6+B' )
219
		);
220
	}
221
222
	/**
223
	 * Test the ordering method.
224
	 */
225
	public function testOrdering() {
226
		$this->resetConfigCache();
227
		$w = TestingAccessWrapper::newFromClass( Config::class );
228
229
		$this->assertSame( $w->defaultConfig['ordering'], Config::ordering() );
230
231
		$w->config = array(
232
			'ordering' => array(
233
				'subheading',
234
				'bogus',
235
				123,
236
				'x' => 'y',
237
			),
238
		);
239
240
		// No change because of caching.
241
		$this->assertSame( $w->defaultConfig['ordering'], Config::ordering() );
242
243
		// Clear cache, now it changes.
244
		$w->cache = array();
245
		$this->assertSame(
246
			array(
247
				'subheading',
248
				'bogus',
249
				'123',
250
				'x' => 'y',
251
			),
252
			Config::ordering()
253
		);
254
255
		// Not really supported, but DWIM.
256
		$w->config = array(
257
			'ordering' => 'content',
258
		);
259
		$w->cache  = array();
260
		$this->assertSame( array( 'content' ), Config::ordering() );
261
	}
262
263
	/**
264
	 * Test the types method.
265
	 */
266
	public function testTypes() {
267
		$this->resetConfigCache();
268
		$w = TestingAccessWrapper::newFromClass( Config::class );
269
270
		$this->assertSame( $w->defaultConfig['types'], Config::types() );
271
272
		$w->config = array(
273
			'types' => array(
274
				'FOO' => 'Stuff',
275
				'bAr' => 'More stuff',
276
			),
277
		);
278
279
		// No change because of caching.
280
		$this->assertSame( $w->defaultConfig['types'], Config::types() );
281
282
		// Clear cache, now it changes.
283
		$w->cache = array();
284
		$this->assertSame(
285
			array(
286
				'foo' => 'Stuff',
287
				'bar' => 'More stuff',
288
			),
289
			Config::types()
290
		);
291
	}
292
293
	/**
294
	 * Test the getPlugin method.
295
	 */
296
	public function testGetPlugin() {
297
		$w = TestingAccessWrapper::newFromClass( Config::class );
298
299
		// Get plugin by class.
300
		$ret = $w->getPlugin(
301
			array(
302
				'class'  => DummyPluginImpl::class,
303
				'option' => 'value',
304
			),
305
			'Dummy',
306
			DummyPlugin::class
307
		);
308
		$this->assertInstanceOf( DummyPluginImpl::class, $ret );
309
		$this->assertSame(
310
			array(
311
				'class'  => DummyPluginImpl::class,
312
				'option' => 'value',
313
			),
314
			$ret->config
315
		);
316
317
		// Get plugin by name.
318
		class_alias( DummyPluginImpl::class, \Automattic\Jetpack\Changelogger\Plugins\FooDummy::class );
319
		$ret = $w->getPlugin( 'foo', 'Dummy', DummyPlugin::class );
320
		$this->assertInstanceOf( DummyPluginImpl::class, $ret );
321
		$this->assertSame( array( 'name' => 'foo' ), $ret->config );
322
		$ret = $w->getPlugin(
323
			array(
324
				'name'   => 'foo',
325
				'option' => 'value',
326
			),
327
			'Dummy',
328
			DummyPlugin::class
329
		);
330
		$this->assertInstanceOf( DummyPluginImpl::class, $ret );
331
		$this->assertSame(
332
			array(
333
				'name'   => 'foo',
334
				'option' => 'value',
335
			),
336
			$ret->config
337
		);
338
339
		// Get by loading file, valid file.
340
		$ns        = __NAMESPACE__;
341
		$classBody = 'implements \\' . DummyPlugin::class . " {\n\tuse \\" . PluginTrait::class . ";\n\tpublic function __construct( \$c ) { \$this->c = \$c; }\n}";
342
		file_put_contents(
343
			'dummy.php',
344
			"<?php\nnamespace $ns;\nclass TestFromFile $classBody\n"
345
		);
346
		$ret = $w->getPlugin(
347
			array(
348
				'filename' => 'dummy.php',
349
				'option'   => 'value',
350
			),
351
			'Dummy',
352
			DummyPlugin::class
353
		);
354
		$this->assertInstanceOf( __NAMESPACE__ . '\\TestFromFile', $ret );
355
		$this->assertSame(
356
			array(
357
				'filename' => 'dummy.php',
358
				'option'   => 'value',
359
			),
360
			$ret->c
361
		);
362
363
		// Get by loading file, file with no classes.
364
		file_put_contents( 'dummy2.php', "<?php\n" );
365
		$this->assertNull( $w->getPlugin( array( 'filename' => 'dummy2.php' ), 'Dummy', DummyPlugin::class ) );
366
367
		// Get by loading file, file with no valid classes.
368
		file_put_contents(
369
			'dummy3.php',
370
			"<?php\nnamespace $ns;\nclass TestFromFile3 {}\n"
371
		);
372
		$this->assertNull( $w->getPlugin( array( 'filename' => 'dummy3.php' ), 'Dummy', DummyPlugin::class ) );
373
374
		// Get by loading file, file with one valid class.
375
		file_put_contents(
376
			'dummy4.php',
377
			"<?php\nnamespace $ns;\nclass TestFromFile4a {}\nclass TestFromFile4b $classBody\n"
378
		);
379
		$ret = $w->getPlugin( array( 'filename' => 'dummy4.php' ), 'Dummy', DummyPlugin::class );
380
		$this->assertInstanceOf( __NAMESPACE__ . '\\TestFromFile4b', $ret );
381
382
		// Get by loading file, file with two valid class.
383
		file_put_contents(
384
			'dummy5.php',
385
			"<?php\nnamespace $ns;\nclass TestFromFile5a $classBody\nclass TestFromFile5b $classBody\n"
386
		);
387
		$this->assertNull( $w->getPlugin( array( 'filename' => 'dummy5.php' ), 'Dummy', DummyPlugin::class ) );
388
389
		// Test invalid class handling.
390
		$this->assertNull( $w->getPlugin( 'baz', 'Dummy', DummyPlugin::class ) );
391
392
		// Test a config array with no valid plugin specifier.
393
		$this->assertNull( $w->getPlugin( array(), 'Dummy', DummyPlugin::class ) );
394
	}
395
396
	/**
397
	 * Test the formatterPlugin method.
398
	 */
399
	public function testFormatterPlugin() {
400
		$this->resetConfigCache();
401
402
		$this->assertInstanceOf(
403
			\Automattic\Jetpack\Changelogger\Plugins\KeepachangelogFormatter::class,
404
			Config::formatterPlugin()
405
		);
406
	}
407
408
	/**
409
	 * Test the formatterPlugin method error case.
410
	 */
411 View Code Duplication
	public function testFormatterPlugin_error() {
412
		$this->resetConfigCache();
413
		$this->writeComposerJson( array( 'formatter' => array( 'class' => 'foobar' ) ) );
414
415
		$this->expectException( \RuntimeException::class );
416
		$this->expectExceptionMessage( "Unknown formatter plugin {\n    \"class\": \"foobar\"\n}" );
417
		Config::formatterPlugin();
418
	}
419
420
	/**
421
	 * Test the versioningPlugin method.
422
	 */
423
	public function testVersioningPlugin() {
424
		$this->resetConfigCache();
425
426
		$this->assertInstanceOf(
427
			\Automattic\Jetpack\Changelogger\Plugins\SemverVersioning::class,
428
			Config::versioningPlugin()
429
		);
430
	}
431
432
	/**
433
	 * Test the versioningPlugin method error case.
434
	 */
435 View Code Duplication
	public function testVersioningPlugin_error() {
436
		$this->resetConfigCache();
437
		$this->writeComposerJson( array( 'versioning' => array( 'class' => 'foobar' ) ) );
438
439
		$this->expectException( \RuntimeException::class );
440
		$this->expectExceptionMessage( "Unknown versioning plugin {\n    \"class\": \"foobar\"\n}" );
441
		Config::versioningPlugin();
442
	}
443
444
}
445