Completed
Push — add/changelog-tooling ( e205c1...20e03c )
by
unknown
10:18
created

WriteCommandTest::testDeleteChanges_error()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
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 write 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\Changelog\Changelog;
13
use Automattic\Jetpack\Changelogger\FormatterPlugin;
14
use Automattic\Jetpack\Changelogger\WriteCommand;
15
use InvalidArgumentException;
16
use Symfony\Component\Console\Input\ArrayInput;
17
use Symfony\Component\Console\Output\BufferedOutput;
18
use Symfony\Component\Console\Output\OutputInterface;
19
use Symfony\Component\Console\Tester\CommandTester;
20
use Wikimedia\TestingAccessWrapper;
21
22
/**
23
 * Tests for the changelogger write command.
24
 *
25
 * @covers \Automattic\Jetpack\Changelogger\WriteCommand
26
 */
27
class WriteCommandTest extends CommandTestCase {
28
	use \Yoast\PHPUnitPolyfills\Polyfills\AssertionRenames;
29
	use \Yoast\PHPUnitPolyfills\Polyfills\AssertStringContains;
30
31
	/**
32
	 * Set up.
33
	 *
34
	 * @before
35
	 */
36
	public function set_up() {
37
		parent::set_up();
38
		$this->useTempDir();
39
		mkdir( 'changelog' );
40
		file_put_contents( 'changelog/.gitkeep', '' );
41
	}
42
43
	/**
44
	 * Test the command.
45
	 *
46
	 * @dataProvider provideExecute
47
	 * @param string[]    $args Command line arguments.
48
	 * @param array       $options Options for the test and CommandTester.
49
	 * @param string[]    $inputs User inputs.
50
	 * @param int         $expectExitCode Expected exit code.
51
	 * @param string[]    $expectOutputRegexes Regexes to run against the output.
52
	 * @param bool|array  $expectChangesDeleted Whether change files should have been deleted, or an array of files that should have been deleted.
53
	 * @param string|null $expectChangelog Expected changelog file contents, or null if it should be the same as $options['changelog'].
54
	 */
55
	public function testExecute( array $args, array $options, array $inputs, $expectExitCode, $expectOutputRegexes = array(), $expectChangesDeleted = false, $expectChangelog = null ) {
56
		$options += array(
57
			'changelog' => "# Changelog\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
58
			'changes'   => array(
59
				'a-change' => "Significance: patch\nType: fixed\n\nFixed a thing.\n",
60
			),
61
		);
62
63 View Code Duplication
		if ( isset( $options['composer.json'] ) ) {
64
			file_put_contents( 'composer.json', json_encode( array( 'extra' => array( 'changelogger' => $options['composer.json'] ) ), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) );
65
			unset( $options['composer.json'] );
66
		}
67
		$changelog = isset( $options['changelog'] ) ? $options['changelog'] : null;
68
		$changes   = isset( $options['changes'] ) ? $options['changes'] : array();
69
		unset( $options['changelog'], $options['changes'] );
70
		if ( null !== $changelog ) {
71
			file_put_contents( 'CHANGELOG.md', $changelog );
72
		}
73
		foreach ( $changes as $name => $change ) {
74
			file_put_contents( "changelog/$name", $change );
75
		}
76
77
		$tester = $this->getTester( 'write' );
78
		$tester->setInputs( $inputs );
79
		$code = $tester->execute( $args, $options );
80
		foreach ( $expectOutputRegexes as $re ) {
81
			$this->assertMatchesRegularExpression( $re, $tester->getDisplay() );
82
		}
83
		$this->assertSame( $expectExitCode, $code );
84
85
		$expectExisting = array( '.gitkeep' );
86
		$expectDeleted  = array();
87
		if ( false === $expectChangesDeleted ) {
88
			$expectExisting = array_merge( $expectExisting, array_keys( $changes ) );
89
		} elseif ( true === $expectChangesDeleted ) {
90
			$expectDeleted = array_merge( $expectDeleted, array_keys( $changes ) );
91
		} else {
92
			$expectExisting = array_merge( $expectExisting, array_diff( array_keys( $changes ), $expectChangesDeleted ) );
93
			$expectDeleted  = array_merge( $expectDeleted, array_intersect( array_keys( $changes ), $expectChangesDeleted ) );
94
		}
95
		foreach ( $expectDeleted as $name ) {
96
			$this->assertFileDoesNotExist( "changelog/$name" );
97
		}
98
		foreach ( $expectExisting as $name ) {
99
			$this->assertFileExists( "changelog/$name" );
100
		}
101
102
		if ( null === $expectChangelog ) {
103
			$expectChangelog = $changelog;
104
		}
105
		if ( null === $expectChangelog ) {
106
			$this->assertFileDoesNotExist( 'CHANGELOG.md' );
107
		} else {
108
			$this->assertFileExists( 'CHANGELOG.md' );
109
			$this->assertSame( $expectChangelog, file_get_contents( 'CHANGELOG.md' ) );
110
		}
111
	}
112
113
	/**
114
	 * Data provider for testExecute.
115
	 */
116
	public function provideExecute() {
117
		$date = gmdate( 'Y-m-d' );
118
119
		return array(
120
			'Normal run'                                   => array(
121
				array(),
122
				array(),
123
				array(),
124
				0,
125
				array( '/^$/' ),
126
				true,
127
				"# Changelog\n\n## 1.0.2 - $date\n### Fixed\n- Fixed a thing.\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
128
			),
129
			'Debug run'                                    => array(
130
				array(),
131
				array( 'verbosity' => OutputInterface::VERBOSITY_DEBUG ),
132
				array(),
133
				0,
134
				array(
135
					'{^Reading changelog from /.*/CHANGELOG.md\\.\\.\\.$}m',
136
					'{^Reading changes from /.*/changelog\\.\\.\\.$}m',
137
					'{^Deduplicating changes from the last 1 version\\(s\\)\\.\\.\\.$}m',
138
					'{^Checking if any changes have content\\.\\.\\.$}m',
139
					'{^Yes, a-change has content\\.$}m',
140
					'{^Latest version from changelog is 1\\.0\\.1\\.$}m',
141
					'{^Next version is 1\\.0\\.2\\.$}m',
142
					'{^Creating new changelog entry\\.$}m',
143
					'{^Writing changelog to /.*/CHANGELOG.md\\.\\.\\.$}m',
144
					'{^Deleted change file a-change\\.$}m',
145
				),
146
				true,
147
				"# Changelog\n\n## 1.0.2 - $date\n### Fixed\n- Fixed a thing.\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
148
			),
149
			'Run with extra command line args'             => array(
150
				array(
151
					'--prerelease' => 'dev',
152
					'--buildinfo'  => 'g1234567',
153
					'--prologue'   => 'Prologue for the new entry',
154
					'--epilogue'   => 'Epilogue for the new entry',
155
					'--link'       => 'https://example.org/link',
156
				),
157
				array(),
158
				array(),
159
				0,
160
				array( '/^$/' ),
161
				true,
162
				"# Changelog\n\n## [1.0.2-dev+g1234567] - $date\n\nPrologue for the new entry\n\n### Fixed\n- Fixed a thing.\n\nEpilogue for the new entry\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n\n[1.0.2-dev+g1234567]: https://example.org/link\n",
163
			),
164
165
			'Invalid formatter'                            => array(
166
				array(),
167
				array( 'composer.json' => array( 'formatter' => 'bogus' ) ),
168
				array(),
169
				WriteCommand::FATAL_EXIT,
170
				array( '/^Unknown formatter plugin "bogus"$/' ),
171
			),
172
			'Invalid versioning'                           => array(
173
				array(),
174
				array( 'composer.json' => array( 'versioning' => 'bogus' ) ),
175
				array(),
176
				WriteCommand::FATAL_EXIT,
177
				array( '/^Unknown versioning plugin "bogus"$/' ),
178
			),
179
180
			'No changelog file, interactive'               => array(
181
				array(),
182
				array( 'changelog' => null ),
183
				array( 'N' ),
184
				WriteCommand::ASKED_EXIT,
185
				array( '{^Changelog file /.*/CHANGELOG\.md does not exist! Proceed\? \[y/N\]}m' ),
186
			),
187
			'No changelog file, interactive (2)'           => array(
188
				array(),
189
				array( 'changelog' => null ),
190
				array( 'Y' ),
191
				WriteCommand::FATAL_EXIT,
192
				array( '/Changelog file contains no entries! Use --use-version to specify the initial version\.$/m' ),
193
			),
194
			'No changelog file, interactive (3)'           => array(
195
				array( '--use-version' => '1.0.0' ),
196
				array( 'changelog' => null ),
197
				array( 'Y' ),
198
				0,
199
				array(),
200
				true,
201
				"## 1.0.0 - $date\n### Fixed\n- Fixed a thing.\n",
202
			),
203
			'No changelog file, non-interactive'           => array(
204
				array(),
205
				array(
206
					'interactive' => false,
207
					'changelog'   => null,
208
				),
209
				array(),
210
				WriteCommand::ASKED_EXIT,
211
				array( '{^Changelog file /.*/CHANGELOG\.md does not exist!$}m' ),
212
			),
213
			'No changelog file, non-interactive, --yes'    => array(
214
				array( '--yes' => true ),
215
				array(
216
					'interactive' => false,
217
					'changelog'   => null,
218
				),
219
				array(),
220
				WriteCommand::FATAL_EXIT,
221
				array(
222
					'{^<warning>Changelog file /.*/CHANGELOG\.md does not exist! Continuing anyway\.$}m',
223
					'/Changelog file contains no entries! Use --use-version to specify the initial version\.$/m',
224
				),
225
			),
226
			'No changelog file, non-interactive, --yes (2)' => array(
227
				array(
228
					'--yes'         => true,
229
					'--use-version' => '1.0.0',
230
				),
231
				array(
232
					'interactive' => false,
233
					'changelog'   => null,
234
				),
235
				array(),
236
				0,
237
				array(
238
					'{^<warning>Changelog file /.*/CHANGELOG\.md does not exist! Continuing anyway\.$}m',
239
				),
240
				true,
241
				"## 1.0.0 - $date\n### Fixed\n- Fixed a thing.\n",
242
			),
243
244
			'Unparseable changelog'                        => array(
245
				array(),
246
				array( 'changelog' => "## bogus\n" ),
247
				array(),
248
				WriteCommand::FATAL_EXIT,
249
				array( '{^Failed to parse changelog: Invalid heading: ## bogus$}' ),
250
			),
251
252
			'No changes directory'                         => array(
253
				array(),
254
				array( 'composer.json' => array( 'changes-dir' => 'changes' ) ),
255
				array( 'N' ),
256
				WriteCommand::NO_CHANGE_EXIT,
257
				array( '{^Changes directory does not exist, so there are no changes to write! Proceed\? \[y/N\]}m' ),
258
			),
259
			'No changes directory (2)'                     => array(
260
				array(),
261
				array( 'composer.json' => array( 'changes-dir' => 'changes' ) ),
262
				array( 'Y' ),
263
				0,
264
				array( '{^Changes directory does not exist, so there are no changes to write! Proceed\? \[y/N\]}m' ),
265
				false,
266
				"# Changelog\n\n## 1.0.2 - $date\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
267
			),
268
			'No changes directory, non-interactive'        => array(
269
				array(),
270
				array(
271
					'composer.json' => array( 'changes-dir' => 'changes' ),
272
					'interactive'   => false,
273
				),
274
				array(),
275
				WriteCommand::NO_CHANGE_EXIT,
276
				array( '{^Changes directory does not exist, so there are no changes to write!$}m' ),
277
			),
278
			'No changes directory, non-interactive, --yes' => array(
279
				array( '--yes' => true ),
280
				array(
281
					'composer.json' => array( 'changes-dir' => 'changes' ),
282
					'interactive'   => false,
283
				),
284
				array(),
285
				0,
286
				array( '{^<warning>Changes directory does not exist, so there are no changes to write! Continuing anyway\.$}m' ),
287
				false,
288
				"# Changelog\n\n## 1.0.2 - $date\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
289
			),
290
291
			'Change files with warnings'                   => array(
292
				array(),
293
				array(
294
					'changes' => array(
295
						'a-change' => "Significance: patch\nType: fixed\n\nFixed a thing.\n",
296
						'b-change' => "Significance: patch\nType: fixed\nType: fixed\n\nDid a thing.\n",
297
					),
298
				),
299
				array( 'N' ),
300
				WriteCommand::ASKED_EXIT,
301
				array( '{^Warnings were encountered while reading changes! Proceed\? \[y/N\]}m' ),
302
			),
303
			'Change files with warnings (2)'               => array(
304
				array(),
305
				array(
306
					'changes' => array(
307
						'a-change' => "Significance: patch\nType: fixed\n\nFixed a thing.\n",
308
						'b-change' => "Significance: patch\nType: fixed\nType: fixed\n\nDid a thing.\n",
309
					),
310
				),
311
				array( 'Y' ),
312
				0,
313
				array( '{^Warnings were encountered while reading changes! Proceed\? \[y/N\]}m' ),
314
				true,
315
				"# Changelog\n\n## 1.0.2 - $date\n### Fixed\n- Did a thing.\n- Fixed a thing.\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
316
			),
317
			'Change files with warnings, non-interactive'  => array(
318
				array(),
319
				array(
320
					'interactive' => false,
321
					'changes'     => array(
322
						'a-change' => "Significance: patch\nType: fixed\n\nFixed a thing.\n",
323
						'b-change' => "Significance: patch\nType: fixed\nType: fixed\n\nDid a thing.\n",
324
					),
325
				),
326
				array(),
327
				WriteCommand::ASKED_EXIT,
328
				array( '{^Warnings were encountered while reading changes!$}m' ),
329
			),
330
			'Change files with warnings, non-interactive, --yes' => array(
331
				array( '--yes' => true ),
332
				array(
333
					'interactive' => false,
334
					'changes'     => array(
335
						'a-change' => "Significance: patch\nType: fixed\n\nFixed a thing.\n",
336
						'b-change' => "Significance: patch\nType: fixed\nType: fixed\n\nDid a thing.\n",
337
					),
338
				),
339
				array(),
340
				0,
341
				array( '{^<warning>Warnings were encountered while reading changes! Continuing anyway\.$}m' ),
342
				true,
343
				"# Changelog\n\n## 1.0.2 - $date\n### Fixed\n- Did a thing.\n- Fixed a thing.\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
344
			),
345
346
			'Change files with errors'                     => array(
347
				array(),
348
				array(
349
					'changes' => array(
350
						'a-change' => "Significance: patch\nType: fixed\n\nFixed a thing.\n",
351
						'b-change' => "Significance: patch\nType: fixed\nType: fixed\n\nDid a thing.\n",
352
						'error'    => "Bogus\n",
353
					),
354
				),
355
				array( 'N' ),
356
				WriteCommand::ASKED_EXIT,
357
				array( '{^Errors were encountered while reading changes! Proceed\? \[y/N\]}m' ),
358
			),
359
			'Change files with errors (2)'                 => array(
360
				array(),
361
				array(
362
					'changes' => array(
363
						'a-change' => "Significance: patch\nType: fixed\n\nFixed a thing.\n",
364
						'b-change' => "Significance: patch\nType: fixed\nType: fixed\n\nDid a thing.\n",
365
						'error'    => "Bogus\n",
366
					),
367
				),
368
				array( 'Y' ),
369
				0,
370
				array( '{^Errors were encountered while reading changes! Proceed\? \[y/N\]}m' ),
371
				array( 'a-change', 'b-change' ),
372
				"# Changelog\n\n## 1.0.2 - $date\n### Fixed\n- Did a thing.\n- Fixed a thing.\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
373
			),
374
			'Change files with errors, non-interactive'    => array(
375
				array(),
376
				array(
377
					'interactive' => false,
378
					'changes'     => array(
379
						'a-change' => "Significance: patch\nType: fixed\n\nFixed a thing.\n",
380
						'b-change' => "Significance: patch\nType: fixed\nType: fixed\n\nDid a thing.\n",
381
						'error'    => "Bogus\n",
382
					),
383
				),
384
				array(),
385
				WriteCommand::ASKED_EXIT,
386
				array( '{^Errors were encountered while reading changes!$}m' ),
387
			),
388
			'Change files with errors, non-interactive, --yes' => array(
389
				array( '--yes' => true ),
390
				array(
391
					'interactive' => false,
392
					'changes'     => array(
393
						'a-change' => "Significance: patch\nType: fixed\n\nFixed a thing.\n",
394
						'b-change' => "Significance: patch\nType: fixed\nType: fixed\n\nDid a thing.\n",
395
						'error'    => "Bogus\n",
396
					),
397
				),
398
				array(),
399
				0,
400
				array( '{^<warning>Errors were encountered while reading changes! Continuing anyway\.$}m' ),
401
				array( 'a-change', 'b-change' ),
402
				"# Changelog\n\n## 1.0.2 - $date\n### Fixed\n- Did a thing.\n- Fixed a thing.\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
403
			),
404
405
			'No changes'                                   => array(
406
				array(),
407
				array( 'changes' => array() ),
408
				array( 'N' ),
409
				WriteCommand::NO_CHANGE_EXIT,
410
				array( '{^No changes were found! Proceed\? \[y/N\]}m' ),
411
			),
412
			'No changes (2)'                               => array(
413
				array(),
414
				array( 'changes' => array() ),
415
				array( 'Y' ),
416
				0,
417
				array( '{^No changes were found! Proceed\? \[y/N\]}m' ),
418
				true,
419
				"# Changelog\n\n## 1.0.2 - $date\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
420
			),
421
			'No changes, non-interactive'                  => array(
422
				array(),
423
				array(
424
					'interactive' => false,
425
					'changes'     => array(),
426
				),
427
				array(),
428
				WriteCommand::NO_CHANGE_EXIT,
429
				array( '{^No changes were found!$}m' ),
430
			),
431
			'No changes, non-interactive, --yes'           => array(
432
				array( '--yes' => true ),
433
				array(
434
					'interactive' => false,
435
					'changes'     => array(),
436
				),
437
				array(),
438
				0,
439
				array( '{^<warning>No changes were found! Continuing anyway\.$}m' ),
440
				true,
441
				"# Changelog\n\n## 1.0.2 - $date\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
442
			),
443
444
			'Deduplication'                                => array(
445
				array(),
446
				array(
447
					'changes' => array(
448
						'initial-release' => "Significance: patch\nType: added\n\nInitial release.\n",
449
						'added-stuff'     => "Significance: patch\nType: added\n\nStuff.\n",
450
						'added-stuff-2'   => "Significance: patch\nType: added\n\nStuff. And more stuff.\n",
451
					),
452
				),
453
				array(),
454
				0,
455
				array( '{^$}m' ),
456
				true,
457
				"# Changelog\n\n## 1.0.2 - $date\n### Added\n- Initial release.\n- Stuff. And more stuff.\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
458
			),
459
			'Deduplication, --dedupliacte=0'               => array(
460
				array( '--deduplicate' => '0' ),
461
				array(
462
					'changes' => array(
463
						'initial-release' => "Significance: patch\nType: added\n\nInitial release.\n",
464
						'added-stuff'     => "Significance: patch\nType: added\n\nStuff.\n",
465
						'added-stuff-2'   => "Significance: patch\nType: added\n\nStuff. And more stuff.\n",
466
					),
467
				),
468
				array(),
469
				0,
470
				array( '{^$}m' ),
471
				true,
472
				"# Changelog\n\n## 1.0.2 - $date\n### Added\n- Initial release.\n- Stuff.\n- Stuff. And more stuff.\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
473
			),
474
			'Deduplication, --dedupliacte=2'               => array(
475
				array( '--deduplicate' => '2' ),
476
				array(
477
					'changes' => array(
478
						'initial-release' => "Significance: patch\nType: added\n\nInitial release.\n",
479
						'added-stuff'     => "Significance: patch\nType: added\n\nStuff.\n",
480
						'added-stuff-2'   => "Significance: patch\nType: added\n\nStuff. And more stuff.\n",
481
					),
482
				),
483
				array(),
484
				0,
485
				array( '{^$}m' ),
486
				true,
487
				"# Changelog\n\n## 1.0.2 - $date\n### Added\n- Stuff. And more stuff.\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
488
			),
489
490
			'Deduplication removes all changes'            => array(
491
				array(),
492
				array(
493
					'changes' => array(
494
						'added-stuff' => "Significance: patch\nType: added\n\nStuff.\n",
495
					),
496
				),
497
				array( 'N' ),
498
				WriteCommand::NO_CHANGE_EXIT,
499
				array( '{^All changes were duplicates\. Proceed\? \[y/N\]}m' ),
500
			),
501
			'Deduplication removes all changes (2)'        => array(
502
				array(),
503
				array(
504
					'changes' => array(
505
						'added-stuff' => "Significance: patch\nType: added\n\nStuff.\n",
506
					),
507
				),
508
				array( 'Y' ),
509
				0,
510
				array( '{^All changes were duplicates\. Proceed\? \[y/N\]}m' ),
511
				true,
512
				"# Changelog\n\n## 1.0.2 - $date\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
513
			),
514
			'Deduplication removes all changes, non-interactive' => array(
515
				array(),
516
				array(
517
					'interactive' => false,
518
					'changes'     => array(
519
						'added-stuff' => "Significance: patch\nType: added\n\nStuff.\n",
520
					),
521
				),
522
				array(),
523
				WriteCommand::NO_CHANGE_EXIT,
524
				array( '{^All changes were duplicates\.$}m' ),
525
			),
526
			'Deduplication removes all changes, non-interactive, --yes' => array(
527
				array( '--yes' => true ),
528
				array(
529
					'interactive' => false,
530
					'changes'     => array(
531
						'added-stuff' => "Significance: patch\nType: added\n\nStuff.\n",
532
					),
533
				),
534
				array(),
535
				0,
536
				array( '{^<warning>All changes were duplicates\. Continuing anyway\.$}m' ),
537
				true,
538
				"# Changelog\n\n## 1.0.2 - $date\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
539
			),
540
541
			'All changes are empty'                        => array(
542
				array(),
543
				array(
544
					'changes' => array(
545
						'duplicate' => "Significance: patch\nType: added\n\nStuff.\n",
546
						'error'     => "Bogus\n",
547
						'empty'     => "Significance: patch\nType: fixed\n\n",
548
					),
549
				),
550
				array( 'Y', 'N' ),
551
				WriteCommand::NO_CHANGE_EXIT,
552
				array( '{There are no changes with content for this write\. Proceed\? \[y/N\]}m' ),
553
			),
554
			'All changes are empty (2)'                    => array(
555
				array(),
556
				array(
557
					'changes' => array(
558
						'duplicate' => "Significance: patch\nType: added\n\nStuff.\n",
559
						'error'     => "Bogus\n",
560
						'empty'     => "Significance: patch\nType: fixed\n\n",
561
					),
562
				),
563
				array( 'Y', 'Y' ),
564
				0,
565
				array( '{There are no changes with content for this write\. Proceed\? \[y/N\]}m' ),
566
				array( 'duplicate', 'empty' ),
567
				"# Changelog\n\n## 1.0.2 - $date\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
568
			),
569
			'All changes are empty, non-interactive'       => array(
570
				array(),
571
				array(
572
					'interactive' => false,
573
					'changes'     => array(
574
						'duplicate' => "Significance: patch\nType: added\n\nStuff.\n",
575
						'empty'     => "Significance: patch\nType: fixed\n\n",
576
					),
577
				),
578
				array(),
579
				WriteCommand::NO_CHANGE_EXIT,
580
				array( '{^There are no changes with content for this write\.$}m' ),
581
			),
582
			'All changes are empty, non-interactive, --yes' => array(
583
				array( '--yes' => true ),
584
				array(
585
					'interactive' => false,
586
					'changes'     => array(
587
						'duplicate' => "Significance: patch\nType: added\n\nStuff.\n",
588
						'error'     => "Bogus\n",
589
						'empty'     => "Significance: patch\nType: fixed\n\n",
590
					),
591
				),
592
				array(),
593
				0,
594
				array( '{^<warning>There are no changes with content for this write\. Continuing anyway\.$}m' ),
595
				array( 'duplicate', 'empty' ),
596
				"# Changelog\n\n## 1.0.2 - $date\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
597
			),
598
599
			'Amend'                                        => array(
600
				array( '--amend' => true ),
601
				array(
602
					'changes' => array(
603
						'added'  => "Significance: patch\nType: added\n\nNew stuff.\n",
604
						'added2' => "Significance: patch\nType: added\n\nZZZ.\n",
605
						'fixed'  => "Significance: patch\nType: fixed\n\nBroken stuff.\n",
606
					),
607
				),
608
				array(),
609
				0,
610
				array( '{^$}' ),
611
				true,
612
				"# Changelog\n\n## 1.0.1 - $date\n\nPrologue for v1.0.1\n\n### Added\n- New stuff.\n- Stuff.\n- ZZZ.\n\n### Removed\n- Other stuff.\n\n### Fixed\n- Broken stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
613
			),
614
			'Amend, upgrade version'                       => array(
615
				array( '--amend' => true ),
616
				array(
617
					'changes' => array(
618
						'added'  => "Significance: minor\nType: added\n\nNew stuff.\n",
619
						'added2' => "Significance: patch\nType: added\n\nZZZ.\n",
620
						'fixed'  => "Significance: patch\nType: fixed\n\nBroken stuff.\n",
621
					),
622
				),
623
				array(),
624
				0,
625
				array( '{^$}' ),
626
				true,
627
				"# Changelog\n\n## 1.1.0 - $date\n\nPrologue for v1.0.1\n\n### Added\n- New stuff.\n- Stuff.\n- ZZZ.\n\n### Removed\n- Other stuff.\n\n### Fixed\n- Broken stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
628
			),
629
			'Amend, no downgrade version'                  => array(
630
				array( '--amend' => true ),
631
				array(
632
					'changes'   => array(
633
						'added'  => "Significance: minor\nType: added\n\nNew stuff.\n",
634
						'added2' => "Significance: patch\nType: added\n\nZZZ.\n",
635
						'fixed'  => "Significance: patch\nType: fixed\n\nBroken stuff.\n",
636
					),
637
					'changelog' => "# Changelog\n\n## 2.0.0 - 2021-02-23\n### Changed\n- Stuff.\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
638
				),
639
				array(),
640
				0,
641
				array( '{^$}' ),
642
				true,
643
				"# Changelog\n\n## 2.0.0 - $date\n### Added\n- New stuff.\n- ZZZ.\n\n### Changed\n- Stuff.\n\n### Fixed\n- Broken stuff.\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
644
			),
645
			'Amend initial release'                        => array(
646
				array( '--amend' => true ),
647
				array(
648
					'changes'   => array(
649
						'added'  => "Significance: minor\nType: added\n\nNew stuff.\n",
650
						'added2' => "Significance: patch\nType: added\n\nZZZ.\n",
651
						'fixed'  => "Significance: patch\nType: fixed\n\nBroken stuff.\n",
652
					),
653
					'changelog' => "# Changelog\n\n## [1.0.0] - 2021-02-23\n\n- Initial release.\n\n### Added\n- Everything.\n\n[1.0.0]: https://example.org/new-thing\n",
654
				),
655
				array(),
656
				0,
657
				array( '{^$}' ),
658
				true,
659
				"# Changelog\n\n## [1.0.0] - $date\n\n- Initial release.\n\n### Added\n- Everything.\n- New stuff.\n- ZZZ.\n\n### Fixed\n- Broken stuff.\n\n[1.0.0]: https://example.org/new-thing\n",
660
			),
661
			'Amend empty changelog'                        => array(
662
				array(
663
					'--amend'       => true,
664
					'--use-version' => '0.1.0',
665
				),
666
				array(
667
					'changes'   => array(
668
						'added'  => "Significance: minor\nType: added\n\nNew stuff.\n",
669
						'added2' => "Significance: patch\nType: added\n\nZZZ.\n",
670
						'fixed'  => "Significance: patch\nType: fixed\n\nBroken stuff.\n",
671
					),
672
					'changelog' => '',
673
				),
674
				array(),
675
				0,
676
				array( '{^$}' ),
677
				true,
678
				"## 0.1.0 - $date\n### Added\n- New stuff.\n- ZZZ.\n\n### Fixed\n- Broken stuff.\n",
679
			),
680
			'Amend and options'                            => array(
681
				array(
682
					'--amend'    => true,
683
					'--prologue' => 'New prologue',
684
					'--link'     => 'https://example.org/new-link',
685
				),
686
				array(
687
					'changes' => array(
688
						'added'  => "Significance: patch\nType: added\n\nNew stuff.\n",
689
						'added2' => "Significance: patch\nType: added\n\nZZZ.\n",
690
						'fixed'  => "Significance: patch\nType: fixed\n\nBroken stuff.\n",
691
					),
692
				),
693
				array(),
694
				0,
695
				array( '{^$}' ),
696
				true,
697
				"# Changelog\n\n## [1.0.1] - $date\n\nNew prologue\n\n### Added\n- New stuff.\n- Stuff.\n- ZZZ.\n\n### Removed\n- Other stuff.\n\n### Fixed\n- Broken stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n\n[1.0.1]: https://example.org/new-link\n",
698
			),
699
			'Amend without sorting by content'             => array(
700
				array( '--amend' => true ),
701
				array(
702
					'composer.json' => array( 'ordering' => array( 'subheading' ) ),
703
					'changes'       => array(
704
						'added'   => "Significance: patch\nType: added\n\nZZZ.\n",
705
						'removed' => "Significance: patch\nType: removed\n\nBroken stuff.\n",
706
					),
707
				),
708
				array(),
709
				0,
710
				array( '{^$}' ),
711
				true,
712
				"# Changelog\n\n## 1.0.1 - $date\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n- ZZZ.\n\n### Removed\n- Other stuff.\n- Broken stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
713
			),
714
715
			'--use-version invalid'                        => array(
716
				array( '--use-version' => '2.0' ),
717
				array(),
718
				array( 'N' ),
719
				WriteCommand::ASKED_EXIT,
720
				array(
721
					'{^Invalid --use-version: Version number "2.0" is not in a recognized format\.$}m',
722
					'{^The specified version is not valid\. This may cause problems in the future! Proceed\? \[y/N\]}m',
723
				),
724
			),
725
			'--use-version invalid (2)'                    => array(
726
				array( '--use-version' => '2.0' ),
727
				array(),
728
				array( 'Y' ),
729
				0,
730
				array(
731
					'{^Invalid --use-version: Version number "2.0" is not in a recognized format\.$}m',
732
					'{^The specified version is not valid\. This may cause problems in the future! Proceed\? \[y/N\]}m',
733
				),
734
				true,
735
				"# Changelog\n\n## 2.0 - $date\n### Fixed\n- Fixed a thing.\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
736
			),
737
			'--use-version invalid, non-interactive'       => array(
738
				array( '--use-version' => '2.0' ),
739
				array( 'interactive' => false ),
740
				array(),
741
				WriteCommand::ASKED_EXIT,
742
				array(
743
					'{^Invalid --use-version: Version number "2.0" is not in a recognized format\.$}m',
744
					'{^The specified version is not valid\. This may cause problems in the future!$}m',
745
				),
746
			),
747
			'--use-version invalid, non-interactive, --yes' => array(
748
				array(
749
					'--use-version' => '2.0',
750
					'--yes'         => true,
751
				),
752
				array( 'interactive' => false ),
753
				array(),
754
				0,
755
				array(
756
					'{^Invalid --use-version: Version number "2.0" is not in a recognized format\.$}m',
757
					'{^<warning>The specified version is not valid\. This may cause problems in the future! Continuing anyway\.$}m',
758
				),
759
				true,
760
				"# Changelog\n\n## 2.0 - $date\n### Fixed\n- Fixed a thing.\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
761
			),
762
763
			'--use-version not normalized'                 => array(
764
				array( '--use-version' => '2.0.00' ),
765
				array(),
766
				array( 'abort' ),
767
				WriteCommand::ASKED_EXIT,
768
				array(
769
					'{^The supplied version 2.0.00 is not normalized\.$}m',
770
					'{^\s*\[proceed\s*\] Proceed with 2\.0\.00$}m',
771
					'{^\s*\[normalize\s*\] Normalize to 2\.0\.0$}m',
772
					'{^\s*\[abort\s*\] Abort$}m',
773
				),
774
			),
775
			'--use-version not normalized, proceed'        => array(
776
				array( '--use-version' => '2.0.00' ),
777
				array(),
778
				array( 'proceed' ),
779
				0,
780
				array(
781
					'{^The supplied version 2.0.00 is not normalized\.$}m',
782
					'{^\s*\[proceed\s*\] Proceed with 2\.0\.00$}m',
783
					'{^\s*\[normalize\s*\] Normalize to 2\.0\.0$}m',
784
					'{^\s*\[abort\s*\] Abort$}m',
785
				),
786
				true,
787
				"# Changelog\n\n## 2.0.00 - $date\n### Fixed\n- Fixed a thing.\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
788
			),
789
			'--use-version not normalized, normalize'      => array(
790
				array( '--use-version' => '2.0.00' ),
791
				array(),
792
				array( 'normalize' ),
793
				0,
794
				array(
795
					'{^The supplied version 2.0.00 is not normalized\.$}m',
796
					'{^\s*\[proceed\s*\] Proceed with 2\.0\.00$}m',
797
					'{^\s*\[normalize\s*\] Normalize to 2\.0\.0$}m',
798
					'{^\s*\[abort\s*\] Abort$}m',
799
				),
800
				true,
801
				"# Changelog\n\n## 2.0.0 - $date\n### Fixed\n- Fixed a thing.\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
802
			),
803
			'--use-version not normalized, non-interactive' => array(
804
				array( '--use-version' => '2.0.00' ),
805
				array( 'interactive' => false ),
806
				array(),
807
				WriteCommand::ASKED_EXIT,
808
				array( '{^The supplied version 2.0.00 is not normalized, it should be 2.0.0\.$}m' ),
809
			),
810
			'--use-version not normalized, non-interactive, --yes' => array(
811
				array(
812
					'--use-version' => '2.0.00',
813
					'--yes'         => true,
814
				),
815
				array( 'interactive' => false ),
816
				array(),
817
				0,
818
				array( '{^<warning>The supplied version 2.0.00 is not normalized, it should be 2.0.0\. Continuing anyway\.$}m' ),
819
				true,
820
				"# Changelog\n\n## 2.0.00 - $date\n### Fixed\n- Fixed a thing.\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
821
			),
822
823
			'--use-version older'                          => array(
824
				array( '--use-version' => '1.0.1-dev' ),
825
				array(),
826
				array( 'N' ),
827
				WriteCommand::ASKED_EXIT,
828
				array( '{^The most recent version in the changelog is 1\.0\.1, which comes after 1\.0\.1-dev\. Proceed\? \[y/N\]}m' ),
829
			),
830
			'--use-version older (2)'                      => array(
831
				array( '--use-version' => '1.0.1-dev' ),
832
				array(),
833
				array( 'Y' ),
834
				0,
835
				array( '{^The most recent version in the changelog is 1\.0\.1, which comes after 1\.0\.1-dev\. Proceed\? \[y/N\]}m' ),
836
				true,
837
				"# Changelog\n\n## 1.0.1-dev - $date\n### Fixed\n- Fixed a thing.\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
838
			),
839
			'--use-version older, non-interactive'         => array(
840
				array( '--use-version' => '1.0.1-dev' ),
841
				array( 'interactive' => false ),
842
				array(),
843
				WriteCommand::ASKED_EXIT,
844
				array( '{^The most recent version in the changelog is 1\.0\.1, which comes after 1\.0\.1-dev\.$}m' ),
845
			),
846
			'--use-version older, non-interactive, --yes'  => array(
847
				array(
848
					'--use-version' => '1.0.1-dev',
849
					'--yes'         => true,
850
				),
851
				array( 'interactive' => false ),
852
				array(),
853
				0,
854
				array( '{^<warning>The most recent version in the changelog is 1\.0\.1, which comes after 1\.0\.1-dev\. Continuing anyway\.$}m' ),
855
				true,
856
				"# Changelog\n\n## 1.0.1-dev - $date\n### Fixed\n- Fixed a thing.\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
857
			),
858
859
			'--use-version equal'                          => array(
860
				array( '--use-version' => '1.0.1' ),
861
				array(),
862
				array( 'N' ),
863
				WriteCommand::ASKED_EXIT,
864
				array( '{^The most recent version in the changelog is 1\.0\.1, which is equivalent to 1\.0\.1\. Proceed\? \[y/N\]}m' ),
865
			),
866
			'--use-version equal (2)'                      => array(
867
				array( '--use-version' => '1.0.1' ),
868
				array(),
869
				array( 'Y' ),
870
				0,
871
				array( '{^The most recent version in the changelog is 1\.0\.1, which is equivalent to 1\.0\.1\. Proceed\? \[y/N\]}m' ),
872
				true,
873
				"# Changelog\n\n## 1.0.1 - $date\n### Fixed\n- Fixed a thing.\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
874
			),
875
			'--use-version equal, non-interactive'         => array(
876
				array( '--use-version' => '1.0.1' ),
877
				array( 'interactive' => false ),
878
				array(),
879
				WriteCommand::ASKED_EXIT,
880
				array( '{^The most recent version in the changelog is 1\.0\.1, which is equivalent to 1\.0\.1\.$}m' ),
881
			),
882
			'--use-version equal, non-interactive, --yes'  => array(
883
				array(
884
					'--use-version' => '1.0.1',
885
					'--yes'         => true,
886
				),
887
				array( 'interactive' => false ),
888
				array(),
889
				0,
890
				array( '{^<warning>The most recent version in the changelog is 1\.0\.1, which is equivalent to 1\.0\.1\. Continuing anyway\.$}m' ),
891
				true,
892
				"# Changelog\n\n## 1.0.1 - $date\n### Fixed\n- Fixed a thing.\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
893
			),
894
895
			'--use-significance'                           => array(
896
				array( '--use-significance' => 'major' ),
897
				array(),
898
				array(),
899
				0,
900
				array( '{^$}' ),
901
				true,
902
				"# Changelog\n\n## 2.0.0 - $date\n### Fixed\n- Fixed a thing.\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
903
			),
904
			'--use-significance invalid'                   => array(
905
				array( '--use-significance' => 'bogus' ),
906
				array(),
907
				array(),
908
				WriteCommand::FATAL_EXIT,
909
				array( '/^' . preg_quote( "Automattic\\Jetpack\\Changelog\\ChangeEntry::setSignificance: Significance must be 'patch', 'minor', or 'major' (or null)", '/' ) . '$/' ),
910
			),
911
912
			'--prerelease invalid'                         => array(
913
				array( '--prerelease' => '???' ),
914
				array(),
915
				array(),
916
				WriteCommand::FATAL_EXIT,
917
				array( '{^Failed to determine new version: Invalid prerelease data$}m' ),
918
			),
919
			'Version in changelog valid'                   => array(
920
				array(),
921
				array(
922
					'changelog' => "## 2.0 - 2021-02-24\n\nThis is the \"problem in the future\" that comes of ignoring the warning about an invalid --use-version...\n",
923
				),
924
				array(),
925
				WriteCommand::FATAL_EXIT,
926
				array( '{^Changelog file contains invalid version 2\.0! Use --use-version to specify the new version\.$}m' ),
927
			),
928
			'Version in changelog valid, amending'         => array(
929
				array( '--amend' => true ),
930
				array(
931
					'changelog' => "## 2.0 - 2021-02-24\n\nThis is the \"problem in the future\" that comes of ignoring the warning about an invalid --use-version...\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
932
				),
933
				array(),
934
				0,
935
				array( '{^$}m' ),
936
				true,
937
				"## 1.0.1 - $date\n\nThis is the \"problem in the future\" that comes of ignoring the warning about an invalid --use-version...\n\n### Fixed\n- Fixed a thing.\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n",
938
			),
939
940
			'Entry creation fail'                          => array(
941
				array( '--link' => '???' ),
942
				array(),
943
				array(),
944
				WriteCommand::FATAL_EXIT,
945
				array( '/^' . preg_quote( 'Failed to create changelog entry: Automattic\\Jetpack\\Changelog\\ChangelogEntry::setLink: Invalid URL', '/' ) . '$/' ),
946
			),
947
			'Link template'                                => array(
948
				array(),
949
				array(
950
					'composer.json' => array( 'link-template' => 'https://example.org/diff/${old}..${new}' ),
951
				),
952
				array(),
953
				0,
954
				array( '{^$}' ),
955
				true,
956
				"# Changelog\n\n## [1.0.2] - $date\n### Fixed\n- Fixed a thing.\n\n## 1.0.1 - 2021-02-23\n\nPrologue for v1.0.1\n\n### Added\n- Stuff.\n\n### Removed\n- Other stuff.\n\nEpilogue for v1.0.1\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n\n[1.0.2]: https://example.org/diff/1.0.1..1.0.2\n",
957
			),
958
			'Link template, initial version'               => array(
959
				array( '--use-version' => '1.0.0' ),
960
				array(
961
					'composer.json' => array( 'link-template' => 'https://example.org/diff/${old}..${new}' ),
962
					'changelog'     => "# Changelog\n",
963
				),
964
				array(),
965
				0,
966
				array( '{^$}' ),
967
				true,
968
				"# Changelog\n\n## 1.0.0 - $date\n### Fixed\n- Fixed a thing.\n",
969
			),
970
971
			'Interactive defaulting'                       => array(
972
				array(),
973
				array( 'changelog' => null ),
974
				array( '' ),
975
				WriteCommand::ASKED_EXIT,
976
				array( '{^Changelog file /.*/CHANGELOG\.md does not exist! Proceed\? \[y/N\]}m' ),
977
			),
978
			'Interactive defaulting, --yes'                => array(
979
				array( '--yes' => true ),
980
				array( 'changelog' => null ),
981
				array( '' ),
982
				WriteCommand::FATAL_EXIT,
983
				array(
984
					'{^Changelog file /.*/CHANGELOG\.md does not exist! Proceed\? \[Y/n\]}m',
985
					'/Changelog file contains no entries! Use --use-version to specify the initial version\.$/m',
986
				),
987
			),
988
989
			'Options from versioning plugin'               => array(
990
				array( '--point-release' => true ),
991
				array(
992
					'composer.json' => array( 'versioning' => 'wordpress' ),
993
					'changelog'     => "# Changelog\n\n## 1.0 - 2021-02-24\n\n- Initial release\n",
994
				),
995
				array(),
996
				0,
997
				array( '{^$}' ),
998
				true,
999
				'changelog' => "# Changelog\n\n## 1.0.1 - $date\n### Fixed\n- Fixed a thing.\n\n## 1.0 - 2021-02-24\n\n- Initial release\n",
1000
			),
1001
		);
1002
	}
1003
1004
	/**
1005
	 * Test failure to format changelog.
1006
	 */
1007
	public function testWriteChangelog_formatError() {
1008
		$formatter = $this->getMockBuilder( FormatterPlugin::class )
1009
			->setMethodsExcept( array() )
1010
			->getMock();
1011
		$formatter->expects( $this->never() )->method( $this->logicalNot( $this->matches( 'format' ) ) );
1012
		$formatter->method( 'format' )->willThrowException( new InvalidArgumentException( 'Exception for test.' ) );
1013
1014
		$w            = TestingAccessWrapper::newFromObject( $this->getCommand( 'write' ) );
1015
		$w->formatter = $formatter;
1016
1017
		$input  = new ArrayInput( array() );
1018
		$output = new BufferedOutput();
1019
		$ret    = $w->writeChangelog( $input, $output, new Changelog() );
1020
		$this->assertSame( WriteCommand::FATAL_EXIT, $ret );
1021
		$this->assertSame( "Failed to write the changelog: Exception for test.\n", $output->fetch() );
1022
	}
1023
1024
	/**
1025
	 * Test failure to write changelog.
1026
	 */
1027
	public function testWriteChangelog_writeError() {
1028
		mkdir( 'CHANGELOG.md' );
1029
1030
		$formatter = $this->getMockBuilder( FormatterPlugin::class )
1031
			->setMethodsExcept( array() )
1032
			->getMock();
1033
		$formatter->expects( $this->never() )->method( $this->logicalNot( $this->matches( 'format' ) ) );
1034
		$formatter->method( 'format' )->willReturn( "Changelog!\n" );
1035
1036
		$w            = TestingAccessWrapper::newFromObject( $this->getCommand( 'write' ) );
1037
		$w->formatter = $formatter;
1038
1039
		$input  = new ArrayInput( array() );
1040
		$output = new BufferedOutput();
1041
		$ret    = $w->writeChangelog( $input, $output, new Changelog() );
1042
		$this->assertSame( WriteCommand::FATAL_EXIT, $ret );
1043
		$cwd = getcwd();
1044
		$this->assertStringContainsString( "Failed to write $cwd/CHANGELOG.md: ", $output->fetch() );
1045
	}
1046
1047
	/**
1048
	 * Test delete changes failure.
1049
	 */
1050
	public function testDeleteChanges_error() {
1051
		$w = TestingAccessWrapper::newFromObject( $this->getCommand( 'write' ) );
1052
1053
		$input  = new ArrayInput( array() );
1054
		$output = new BufferedOutput();
1055
		$ret    = $w->deleteChanges(
1056
			$input,
1057
			$output,
1058
			array(
1059
				'doesnotexist' => 0,
1060
				'.gitkeep'     => 0,
1061
			)
1062
		);
1063
		$this->assertSame( WriteCommand::DELETE_FAILED_EXIT, $ret );
1064
		$out = $output->fetch();
1065
		$this->assertStringContainsString( 'Failed to delete doesnotexist: ', $out );
1066
		$this->assertStringNotContainsString( 'Failed to delete .gitkeep: ', $out );
1067
	}
1068
1069
	/**
1070
	 * Test execute handling of writeChangelog failing.
1071
	 */
1072
	public function testExecute_writeChangelog_fail() {
1073
		$command = $this->getMockBuilder( WriteCommand::class )
1074
			->setMethods( array( 'writeChangelog', 'deleteChanges' ) )
1075
			->getMock();
1076
		$command->setApplication( $this->getCommand( 'write' )->getApplication() );
1077
		$command->method( 'writeChangelog' )->willReturn( WriteCommand::FATAL_EXIT );
1078
		$command->expects( $this->never() )->method( 'deleteChanges' );
1079
1080
		file_put_contents( 'CHANGELOG.md', "# Changelog\n\n## 1.0.0 - 2021-02-23\n\n- Initial release.\n" );
1081
		file_put_contents( 'changelog/a-change', "Significance: patch\nType: fixed\n\nFixed a thing.\n" );
1082
1083
		$tester = new CommandTester( $command );
1084
		$code   = $tester->execute( array() );
1085
		$this->assertSame( WriteCommand::FATAL_EXIT, $code );
1086
	}
1087
1088
}
1089