|
1
|
|
|
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName |
|
2
|
|
|
/** |
|
3
|
|
|
* Tests for the semver versioning plugin. |
|
4
|
|
|
* |
|
5
|
|
|
* @package automattic/jetpack-changelogger |
|
6
|
|
|
*/ |
|
7
|
|
|
|
|
8
|
|
|
// phpcs:disable WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase |
|
9
|
|
|
|
|
10
|
|
|
namespace Automattic\Jetpack\Changelogger\Tests\Plugins; |
|
11
|
|
|
|
|
12
|
|
|
use Automattic\Jetpack\Changelog\ChangeEntry; |
|
13
|
|
|
use Automattic\Jetpack\Changelogger\Plugins\SemverVersioning; |
|
14
|
|
|
use InvalidArgumentException; |
|
15
|
|
|
use PHPUnit\Framework\TestCase; |
|
16
|
|
|
use Symfony\Component\Console\Input\ArrayInput; |
|
17
|
|
|
use Symfony\Component\Console\Output\BufferedOutput; |
|
18
|
|
|
|
|
19
|
|
|
/** |
|
20
|
|
|
* Tests for the semver versioning plugin. |
|
21
|
|
|
* |
|
22
|
|
|
* @covers \Automattic\Jetpack\Changelogger\Plugins\SemverVersioning |
|
23
|
|
|
*/ |
|
24
|
|
|
class SemverVersioningTest extends TestCase { |
|
25
|
|
|
use \Yoast\PHPUnitPolyfills\Polyfills\AssertIsType; |
|
26
|
|
|
use \Yoast\PHPUnitPolyfills\Polyfills\ExpectException; |
|
27
|
|
|
|
|
28
|
|
|
/** |
|
29
|
|
|
* Test normalizeVersion and parseVersion. |
|
30
|
|
|
* |
|
31
|
|
|
* @dataProvider provideNormalizeVersion |
|
32
|
|
|
* @param string $version Version. |
|
33
|
|
|
* @param string|InvalidArgumentException $expect Expected result. |
|
34
|
|
|
*/ |
|
35
|
|
View Code Duplication |
public function testNormalizeVersion( $version, $expect ) { |
|
36
|
|
|
$obj = new SemverVersioning( array() ); |
|
|
|
|
|
|
37
|
|
|
if ( $expect instanceof InvalidArgumentException ) { |
|
38
|
|
|
$this->expectException( InvalidArgumentException::class ); |
|
39
|
|
|
$this->expectExceptionMessage( $expect->getMessage() ); |
|
40
|
|
|
$obj->normalizeVersion( $version ); |
|
41
|
|
|
} else { |
|
42
|
|
|
$this->assertSame( $expect, $obj->normalizeVersion( $version ) ); |
|
43
|
|
|
} |
|
44
|
|
|
} |
|
45
|
|
|
|
|
46
|
|
|
/** |
|
47
|
|
|
* Data provider for testNormalizeVersion. |
|
48
|
|
|
*/ |
|
49
|
|
|
public function provideNormalizeVersion() { |
|
50
|
|
|
return array( |
|
51
|
|
|
array( '1.2.3', '1.2.3' ), |
|
52
|
|
|
array( '11.22.33', '11.22.33' ), |
|
53
|
|
|
array( '0.0.0', '0.0.0' ), |
|
54
|
|
|
array( '1.2.3-alpha', '1.2.3-alpha' ), |
|
55
|
|
|
array( '1.2.3-alpha.1', '1.2.3-alpha.1' ), |
|
56
|
|
|
array( '1.2.3+foobar', '1.2.3+foobar' ), |
|
57
|
|
|
array( '1.2.3+foobar.2', '1.2.3+foobar.2' ), |
|
58
|
|
|
array( '1.2.3-alpha+foobar', '1.2.3-alpha+foobar' ), |
|
59
|
|
|
array( '1.2.3-alpha.1+foobar.2', '1.2.3-alpha.1+foobar.2' ), |
|
60
|
|
|
array( '0001.0002.000-000alpha000.0001+000foobar000.0002', '1.2.0-000alpha000.1+000foobar000.0002' ), |
|
61
|
|
|
|
|
62
|
|
|
array( '1.2', new InvalidArgumentException( 'Version number "1.2" is not in a recognized format.' ) ), |
|
63
|
|
|
array( '1.2.x', new InvalidArgumentException( 'Version number "1.2.x" is not in a recognized format.' ) ), |
|
64
|
|
|
array( '1.x.4', new InvalidArgumentException( 'Version number "1.x.4" is not in a recognized format.' ) ), |
|
65
|
|
|
array( '1..4', new InvalidArgumentException( 'Version number "1..4" is not in a recognized format.' ) ), |
|
66
|
|
|
array( '.2.3', new InvalidArgumentException( 'Version number ".2.3" is not in a recognized format.' ) ), |
|
67
|
|
|
array( '1.2.', new InvalidArgumentException( 'Version number "1.2." is not in a recognized format.' ) ), |
|
68
|
|
|
array( '1.2.-1', new InvalidArgumentException( 'Version number "1.2.-1" is not in a recognized format.' ) ), |
|
69
|
|
|
array( 'v1.2.3', new InvalidArgumentException( 'Version number "v1.2.3" is not in a recognized format.' ) ), |
|
70
|
|
|
array( '1.2.3.4', new InvalidArgumentException( 'Version number "1.2.3.4" is not in a recognized format.' ) ), |
|
71
|
|
|
array( '1.2-alpha', new InvalidArgumentException( 'Version number "1.2-alpha" is not in a recognized format.' ) ), |
|
72
|
|
|
array( '1.2.3-?', new InvalidArgumentException( 'Version number "1.2.3-?" is not in a recognized format.' ) ), |
|
73
|
|
|
array( '1.2.3+?', new InvalidArgumentException( 'Version number "1.2.3+?" is not in a recognized format.' ) ), |
|
74
|
|
|
array( '1.2.3-a..b', new InvalidArgumentException( 'Version number "1.2.3-a..b" is not in a recognized format.' ) ), |
|
75
|
|
|
array( '1.2.3+a..b', new InvalidArgumentException( 'Version number "1.2.3+a..b" is not in a recognized format.' ) ), |
|
76
|
|
|
); |
|
77
|
|
|
} |
|
78
|
|
|
|
|
79
|
|
|
/** |
|
80
|
|
|
* Test nextVersion. |
|
81
|
|
|
* |
|
82
|
|
|
* @dataProvider provideNextVersion |
|
83
|
|
|
* @param string $version Version. |
|
84
|
|
|
* @param ChangeEntry[] $changes Changes. |
|
85
|
|
|
* @param array $extra Extra components. |
|
86
|
|
|
* @param string|InvalidArgumentException $expect Expected result. |
|
87
|
|
|
* @param string $expectOutput Expected output. |
|
88
|
|
|
*/ |
|
89
|
|
|
public function testNextVersion( $version, array $changes, array $extra, $expect, $expectOutput = '' ) { |
|
90
|
|
|
$obj = new SemverVersioning( array() ); |
|
|
|
|
|
|
91
|
|
|
|
|
92
|
|
|
$out1 = $this->getMockBuilder( BufferedOutput::class ) |
|
93
|
|
|
->setMethods( array( 'getErrorOutput' ) ) |
|
94
|
|
|
->getMock(); |
|
95
|
|
|
$out2 = new BufferedOutput(); |
|
96
|
|
|
$out1->method( 'getErrorOutput' )->willReturn( $out2 ); |
|
97
|
|
|
|
|
98
|
|
|
$obj->setIO( new ArrayInput( array() ), $out1 ); |
|
99
|
|
|
|
|
100
|
|
|
if ( $expect instanceof InvalidArgumentException ) { |
|
101
|
|
|
$this->expectException( InvalidArgumentException::class ); |
|
102
|
|
|
$this->expectExceptionMessage( $expect->getMessage() ); |
|
103
|
|
|
$obj->nextVersion( $version, $changes, $extra ); |
|
|
|
|
|
|
104
|
|
|
} else { |
|
105
|
|
|
$this->assertSame( $expect, $obj->nextVersion( $version, $changes, $extra ) ); |
|
|
|
|
|
|
106
|
|
|
$this->assertSame( '', $out1->fetch() ); |
|
107
|
|
|
$this->assertSame( $expectOutput, $out2->fetch() ); |
|
108
|
|
|
} |
|
109
|
|
|
} |
|
110
|
|
|
|
|
111
|
|
|
/** |
|
112
|
|
|
* Data provider for testNextVersion. |
|
113
|
|
|
*/ |
|
114
|
|
|
public function provideNextVersion() { |
|
115
|
|
|
return array( |
|
116
|
|
|
'No changes' => array( |
|
117
|
|
|
'1.2.3', |
|
118
|
|
|
array(), |
|
119
|
|
|
array(), |
|
120
|
|
|
'1.2.4', |
|
121
|
|
|
), |
|
122
|
|
|
'Patch changes' => array( |
|
123
|
|
|
'1.2.3', |
|
124
|
|
|
array( |
|
125
|
|
|
new ChangeEntry( array( 'significance' => 'patch' ) ), |
|
126
|
|
|
new ChangeEntry( array( 'significance' => 'patch' ) ), |
|
127
|
|
|
new ChangeEntry( array( 'significance' => null ) ), |
|
128
|
|
|
new ChangeEntry( array( 'significance' => 'patch' ) ), |
|
129
|
|
|
), |
|
130
|
|
|
array(), |
|
131
|
|
|
'1.2.4', |
|
132
|
|
|
), |
|
133
|
|
|
'Minor change' => array( |
|
134
|
|
|
'1.2.3', |
|
135
|
|
|
array( |
|
136
|
|
|
new ChangeEntry( array( 'significance' => 'patch' ) ), |
|
137
|
|
|
new ChangeEntry( array( 'significance' => 'minor' ) ), |
|
138
|
|
|
new ChangeEntry( array( 'significance' => null ) ), |
|
139
|
|
|
new ChangeEntry( array( 'significance' => 'patch' ) ), |
|
140
|
|
|
), |
|
141
|
|
|
array(), |
|
142
|
|
|
'1.3.0', |
|
143
|
|
|
), |
|
144
|
|
|
'Major change' => array( |
|
145
|
|
|
'1.2.3', |
|
146
|
|
|
array( |
|
147
|
|
|
new ChangeEntry( array( 'significance' => 'patch' ) ), |
|
148
|
|
|
new ChangeEntry( array( 'significance' => 'minor' ) ), |
|
149
|
|
|
new ChangeEntry( array( 'significance' => null ) ), |
|
150
|
|
|
new ChangeEntry( array( 'significance' => 'major' ) ), |
|
151
|
|
|
new ChangeEntry( array( 'significance' => 'patch' ) ), |
|
152
|
|
|
), |
|
153
|
|
|
array(), |
|
154
|
|
|
'2.0.0', |
|
155
|
|
|
), |
|
156
|
|
|
'Version number with extra components' => array( |
|
157
|
|
|
'1.2.3-foo', |
|
158
|
|
|
array(), |
|
159
|
|
|
array(), |
|
160
|
|
|
'1.2.4', |
|
161
|
|
|
), |
|
162
|
|
|
'Version number with extra components (2)' => array( |
|
163
|
|
|
'1.2.9+123', |
|
164
|
|
|
array(), |
|
165
|
|
|
array(), |
|
166
|
|
|
'1.2.10', |
|
167
|
|
|
), |
|
168
|
|
|
'Version number with extra components (3)' => array( |
|
169
|
|
|
'1.2.3-foo+bar', |
|
170
|
|
|
array(), |
|
171
|
|
|
array(), |
|
172
|
|
|
'1.2.4', |
|
173
|
|
|
), |
|
174
|
|
|
|
|
175
|
|
|
'Non-major update for version 0' => array( |
|
176
|
|
|
'0.1.2', |
|
177
|
|
|
array( |
|
178
|
|
|
new ChangeEntry( array( 'significance' => 'patch' ) ), |
|
179
|
|
|
new ChangeEntry( array( 'significance' => 'minor' ) ), |
|
180
|
|
|
new ChangeEntry( array( 'significance' => null ) ), |
|
181
|
|
|
new ChangeEntry( array( 'significance' => 'patch' ) ), |
|
182
|
|
|
), |
|
183
|
|
|
array(), |
|
184
|
|
|
'0.2.0', |
|
185
|
|
|
), |
|
186
|
|
|
'Major update for version 0' => array( |
|
187
|
|
|
'0.1.2', |
|
188
|
|
|
array( |
|
189
|
|
|
new ChangeEntry( array( 'significance' => 'patch' ) ), |
|
190
|
|
|
new ChangeEntry( array( 'significance' => 'major' ) ), |
|
191
|
|
|
new ChangeEntry( array( 'significance' => null ) ), |
|
192
|
|
|
new ChangeEntry( array( 'significance' => 'patch' ) ), |
|
193
|
|
|
), |
|
194
|
|
|
array(), |
|
195
|
|
|
'0.2.0', |
|
196
|
|
|
"<warning>Semver does not automatically move version 0.y.z to 1.0.0.\n<warning>You will have to do that manually when you're ready for the first release.\n", |
|
197
|
|
|
), |
|
198
|
|
|
|
|
199
|
|
|
'Including extra components' => array( |
|
200
|
|
|
'1.2.3', |
|
201
|
|
|
array(), |
|
202
|
|
|
array( |
|
203
|
|
|
'prerelease' => 'alpha.002', |
|
204
|
|
|
'buildinfo' => 'g12345678.003', |
|
205
|
|
|
), |
|
206
|
|
|
'1.2.4-alpha.2+g12345678.003', |
|
207
|
|
|
), |
|
208
|
|
|
'Including extra components (2)' => array( |
|
209
|
|
|
'1.2.3-foo', |
|
210
|
|
|
array(), |
|
211
|
|
|
array( 'buildinfo' => 'g12345678' ), |
|
212
|
|
|
'1.2.4+g12345678', |
|
213
|
|
|
), |
|
214
|
|
|
|
|
215
|
|
|
'Invalid prerelease component' => array( |
|
216
|
|
|
'1.2.3-foo', |
|
217
|
|
|
array(), |
|
218
|
|
|
array( 'prerelease' => 'delta?' ), |
|
219
|
|
|
new InvalidArgumentException( 'Invalid prerelease data' ), |
|
220
|
|
|
), |
|
221
|
|
|
'Invalid buildinfo component' => array( |
|
222
|
|
|
'1.2.3-foo', |
|
223
|
|
|
array(), |
|
224
|
|
|
array( 'buildinfo' => 'build?' ), |
|
225
|
|
|
new InvalidArgumentException( 'Invalid buildinfo data' ), |
|
226
|
|
|
), |
|
227
|
|
|
); |
|
228
|
|
|
} |
|
229
|
|
|
|
|
230
|
|
|
/** |
|
231
|
|
|
* Test nextVersion, 0.x version major update with non-console output. |
|
232
|
|
|
*/ |
|
233
|
|
|
public function testNextVersion_majorNonConsole() { |
|
234
|
|
|
$obj = new SemverVersioning( array() ); |
|
|
|
|
|
|
235
|
|
|
$out = new BufferedOutput(); |
|
236
|
|
|
$obj->setIO( new ArrayInput( array() ), $out ); |
|
237
|
|
|
$this->assertSame( |
|
238
|
|
|
'0.2.0', |
|
239
|
|
|
$obj->nextVersion( '0.1.2', array( new ChangeEntry( array( 'significance' => 'major' ) ) ) ) |
|
|
|
|
|
|
240
|
|
|
); |
|
241
|
|
|
$this->assertSame( '', $out->fetch() ); |
|
242
|
|
|
} |
|
243
|
|
|
|
|
244
|
|
|
/** |
|
245
|
|
|
* Test compareVersions. |
|
246
|
|
|
* |
|
247
|
|
|
* @dataProvider provideCompareVersions |
|
248
|
|
|
* @param string $a Version A. |
|
249
|
|
|
* @param string $expect Expected result converted to a string, '>', '==', or '<'. |
|
250
|
|
|
* @param string $b Version B. |
|
251
|
|
|
*/ |
|
252
|
|
View Code Duplication |
public function testCompareVersions( $a, $expect, $b ) { |
|
253
|
|
|
$obj = new SemverVersioning( array() ); |
|
|
|
|
|
|
254
|
|
|
$ret = $obj->compareVersions( $a, $b ); |
|
255
|
|
|
$this->assertIsInt( $ret ); |
|
256
|
|
|
$ret = $ret < 0 ? '<' : ( $ret > 0 ? '>' : '==' ); |
|
257
|
|
|
$this->assertSame( $expect, $ret ); |
|
258
|
|
|
} |
|
259
|
|
|
|
|
260
|
|
|
/** |
|
261
|
|
|
* Data provider for testCompareVersions. |
|
262
|
|
|
*/ |
|
263
|
|
|
public function provideCompareVersions() { |
|
264
|
|
|
return array( |
|
265
|
|
|
array( '1.0.0', '==', '1.0.0' ), |
|
266
|
|
|
array( '1.0.0', '<', '2.0.0' ), |
|
267
|
|
|
array( '2.0.0', '>', '1.0.0' ), |
|
268
|
|
|
array( '1.1.0', '>', '1.0.0' ), |
|
269
|
|
|
array( '1.0.0', '<', '1.1.0' ), |
|
270
|
|
|
array( '1.999.999', '<', '2.0.0' ), |
|
271
|
|
|
array( '1.1.0', '<', '1.1.1' ), |
|
272
|
|
|
array( '1.0.999', '<', '1.1.0' ), |
|
273
|
|
|
array( '1.1.2', '>', '1.1.1' ), |
|
274
|
|
|
array( '1.1.1-dev', '<', '1.1.1' ), |
|
275
|
|
|
array( '1.1.1', '>', '1.1.1-p1' ), |
|
276
|
|
|
array( '1.1.1-alpha', '<', '1.1.1-beta' ), |
|
277
|
|
|
array( '1.1.1-dev', '>', '1.1.1-beta' ), // No special treatment for "dev". |
|
278
|
|
|
array( '1.1.1-alpha.9', '<', '1.1.1-beta.1' ), |
|
279
|
|
|
array( '1.1.1-beta.9', '>', '1.1.1-beta.1' ), |
|
280
|
|
|
array( '1.1.1-beta.9', '==', '1.1.1-beta.9' ), |
|
281
|
|
|
array( '1.1.1-beta.9.1', '>', '1.1.1-beta.9' ), |
|
282
|
|
|
array( '1.1.1-beta.9', '<', '1.1.1-beta.a' ), |
|
283
|
|
|
array( '1.1.1-beta.1a', '>', '1.1.1-beta.9' ), |
|
284
|
|
|
array( '1.1.1-BETA', '<', '1.1.1-beta' ), // Case sensitive. |
|
285
|
|
|
array( '1.1.1-ZETA', '<', '1.1.1-beta' ), // Case sensitive. |
|
286
|
|
|
array( '1.1.1-alpha2', '>', '1.1.1-alpha10' ), // No natural sorting. |
|
287
|
|
|
array( '1.1.1+beta.9.1', '==', '1.1.1+beta.9' ), |
|
288
|
|
|
); |
|
289
|
|
|
} |
|
290
|
|
|
|
|
291
|
|
|
/** |
|
292
|
|
|
* Test firstVersion. |
|
293
|
|
|
* |
|
294
|
|
|
* @dataProvider provideFirstVersion |
|
295
|
|
|
* @param array $extra Extra components. |
|
296
|
|
|
* @param string|InvalidArgumentException $expect Expected result. |
|
297
|
|
|
*/ |
|
298
|
|
View Code Duplication |
public function testFirstVersion( array $extra, $expect ) { |
|
299
|
|
|
$obj = new SemverVersioning( array() ); |
|
|
|
|
|
|
300
|
|
|
|
|
301
|
|
|
if ( $expect instanceof InvalidArgumentException ) { |
|
302
|
|
|
$this->expectException( InvalidArgumentException::class ); |
|
303
|
|
|
$this->expectExceptionMessage( $expect->getMessage() ); |
|
304
|
|
|
$obj->firstVersion( $extra ); |
|
305
|
|
|
} else { |
|
306
|
|
|
$this->assertSame( $expect, $obj->firstVersion( $extra ) ); |
|
307
|
|
|
} |
|
308
|
|
|
} |
|
309
|
|
|
|
|
310
|
|
|
/** |
|
311
|
|
|
* Data provider for testFirstVersion. |
|
312
|
|
|
*/ |
|
313
|
|
View Code Duplication |
public function provideFirstVersion() { |
|
314
|
|
|
return array( |
|
315
|
|
|
'Normal' => array( |
|
316
|
|
|
array(), |
|
317
|
|
|
'0.1.0', |
|
318
|
|
|
), |
|
319
|
|
|
'Some extra' => array( |
|
320
|
|
|
array( 'prerelease' => 'alpha' ), |
|
321
|
|
|
'0.1.0-alpha', |
|
322
|
|
|
), |
|
323
|
|
|
'Invalid prerelease' => array( |
|
324
|
|
|
array( 'prerelease' => 'delta?' ), |
|
325
|
|
|
new InvalidArgumentException( 'Invalid prerelease data' ), |
|
326
|
|
|
), |
|
327
|
|
|
'Invalid buildinfo' => array( |
|
328
|
|
|
array( 'buildinfo' => 'build?' ), |
|
329
|
|
|
new InvalidArgumentException( 'Invalid buildinfo data' ), |
|
330
|
|
|
), |
|
331
|
|
|
); |
|
332
|
|
|
} |
|
333
|
|
|
|
|
334
|
|
|
} |
|
335
|
|
|
|
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignorePhpDoc annotation to the duplicate definition and it will be ignored.