Completed
Push — add/changelog-tooling ( 86359e...64ad4d )
by
unknown
40:44 queued 31:04
created

ConfigTest::testLoad()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 1
nop 3
dl 0
loc 18
rs 9.6666
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\PluginTrait;
14
use Automattic\Jetpack\Changelogger\VersioningPlugin;
15
use PHPUnit\Framework\MockObject\MockObject;
16
use Symfony\Component\Console\Output\BufferedOutput;
17
use Wikimedia\TestingAccessWrapper;
18
19
/**
20
 * Tests for the changelogger config.
21
 *
22
 * @covers \Automattic\Jetpack\Changelogger\Config
23
 */
24
class ConfigTest extends TestCase {
25
	use \Yoast\PHPUnitPolyfills\Polyfills\ExpectException;
26
27
	/**
28
	 * Set up.
29
	 *
30
	 * @before
31
	 */
32
	public function set_up() {
33
		$this->useTempDir();
34
35
		file_put_contents( 'bogus.json', "bogus\n" );
36
		$this->writeComposerJson(
37
			array(
38
				'types'  => (object) array(),
39
				'foobar' => 'baz',
40
			),
41
			'no-types.json'
42
		);
43
	}
44
45
	/**
46
	 * Write a composer.json file.
47
	 *
48
	 * @param array  $config Contents for `.extra.changelogger`.
49
	 * @param string $file Filename.
50
	 */
51
	public function writeComposerJson( array $config, $file = 'composer.json' ) {
52
		file_put_contents(
53
			$file,
54
			json_encode(
55
				array( 'extra' => array( 'changelogger' => $config ) ),
56
				JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE
57
			)
58
		);
59
	}
60
61
	/**
62
	 * Test that calling load() before setOutput() throws.
63
	 */
64
	public function testLoadBeforeSetOutput() {
65
		$this->resetConfigCache();
66
67
		$this->expectException( \LogicException::class );
68
		$this->expectExceptionMessage( 'Must call Config::setOutput() before Config::load()' );
69
		TestingAccessWrapper::newFromClass( Config::class )->load();
70
	}
71
72
	/**
73
	 * Test parsing composer.json.
74
	 *
75
	 * @dataProvider provideLoad
76
	 * @param string|false $composer Value for COMPOSER environment variable.
77
	 * @param string       $expectOut Expected console output.
78
	 * @param array        $expectConfig Expected configuration data.
79
	 */
80
	public function testLoad( $composer, $expectOut, $expectConfig ) {
81
		$expectConfig['base'] = getcwd();
82
83
		$this->resetConfigCache();
84
		putenv( false === $composer ? 'COMPOSER' : "COMPOSER=$composer" );
85
		$out = new BufferedOutput();
86
		Config::setOutput( $out );
87
		$w = TestingAccessWrapper::newFromClass( Config::class );
88
		$w->load();
89
		$this->assertSame( $expectOut, $out->fetch() );
90
		$this->assertEquals( $expectConfig, $w->config );
91
92
		// Second load call should do nothing.
93
		putenv( 'COMPOSER=./doesnotexist.json' );
94
		$w->load();
95
		$this->assertSame( '', $out->fetch() );
96
		$this->assertEquals( $expectConfig, $w->config );
97
	}
98
99
	/**
100
	 * Data provider for testLoad.
101
	 */
102
	public function provideLoad() {
103
		$defaultConfig = TestingAccessWrapper::newFromClass( Config::class )->defaultConfig;
104
105
		return array(
106
			'default'                 => array(
107
				false,
108
				'',
109
				$defaultConfig,
110
			),
111
			'Alternate composer.json' => array(
112
				'no-types.json',
113
				'',
114
				array(
115
					'types'  => array(),
116
					'foobar' => 'baz',
117
				) + $defaultConfig,
118
			),
119
			'missing composer.json'   => array(
120
				'missing.json',
121
				"File missing.json (as specified by the COMPOSER environment variable) is not found.\n",
122
				$defaultConfig,
123
			),
124
			'broken composer.json'    => array(
125
				'bogus.json',
126
				"File bogus.json (as specified by the COMPOSER environment variable) could not be parsed.\n",
127
				$defaultConfig,
128
			),
129
		);
130
	}
131
132
	/**
133
	 * Test the base method.
134
	 */
135
	public function testBase() {
136
		$this->resetConfigCache();
137
		$out = new BufferedOutput();
138
		Config::setOutput( $out );
139
		$this->assertSame( getcwd(), Config::base() );
140
141
		$this->resetConfigCache();
142
		putenv( 'COMPOSER=' . __DIR__ . '/../../../../composer.json' );
143
		Config::setOutput( $out );
144
		$this->assertSame( dirname( dirname( dirname( dirname( __DIR__ ) ) ) ), Config::base() );
145
	}
146
147
	/**
148
	 * Test the changelogFile method.
149
	 */
150 View Code Duplication
	public function testChangelogFile() {
151
		$this->resetConfigCache();
152
		$out = new BufferedOutput();
153
		Config::setOutput( $out );
154
		$this->assertSame( getcwd() . DIRECTORY_SEPARATOR . 'CHANGELOG.md', Config::changelogFile() );
155
156
		$this->resetConfigCache();
157
		$this->writeComposerJson( array( 'changelog' => 'changes.txt' ) );
158
		Config::setOutput( $out );
159
		$this->assertSame( getcwd() . DIRECTORY_SEPARATOR . 'changes.txt', Config::changelogFile() );
160
161
		$this->resetConfigCache();
162
		$this->writeComposerJson( array( 'changelog' => '/tmp/changes.md' ) );
163
		Config::setOutput( $out );
164
		$this->assertSame( '/tmp/changes.md', Config::changelogFile() );
165
166
		$this->resetConfigCache();
167
		$this->writeComposerJson( array( 'changelog' => 'c:\\changes.md' ) );
168
		Config::setOutput( $out );
169
		$this->assertSame( 'c:\\changes.md', Config::changelogFile() );
170
	}
171
172
	/**
173
	 * Test the changesDir method.
174
	 */
175 View Code Duplication
	public function testChangesDir() {
176
		$this->resetConfigCache();
177
		$out = new BufferedOutput();
178
		Config::setOutput( $out );
179
		$this->assertSame( getcwd() . DIRECTORY_SEPARATOR . 'changelog', Config::changesDir() );
180
181
		$this->resetConfigCache();
182
		$this->writeComposerJson( array( 'changes-dir' => 'changes' ) );
183
		Config::setOutput( $out );
184
		$this->assertSame( getcwd() . DIRECTORY_SEPARATOR . 'changes', Config::changesDir() );
185
186
		$this->resetConfigCache();
187
		$this->writeComposerJson( array( 'changes-dir' => '/tmp/changes' ) );
188
		Config::setOutput( $out );
189
		$this->assertSame( '/tmp/changes', Config::changesDir() );
190
191
		$this->resetConfigCache();
192
		$this->writeComposerJson( array( 'changes-dir' => 'c:\\changes' ) );
193
		Config::setOutput( $out );
194
		$this->assertSame( 'c:\\changes', Config::changesDir() );
195
	}
196
197
	/**
198
	 * Test the types method.
199
	 */
200
	public function testTypes() {
201
		$this->resetConfigCache();
202
		$out = new BufferedOutput();
203
		Config::setOutput( $out );
204
		$w = TestingAccessWrapper::newFromClass( Config::class );
205
206
		$this->assertSame( $w->defaultConfig['types'], Config::types() );
207
208
		$w->config = array(
209
			'types' => array(
210
				'FOO' => 'Stuff',
211
				'bAr' => 'More stuff',
212
			),
213
		);
214
215
		// No change because of caching.
216
		$this->assertSame( $w->defaultConfig['types'], Config::types() );
217
218
		// Clear cache, now it changes.
219
		$w->cache = array();
220
		$this->assertSame(
221
			array(
222
				'foo' => 'Stuff',
223
				'bar' => 'More stuff',
224
			),
225
			Config::types()
226
		);
227
	}
228
229
	/**
230
	 * Test the getPlugin method.
231
	 */
232
	public function testGetPlugin() {
233
		$w = TestingAccessWrapper::newFromClass( Config::class );
234
235
		// Get plugin by class.
236
		$mock1  = $this->getMockBuilder( PluginTrait::class )->getMockForTrait();
237
		$class1 = get_class( $mock1 );
238
		$this->assertInstanceOf( $class1, $w->getPlugin( array( 'class' => $class1 ), 'Dummy', MockObject::class ) );
239
240
		// Get plugin by name.
241
		$mock2  = $this->getMockBuilder( PluginTrait::class )->getMockForTrait();
242
		$class2 = get_class( $mock2 );
243
		class_alias( $class2, \Automattic\Jetpack\Changelogger\Plugins\FooDummy::class );
244
		$this->assertInstanceOf( $class2, $w->getPlugin( 'foo', 'Dummy', MockObject::class ) );
245
		$this->assertInstanceOf( $class2, $w->getPlugin( array( 'name' => 'foo' ), 'Dummy', MockObject::class ) );
246
247
		// Get by loading file, valid file.
248
		$ns        = __NAMESPACE__;
249
		$classBody = 'implements \\' . VersioningPlugin::class . " {\n\tuse \\" . PluginTrait::class . ";\n\tpublic function __construct( \$c ) { \$this->c = \$c; }\n\tpublic function nextVersion( \$version, array \$changes, array \$extra = array() ){}\n\tpublic function normalizeVersion( \$v ){}\n\tpublic function compareVersions( \$a, \$b ){}\n}";
250
		file_put_contents(
251
			'dummy.php',
252
			"<?php\nnamespace $ns;\nclass TestFromFile $classBody\n"
253
		);
254
		$ret = $w->getPlugin(
255
			array(
256
				'filename' => 'dummy.php',
257
				'option'   => 'value',
258
			),
259
			'Dummy',
260
			VersioningPlugin::class
261
		);
262
		$this->assertInstanceOf( __NAMESPACE__ . '\\TestFromFile', $ret );
263
		$this->assertSame(
264
			array(
265
				'filename' => 'dummy.php',
266
				'option'   => 'value',
267
			),
268
			$ret->c
269
		);
270
271
		// Get by loading file, file with no classes.
272
		file_put_contents( 'dummy2.php', '<?php' );
273
		$this->assertNull( $w->getPlugin( array( 'filename' => 'dummy2.php' ), 'Dummy', VersioningPlugin::class ) );
274
275
		// Get by loading file, file with no valid classes.
276
		file_put_contents(
277
			'dummy3.php',
278
			"<?php\nnamespace $ns;\nclass TestFromFile3 {}\n"
279
		);
280
		$this->assertNull( $w->getPlugin( array( 'filename' => 'dummy3.php' ), 'Dummy', VersioningPlugin::class ) );
281
282
		// Get by loading file, file with one valid class.
283
		file_put_contents(
284
			'dummy4.php',
285
			"<?php\nnamespace $ns;\nclass TestFromFile4a {}\nclass TestFromFile4b $classBody\n"
286
		);
287
		$ret = $w->getPlugin( array( 'filename' => 'dummy4.php' ), 'Dummy', VersioningPlugin::class );
288
		$this->assertInstanceOf( __NAMESPACE__ . '\\TestFromFile4b', $ret );
289
290
		// Get by loading file, file with two valid class.
291
		file_put_contents(
292
			'dummy5.php',
293
			"<?php\nnamespace $ns;\nclass TestFromFile5a $classBody\nclass TestFromFile5b $classBody\n"
294
		);
295
		$this->assertNull( $w->getPlugin( array( 'filename' => 'dummy5.php' ), 'Dummy', VersioningPlugin::class ) );
296
297
		// Test invalid class handling.
298
		$this->assertNull( $w->getPlugin( 'baz', 'Dummy', MockObject::class ) );
299
300
		// Test a config array with no valid plugin specifier.
301
		$this->assertNull( $w->getPlugin( array(), 'Dummy', MockObject::class ) );
302
	}
303
304
	/**
305
	 * Test the formatterPlugin method.
306
	 */
307
	public function testFormatterPlugin() {
308
		$this->resetConfigCache();
309
		$out = new BufferedOutput();
310
		Config::setOutput( $out );
311
312
		$this->assertInstanceOf(
313
			\Automattic\Jetpack\Changelogger\Plugins\KeepachangelogFormatter::class,
314
			Config::formatterPlugin()
315
		);
316
	}
317
318
	/**
319
	 * Test the formatterPlugin method error case.
320
	 */
321 View Code Duplication
	public function testFormatterPlugin_error() {
322
		$this->resetConfigCache();
323
		$this->writeComposerJson( array( 'formatter' => array( 'class' => 'foobar' ) ) );
324
		$out = new BufferedOutput();
325
		Config::setOutput( $out );
326
327
		$this->expectException( \RuntimeException::class );
328
		$this->expectExceptionMessage( "Unknown formatter plugin {\n    \"class\": \"foobar\"\n}" );
329
		Config::formatterPlugin();
330
	}
331
332
	/**
333
	 * Test the versioningPlugin method.
334
	 */
335
	public function testVersioningPlugin() {
336
		$this->resetConfigCache();
337
		$out = new BufferedOutput();
338
		Config::setOutput( $out );
339
340
		$this->assertInstanceOf(
341
			\Automattic\Jetpack\Changelogger\Plugins\SemverVersioning::class,
342
			Config::versioningPlugin()
343
		);
344
	}
345
346
	/**
347
	 * Test the versioningPlugin method error case.
348
	 */
349 View Code Duplication
	public function testVersioningPlugin_error() {
350
		$this->resetConfigCache();
351
		$this->writeComposerJson( array( 'versioning' => array( 'class' => 'foobar' ) ) );
352
		$out = new BufferedOutput();
353
		Config::setOutput( $out );
354
355
		$this->expectException( \RuntimeException::class );
356
		$this->expectExceptionMessage( "Unknown versioning plugin {\n    \"class\": \"foobar\"\n}" );
357
		Config::versioningPlugin();
358
	}
359
360
}
361