Completed
Push — add/changelog-tooling ( 7f5585...86359e )
by
unknown
58:45 queued 48:46
created

AddCommandTest::provideExecute()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 318

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 318
rs 8
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2
/**
3
 * Tests for the changelogger add command.
4
 *
5
 * @package automattic/jetpack-changelogger
6
 */
7
8
// phpcs:disable WordPress.WP.AlternativeFunctions, WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
9
10
namespace Automattic\Jetpack\Changelogger\Tests;
11
12
use Automattic\Jetpack\Changelogger\Utils;
13
use Symfony\Component\Console\Helper\DebugFormatterHelper;
14
use Symfony\Component\Console\Output\ConsoleOutput;
15
use Symfony\Component\Console\Output\NullOutput;
16
use Wikimedia\TestingAccessWrapper;
17
18
/**
19
 * Tests for the changelogger add command.
20
 *
21
 * @covers \Automattic\Jetpack\Changelogger\AddCommand
22
 */
23
class AddCommandTest extends CommandTestCase {
24
	use \Yoast\PHPUnitPolyfills\Polyfills\AssertionRenames;
25
26
	/**
27
	 * Set up.
28
	 *
29
	 * @before
30
	 */
31
	public function set_up() {
32
		parent::set_up();
33
		$this->useTempDir();
34
	}
35
36
	/**
37
	 * Test getDefaultFilename().
38
	 */
39
	public function testGetDefaultFilename() {
40
		if ( in_array( '--debug', $GLOBALS['argv'], true ) ) {
41
			$output = new ConsoleOutput();
42
			$output->setVerbosity( ConsoleOutput::VERBOSITY_DEBUG );
43
		} else {
44
			$output = new NullOutput();
45
		}
46
		$w = TestingAccessWrapper::newFromObject( $this->getCommand( 'add' ) );
47
48
		// Test with no git checkout.
49
		$this->assertMatchesRegularExpression( '/^\d{4}-\d{2}-\d{2}-\d{2}-\d{2}-\d{2}-\d{6}$/', $w->getDefaultFilename( $output ) );
50
51
		// Create a git checkout, master branch.
52
		$args = array(
53
			$output,
54
			new DebugFormatterHelper(),
55
			array(
56
				'mustRun' => true,
57
				'env'     => array(
58
					'GIT_AUTHOR_NAME'     => 'Dummy',
59
					'GIT_AUTHOR_EMAIL'    => '[email protected]',
60
					'GIT_COMMITTER_NAME'  => 'Dummy',
61
					'GIT_COMMITTER_EMAIL' => '[email protected]',
62
				),
63
			),
64
		);
65
		Utils::runCommand( array( 'git', 'init', '.' ), ...$args );
0 ignored issues
show
Bug introduced by
The call to runCommand() misses a required argument $formatter.

This check looks for function calls that miss required arguments.

Loading history...
Documentation introduced by
$args is of type array<integer,object<Sym...\\\"string\\\"}>\"}>"}>, but the function expects a object<Symfony\Component...Output\OutputInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
66
		Utils::runCommand( array( 'git', 'checkout', '-b', 'master' ), ...$args );
0 ignored issues
show
Documentation introduced by
$args is of type array<integer,object<Sym...\\\"string\\\"}>\"}>"}>, but the function expects a object<Symfony\Component...Output\OutputInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Bug introduced by
The call to runCommand() misses a required argument $formatter.

This check looks for function calls that miss required arguments.

Loading history...
67
		Utils::runCommand( array( 'git', 'commit', '--allow-empty', '-m', 'Empty' ), ...$args );
0 ignored issues
show
Documentation introduced by
$args is of type array<integer,object<Sym...\\\"string\\\"}>\"}>"}>, but the function expects a object<Symfony\Component...Output\OutputInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Bug introduced by
The call to runCommand() misses a required argument $formatter.

This check looks for function calls that miss required arguments.

Loading history...
68
		$this->assertMatchesRegularExpression( '/^\d{4}-\d{2}-\d{2}-\d{2}-\d{2}-\d{2}-\d{6}$/', $w->getDefaultFilename( $output ) );
69
70
		// Try a named branch.
71
		Utils::runCommand( array( 'git', 'checkout', '-b', 'test/default-filename' ), ...$args );
0 ignored issues
show
Documentation introduced by
$args is of type array<integer,object<Sym...\\\"string\\\"}>\"}>"}>, but the function expects a object<Symfony\Component...Output\OutputInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Bug introduced by
The call to runCommand() misses a required argument $formatter.

This check looks for function calls that miss required arguments.

Loading history...
72
		$this->assertSame( 'test-default-filename', $w->getDefaultFilename( $output ) );
73
	}
74
75
	/**
76
	 * Test mkdir failure in execute().
77
	 */
78
	public function testExecute_mkdirFail() {
79
		file_put_contents( 'changelog', '' );
80
		$tester = $this->getTester( 'add' );
81
		$code   = $tester->execute( array() );
82
		$this->assertSame( 1, $code );
83
		$this->assertMatchesRegularExpression( '{^Could not create directory /.*/phpunit-changelogger-[0-9a-f]{6}/changelog: mkdir\(\): File exists\n$}', $tester->getDisplay() );
84
	}
85
86
	/**
87
	 * Test failure in execute() when file exists.
88
	 */
89
	public function testExecute_fileExists() {
90
		mkdir( 'changelog' );
91
		file_put_contents( 'changelog/testing', '' );
92
		$tester = $this->getTester( 'add' );
93
		$code   = $tester->execute( array( '--filename' => 'testing' ), array( 'interactive' => false ) );
94
		$this->assertSame( 1, $code );
95
		$this->assertMatchesRegularExpression( '{^File "/.*/phpunit-changelogger-[0-9a-f]{6}/changelog/testing" already exists. If you want to replace it, delete it manually.\n$}', $tester->getDisplay() );
96
	}
97
98
	/**
99
	 * Test the command.
100
	 *
101
	 * @dataProvider provideExecute
102
	 * @param string[]    $args Command line arguments.
103
	 * @param array       $options Options for the test and CommandTester.
104
	 * @param string[]    $inputs User inputs.
105
	 * @param int         $expectExitCode Expected exit code.
106
	 * @param string|null $expectFile Expected change file contents, or null if no file should exist.
107
	 * @param string[]    $expectOutputRegexes Regexes to run against the output.
108
	 */
109
	public function testExecute( array $args, array $options, array $inputs, $expectExitCode, $expectFile, $expectOutputRegexes = array() ) {
110 View Code Duplication
		if ( isset( $options['composer.json'] ) ) {
111
			file_put_contents( 'composer.json', json_encode( $options['composer.json'], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) );
112
			unset( $options['composer.json'] );
113
		}
114
115
		$tester = $this->getTester( 'add' );
116
		$tester->setInputs( $inputs );
117
		$code = $tester->execute( $args, $options );
118
		foreach ( $expectOutputRegexes as $re ) {
119
			$this->assertMatchesRegularExpression( $re, $tester->getDisplay() );
120
		}
121
		$this->assertSame( $expectExitCode, $code );
122
		$this->assertDirectoryExists( './changelog' );
123
		$files = glob( './changelog/*' );
124
		if ( null === $expectFile ) {
125
			$this->assertCount( 0, $files, 'No change file is expected' );
126
		} else {
127
			$this->assertCount( 1, $files, 'A change file is expected' );
128
			$this->assertSame( $expectFile, file_get_contents( $files[0] ) );
129
		}
130
	}
131
132
	/**
133
	 * Data provider for testExecute.
134
	 */
135
	public function provideExecute() {
136
		$composerWithTypes   = array(
137
			'extra' => array(
138
				'changelogger' => array(
139
					'types' => array(
140
						'foo' => 'Foo',
141
						'bar' => 'Bar',
142
					),
143
				),
144
			),
145
		);
146
		$composerWithNoTypes = array(
147
			'extra' => array(
148
				'changelogger' => array(
149
					'types' => (object) array(),
150
				),
151
			),
152
		);
153
154
		return array(
155
			'Normal interactive use'                       => array(
156
				array(),
157
				array(),
158
				array( '', 'patch', 'fixed', '', 'Testing.' ),
159
				0,
160
				"Significance: patch\nType: fixed\n\nTesting.\n",
161
			),
162
			'Normal interactive use with comment'          => array(
163
				array(),
164
				array(),
165
				array( '', 'patch', 'fixed', 'This is a comment', 'Testing.' ),
166
				0,
167
				"Significance: patch\nType: fixed\nComment: This is a comment\n\nTesting.\n",
168
			),
169
			'Normal interactive use with empty entry'      => array(
170
				array(),
171
				array(),
172
				array( '', 'patch', 'fixed', '', '' ),
173
				0,
174
				"Significance: patch\nType: fixed\n\n\n",
175
			),
176
			'Interactive use with command line defaults'   => array(
177
				array(
178
					'--significance' => 'patch',
179
					'--type'         => 'fixed',
180
					'--entry'        => 'Testing.',
181
				),
182
				array(),
183
				array( '', '', '', '', '' ),
184
				0,
185
				"Significance: patch\nType: fixed\n\nTesting.\n",
186
			),
187
			'Interactive use that runs into some errors'   => array(
188
				array(),
189
				array(),
190
				array(
191
					'<bad filename>',
192
					'bad:filename?',
193
					'bad/|\\filename',
194
					'.bad-ilename',
195
					'',
196
					'extreme',
197
					'minor',
198
					'improved',
199
					'fixed',
200
					'',
201
					'',
202
					'Testing.',
203
				),
204
				0,
205
				"Significance: minor\nType: fixed\n\nTesting.\n",
206
				array(
207
					'/Filename may not contain angle brackets/',
208
					'/Filename may not contain colons or question marks/',
209
					'/Filename may not contain slashes, backslashes, or pipes/',
210
					'/Filename may not begin with a dot/',
211
					'/Value "extreme" is invalid/',
212
					'/Value "improved" is invalid/',
213
					'/An empty changelog entry is only allowed when the significance is "patch"/',
214
				),
215
			),
216
			'Interactive use with custom types'            => array(
217
				array(),
218
				array( 'composer.json' => $composerWithTypes ),
219
				array(
220
					'',
221
					'minor',
222
					'fixed',
223
					'foo',
224
					'',
225
					'Testing.',
226
				),
227
				0,
228
				"Significance: minor\nType: foo\n\nTesting.\n",
229
				array(
230
					'/Value "fixed" is invalid/',
231
				),
232
			),
233
			'Interactive use with no types'                => array(
234
				array(),
235
				array( 'composer.json' => $composerWithNoTypes ),
236
				array(
237
					'',
238
					'minor',
239
					'',
240
					'Testing.',
241
				),
242
				0,
243
				"Significance: minor\n\nTesting.\n",
244
				array(),
245
			),
246
247
			'Normal non-interactive use'                   => array(
248
				array(
249
					'--significance' => 'patch',
250
					'--type'         => 'fixed',
251
					'--entry'        => 'Testing.',
252
				),
253
				array( 'interactive' => false ),
254
				array(),
255
				0,
256
				"Significance: patch\nType: fixed\n\nTesting.\n",
257
			),
258
			'Normal non-interactive use with comment'      => array(
259
				array(
260
					'--significance' => 'patch',
261
					'--type'         => 'fixed',
262
					'--comment'      => 'This is a comment',
263
					'--entry'        => 'Testing.',
264
				),
265
				array( 'interactive' => false ),
266
				array(),
267
				0,
268
				"Significance: patch\nType: fixed\nComment: This is a comment\n\nTesting.\n",
269
			),
270
			'Normal non-interactive use with empty entry'  => array(
271
				array(
272
					'--significance' => 'patch',
273
					'--type'         => 'fixed',
274
					'--entry'        => '',
275
				),
276
				array( 'interactive' => false ),
277
				array(),
278
				0,
279
				"Significance: patch\nType: fixed\n\n\n",
280
			),
281
			'Non-interactive use with empty filename'      => array(
282
				array(
283
					'--filename'     => '',
284
					'--significance' => 'patch',
285
					'--type'         => 'fixed',
286
					'--entry'        => '',
287
				),
288
				array( 'interactive' => false ),
289
				array(),
290
				1,
291
				null,
292
				array( '/Filename may not be empty/' ),
293
			),
294
			'Non-interactive use with dot filename'        => array(
295
				array(
296
					'--filename'     => '.bad',
297
					'--significance' => 'patch',
298
					'--type'         => 'fixed',
299
					'--entry'        => '',
300
				),
301
				array( 'interactive' => false ),
302
				array(),
303
				1,
304
				null,
305
				array( '/Filename may not begin with a dot/' ),
306
			),
307
			'Non-interactive use with missing significance' => array(
308
				array(
309
					'--type'  => 'fixed',
310
					'--entry' => 'Testing.',
311
				),
312
				array( 'interactive' => false ),
313
				array(),
314
				1,
315
				null,
316
				array(
317
					'/Significance must be specified in non-interactive mode/',
318
				),
319
			),
320
			'Non-interactive use with invalid significance' => array(
321
				array(
322
					'--significance' => 'bogus',
323
					'--type'         => 'fixed',
324
					'--entry'        => 'Testing.',
325
				),
326
				array( 'interactive' => false ),
327
				array(),
328
				1,
329
				null,
330
				array(
331
					'/Significance value "bogus" is not valid/',
332
				),
333
			),
334
			'Non-interactive use with missing type'        => array(
335
				array(
336
					'--significance' => 'patch',
337
					'--entry'        => 'Testing.',
338
				),
339
				array( 'interactive' => false ),
340
				array(),
341
				1,
342
				null,
343
				array(
344
					'/Type must be specified in non-interactive mode/',
345
				),
346
			),
347
			'Non-interactive use with invalid type'        => array(
348
				array(
349
					'--significance' => 'patch',
350
					'--type'         => 'bogus',
351
					'--entry'        => 'Testing.',
352
				),
353
				array( 'interactive' => false ),
354
				array(),
355
				1,
356
				null,
357
				array(
358
					'/Type "bogus" is not valid/',
359
				),
360
			),
361
			'Non-interactive use with missing entry'       => array(
362
				array(
363
					'--significance' => 'patch',
364
					'--type'         => 'fixed',
365
				),
366
				array( 'interactive' => false ),
367
				array(),
368
				1,
369
				null,
370
				array(
371
					'/Entry must be specified in non-interactive mode/',
372
				),
373
			),
374
			'Non-interactive use with invalid entry'       => array(
375
				array(
376
					'--significance' => 'minor',
377
					'--type'         => 'fixed',
378
					'--entry'        => '',
379
				),
380
				array( 'interactive' => false ),
381
				array(),
382
				1,
383
				null,
384
				array(
385
					'/An empty changelog entry is only allowed when the significance is "patch"/',
386
				),
387
			),
388
			'Non-interactive use with custom type'         => array(
389
				array(
390
					'--significance' => 'patch',
391
					'--type'         => 'foo',
392
					'--entry'        => 'Testing.',
393
				),
394
				array(
395
					'interactive'   => false,
396
					'composer.json' => $composerWithTypes,
397
				),
398
				array(),
399
				0,
400
				"Significance: patch\nType: foo\n\nTesting.\n",
401
				array(),
402
			),
403
			'Non-interactive use with invalid custom type' => array(
404
				array(
405
					'--significance' => 'patch',
406
					'--type'         => 'fixed',
407
					'--entry'        => 'Testing.',
408
				),
409
				array(
410
					'interactive'   => false,
411
					'composer.json' => $composerWithTypes,
412
				),
413
				array(),
414
				1,
415
				null,
416
				array(
417
					'/Type "fixed" is not valid/',
418
				),
419
			),
420
			'Non-interactive use with no type'             => array(
421
				array(
422
					'--significance' => 'patch',
423
					'--entry'        => 'Testing.',
424
				),
425
				array(
426
					'interactive'   => false,
427
					'composer.json' => $composerWithNoTypes,
428
				),
429
				array(),
430
				0,
431
				"Significance: patch\n\nTesting.\n",
432
				array(),
433
			),
434
			'Non-interactive use with no type (2)'         => array(
435
				array(
436
					'--significance' => 'patch',
437
					'--type'         => 'anything',
438
					'--entry'        => 'Testing.',
439
				),
440
				array(
441
					'interactive'   => false,
442
					'composer.json' => $composerWithNoTypes,
443
				),
444
				array(),
445
				0,
446
				"Significance: patch\n\nTesting.\n",
447
				array(
448
					'/This project does not use types. Do not specify --type./',
449
				),
450
			),
451
		);
452
	}
453
454
}
455