From 3da84727c039cd59c82e1f0dfe9b792d3f36ce7c Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Wed, 6 May 2020 10:30:08 -0400 Subject: Add SemVer corpus tests Only six failures to fix, not bad! --- t/corpus.t | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 t/corpus.t diff --git a/t/corpus.t b/t/corpus.t new file mode 100644 index 0000000..dd74cee --- /dev/null +++ b/t/corpus.t @@ -0,0 +1,98 @@ +#!/usr/bin/perl -w + +# Test the SemVer corpus from https://regex101.com/r/Ly7O1x/3/. + +use strict; +use warnings; +use Test::More tests => 71; +#use Test::More 'no_plan'; + +use FindBin qw($Bin); +use lib "$Bin/../lib"; +use SemVer; + +# Valid Semantic Versions +for my $v (qw( + 0.0.4 + 1.2.3 + 10.20.30 + 1.1.2-prerelease+meta + 1.1.2+meta + 1.1.2+meta-valid + 1.0.0-alpha + 1.0.0-beta + 1.0.0-alpha.beta + 1.0.0-alpha.beta.1 + 1.0.0-alpha.1 + 1.0.0-alpha0.valid + 1.0.0-alpha.0valid + 1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay + 1.0.0-rc.1+build.1 + 2.0.0-rc.1+build.123 + 1.2.3-beta + 10.2.3-DEV-SNAPSHOT + 1.2.3-SNAPSHOT-123 + 1.0.0 + 2.0.0 + 1.1.7 + 2.0.0+build.1848 + 2.0.1-alpha.1227 + 1.0.0-alpha+beta + 1.2.3----RC-SNAPSHOT.12.9.1--.12+788 + 1.2.3----R-S.12.9.1--.12+meta + 1.2.3----RC-SNAPSHOT.12.9.1--.12 + 1.0.0+0.build.1-rc.10000aaa-kk-0.1 + 99999999999999999999999.999999999999999999.99999999999999999 + 1.0.0-0A.is.legal +)) { + local $@; + ok eval { SemVer->new($v) }, "$v should be valid" or diag $@; +} + +# Invalid Semantic Versions +for my $bv (qw( + 1 + 1.2 + 1.2.3-0123 + 1.2.3-0123.0123 + 1.1.2+.123 + +invalid + -invalid + -invalid+invalid + -invalid.01 + alpha + alpha.beta + alpha.beta.1 + alpha.1 + alpha+beta + alpha_beta + alpha. + alpha.. + beta + 1.0.0-alpha_beta + -alpha. + 1.0.0-alpha.. + 1.0.0-alpha..1 + 1.0.0-alpha...1 + 1.0.0-alpha....1 + 1.0.0-alpha.....1 + 1.0.0-alpha......1 + 1.0.0-alpha.......1 + 01.1.1 + 1.01.1 + 1.1.01 + 1.2 + 1.2.3.DEV + 1.2-SNAPSHOT + 1.2.31.2.3----RC-SNAPSHOT.12.09.1--..12+788 + 1.2-RC-SNAPSHOT + -1.0.3-gamma+b7718 + +justmeta + 9.8.7+meta+meta + 9.8.7-whatever+meta+meta + 99999999999999999999999.999999999999999999.99999999999999999----RC-SNAPSHOT.12.09.1--------------------------------..12 +)) { + local $@; + eval { SemVer->new($bv) }; + ok $@, qq{"$bv" should be an invalid semver}; +} -- cgit v1.2.3 From bd86e170ac9f50c3313f4c5b5fae27d4301f8ab5 Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Fri, 8 May 2020 12:50:00 -0400 Subject: Use official regex for strict parsing Gets the full corpus passing except for one that overflows Perl versions, and therefor is not allowed in Perl versions. May or may not want to change that at some point, so make it a TODO test. --- lib/SemVer.pm | 26 ++++++++++++++++---------- t/base.t | 2 +- t/corpus.t | 14 +++++++++++--- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/lib/SemVer.pm b/lib/SemVer.pm index 97cc321..57eb2ca 100644 --- a/lib/SemVer.pm +++ b/lib/SemVer.pm @@ -43,21 +43,27 @@ sub new { if (eval { $ival->isa('version') }) { my $self = $class->SUPER::new($ival); $self->{extra} = $ival->{extra}; - $self->{dash} = $ival->{dash}; - $self->_evalPreRelease($self->{extra}); + $self->{patch} = $ival->{patch}; + $self->{prerelease} = $ival->{prerelease}; return $self; } - my ($val, $dash, $extra) = ( - $ival =~ /^v?($STRICT_DOTTED_INTEGER_VERSION)(?:($DASH_SEPARATOR)($OPTIONAL_EXTRA_PART))?$/ + # Regex taken from https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string. + my ($major, $minor, $patch, $prerelease, $meta) = ( + $ival =~ /^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/ ); _die qq{Invalid semantic version string format: "$ival"} - unless defined $val; + unless defined $major; - my $self = $class->SUPER::new($val); - $self->{dash} = $dash; - $self->{extra} = $extra; - $self->_evalPreRelease($self->{extra}); + my $self = $class->SUPER::new("$major.$minor.$patch"); + if (defined $prerelease) { + $self->{extra} = "-$prerelease"; + @{$self->{prerelease}} = split $DOT_SEPARATOR, $prerelease; + } + if (defined $meta) { + $self->{extra} .= "+$meta"; + @{$self->{patch}} = split $DOT_SEPARATOR, $meta; + } return $self; } @@ -122,7 +128,7 @@ sub normal { my $self = shift; (my $norm = $self->SUPER::normal) =~ s/^v//; $norm =~ s/_/./g; - return $norm . ($self->{extra} ? "-$self->{extra}" : ''); + return $norm . ($self->{extra} || ''); } sub numify { _die 'Semantic versions cannot be numified'; } diff --git a/t/base.t b/t/base.t index 17a4e3c..d695bff 100644 --- a/t/base.t +++ b/t/base.t @@ -45,7 +45,7 @@ for my $v (qw( v1.2.2 999993333.0.0 )) { - isa_ok my $semver =$CLASS->new($v), $CLASS, "new($v)"; + isa_ok my $semver = $CLASS->new($v), $CLASS, "new($v)"; my $str = $v =~ /^v/ ? substr $v, 1 : $v; is "$semver", $str, qq{$v should stringify to "$str"}; $str =~ s/(\d)([a-z].+)$/$1-$2/; diff --git a/t/corpus.t b/t/corpus.t index dd74cee..b35d11d 100644 --- a/t/corpus.t +++ b/t/corpus.t @@ -4,7 +4,7 @@ use strict; use warnings; -use Test::More tests => 71; +use Test::More tests => 102; #use Test::More 'no_plan'; use FindBin qw($Bin); @@ -42,11 +42,19 @@ for my $v (qw( 1.2.3----R-S.12.9.1--.12+meta 1.2.3----RC-SNAPSHOT.12.9.1--.12 1.0.0+0.build.1-rc.10000aaa-kk-0.1 - 99999999999999999999999.999999999999999999.99999999999999999 1.0.0-0A.is.legal )) { local $@; - ok eval { SemVer->new($v) }, "$v should be valid" or diag $@; + ok my $sv = eval { SemVer->new($v) }, qq{"$v" should be valid} or diag $@; + is $sv->stringify, $v, qq{Should stringify to "$v"}; +} + +SKIP: { + local $TODO = 'Large versions overflow version.pm integer bounds'; + local $SIG{__WARN__} = sub { }; # Ignore version overflow warning + my $v = '99999999999999999999999.999999999999999999.99999999999999999'; + ok my $sv = eval { SemVer->new($v) }, qq{"$v" should be valid}; + is $sv->stringify, $v, qq{Should stringify to "$v"}; } # Invalid Semantic Versions -- cgit v1.2.3 From be04763dbc965ed160540a3513ff941cbcf123b0 Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Fri, 8 May 2020 14:16:51 -0400 Subject: Update declare and parse from official regex. Simplifies the code quite a lot. Also we restore support for prerelease and build metadata parts. --- Changes | 6 ++-- lib/SemVer.pm | 88 ++++++++++++++++++++--------------------------------------- t/base.t | 16 +++++------ t/corpus.t | 10 +++++-- 4 files changed, 48 insertions(+), 72 deletions(-) diff --git a/Changes b/Changes index fc173c3..9b07e0e 100644 --- a/Changes +++ b/Changes @@ -1,9 +1,9 @@ Revision history for Perl extension SemVer. 0.7.0 2018-07-24T11:09:17Z - - Implemented Semantic Versioning 2.0.0 specification - - Updated tests for Semantic Versioning 2.0.0 - - added testing for Perl 5.24, 5.26, 5.28 + - Implemented Semantic Versioning 2.0.0 specification + - Updated tests for Semantic Versioning 2.0.0 + - added testing for Perl 5.24, 5.26, 5.28 0.6.0 2015-01-23T05:07:58Z - Removed tests that fail on version.pm 0.9910 and higher due to diff --git a/lib/SemVer.pm b/lib/SemVer.pm index 57eb2ca..e8191c1 100644 --- a/lib/SemVer.pm +++ b/lib/SemVer.pm @@ -19,20 +19,6 @@ sub _die { require Carp; Carp::croak(@_) } # Prevent version.pm from mucking with our internals. sub import {} -# Adapted from version.pm. -my $STRICT_INTEGER_PART = qr/0|[1-9][0-9]*/; -my $DOT_SEPARATOR = qr/\./; -my $PLUS_SEPARATOR = qr/\+/; -my $DASH_SEPARATOR = qr/-/; -my $STRICT_DOTTED_INTEGER_PART = qr/$DOT_SEPARATOR$STRICT_INTEGER_PART/; -my $STRICT_DOTTED_INTEGER_VERSION = qr/ $STRICT_INTEGER_PART $STRICT_DOTTED_INTEGER_PART{2,} /x; -my $IDENTIFIER = qr/[-0-9A-Za-z]+/; -my $DOTTED_IDENTIFIER = qr/(?:$DOT_SEPARATOR$IDENTIFIER)*/; -my $PRERELEASE = qr/$IDENTIFIER$DOTTED_IDENTIFIER/; -my $METADATA = qr/$IDENTIFIER$DOTTED_IDENTIFIER/; - -my $OPTIONAL_EXTRA_PART = qr/$PRERELEASE($PLUS_SEPARATOR$METADATA)?/; - sub new { my ($class, $ival) = @_; @@ -55,65 +41,50 @@ sub new { _die qq{Invalid semantic version string format: "$ival"} unless defined $major; - my $self = $class->SUPER::new("$major.$minor.$patch"); - if (defined $prerelease) { - $self->{extra} = "-$prerelease"; - @{$self->{prerelease}} = split $DOT_SEPARATOR, $prerelease; + + return _init($class->SUPER::new("$major.$minor.$patch"), $prerelease, $meta); +} + +sub _init { + my ($self, $pre, $meta) = @_; + if (defined $pre) { + $self->{extra} = "-$pre"; + @{$self->{prerelease}} = split /[.]/, $pre; } if (defined $meta) { $self->{extra} .= "+$meta"; - @{$self->{patch}} = split $DOT_SEPARATOR, $meta; + @{$self->{patch}} = split /[.]/, $meta; } return $self; } -# Internal function to split up given string into prerelease- and patch-components -sub _evalPreRelease { - no warnings 'uninitialized'; - my $self = shift; - my $v = shift; - my ($preRelease, $plus, $patch) = ( - $v =~ /^($PRERELEASE)(?:($PLUS_SEPARATOR)($METADATA))?$/ - ); - @{$self->{prerelease}} = split $DOT_SEPARATOR,$preRelease; - $self->{plus} = $plus; - @{$self->{patch}} = (split $DOT_SEPARATOR, $patch || undef); - return; -} - $VERSION = __PACKAGE__->new($VERSION); # For ourselves. -sub declare { - my ($class, $ival) = @_; +sub _lenient { + my ($class, $ctor, $ival) = @_; return $class->new($ival) if Scalar::Util::isvstring($ival) or eval { $ival->isa('version') }; - (my $v = $ival) =~ s/^v?$STRICT_DOTTED_INTEGER_VERSION(?:($DASH_SEPARATOR)($OPTIONAL_EXTRA_PART))[[:space:]]*$//; - my $dash = $1; - my $extra = $2; - $v += 0 if $v =~ s/_//g; # ignore underscores. - my $self = $class->SUPER::declare($v); - $self->{dash} = $dash; - $self->{extra} = $extra; - $self->_evalPreRelease($self->{extra}); - return $self; + # Use official regex for prerelease and meta, use more lenient version num matching and whitespace. + my ($v, $prerelease, $meta) = ( + $ival =~ /^[[:space:]]* + v?([\d_]+(?:\.[\d_]+(?:\.[\d_]+)?)?) + (?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))? + [[:space:]]*$/x + ); + $v += 0 if $v && $v =~ s/_//g; # ignore underscores. + my $code = $class->can("SUPER::$ctor"); + return _init($code->($class, $v), $prerelease, $meta); + } -sub parse { - my ($class, $ival) = @_; - return $class->new($ival) if Scalar::Util::isvstring($ival) - or eval { $ival->isa('version') }; +sub declare { + shift->_lenient('declare', @_); +} - (my $v = $ival) =~ s/^v?$STRICT_DOTTED_INTEGER_VERSION(?:($DASH_SEPARATOR)($OPTIONAL_EXTRA_PART))[[:space:]]*$//; - my $dash = $1; - my $extra = $2; - $v += 0 if $v =~ s/_//g; # ignore underscores. - my $self = $class->SUPER::parse($v); - $self->{dash} = $dash; - $self->{extra} = $extra; - $self->_evalPreRelease($self->{extra}); - return $self; +sub parse { + shift->_lenient('parse', @_); } sub stringify { @@ -134,7 +105,6 @@ sub normal { sub numify { _die 'Semantic versions cannot be numified'; } sub is_alpha { !!shift->{extra} } - # Sort Ordering: # Precedence refers to how versions are compared to each other when ordered. Precedence MUST be calculated by # separating the version into major, minor, patch and pre-release identifiers in that order (Build metadata does not figure into precedence). @@ -289,7 +259,7 @@ shown as returned by C: ' 012.2.2' | | 12.2.2 | 12.2.2 '1.1' | | 1.1.0 | 1.100.0 1.1 | | 1.1.0 | 1.100.0 - '1.1.0b1' | | 1.1.0-b1 | 1.1.0-b1 + '1.1.0-b1' | 1.1.0-b1 | 1.1.0-b1 | 1.1.0-b1 '1.1-b1' | | 1.1.0-b1 | 1.100.0-b1 '1.2.b1' | | 1.2.0-b1 | 1.2.0-b1 '9.0-beta4' | | 9.0.0-beta4 | 9.0.0-beta4 diff --git a/t/base.t b/t/base.t index d695bff..424ec5f 100644 --- a/t/base.t +++ b/t/base.t @@ -2,7 +2,7 @@ use strict; use warnings; -use Test::More tests => 610; +use Test::More tests => 666; #use Test::More 'no_plan'; use FindBin qw($Bin); @@ -230,15 +230,15 @@ for my $spec ( ['01.2.2', '1.2.2'], ['1.02.2', '1.2.2'], ['1.2.02', '1.2.2'], -# ['1.2.02b', '1.2.2-b'], -# ['1.2.02beta-3 ', '1.2.2-beta-3'], -# ['1.02.02rc1', '1.2.2-rc1'], + ['1.2.02-b', '1.2.2-b'], + ['1.2.02-beta-3 ', '1.2.2-beta-3'], + ['1.02.02-rc1', '1.2.2-rc1'], ['1.0', '1.0.0'], ['1.1', '1.1.0', '1.100.0'], [ 1.1, '1.1.0', '1.100.0'], -# ['1.1b1', '1.1.0-b1', '1.100.0-b1'], -# ['1b', '1.0.0-b'], -# ['9.0beta4', '9.0.0-beta4'], + ['1.1-b1', '1.1.0-b1', '1.100.0-b1'], + ['1-b', '1.0.0-b'], + ['9.0-beta4', '9.0.0-beta4'], [' 012.2.2', '12.2.2'], ['99999998', '99999998.0.0'], ['1.02_30', '1.23.0'], @@ -251,7 +251,7 @@ for my $spec ( ['9', '9.0.0' ], ['0', '0.0.0' ], [0, '0.0.0' ], -# ['0rc1', '0.0.0-rc1' ], + ['0-rc1', '0.0.0-rc1' ], ) { SKIP: { skip 'Two-integer vstrings weak on Perl 5.8', 12 if $no_2digitvst && Scalar::Util::isvstring($spec->[0]); diff --git a/t/corpus.t b/t/corpus.t index b35d11d..d5dbcb2 100644 --- a/t/corpus.t +++ b/t/corpus.t @@ -4,7 +4,7 @@ use strict; use warnings; -use Test::More tests => 102; +use Test::More tests => 222; #use Test::More 'no_plan'; use FindBin qw($Bin); @@ -45,7 +45,13 @@ for my $v (qw( 1.0.0-0A.is.legal )) { local $@; - ok my $sv = eval { SemVer->new($v) }, qq{"$v" should be valid} or diag $@; + ok my $sv = eval { SemVer->new($v) }, qq{New "$v" should be valid} or diag $@; + is $sv->stringify, $v, qq{Should stringify to "$v"}; + + ok $sv = eval { SemVer->declare($v) }, qq{Declare "$v" should work} or diag $@; + is $sv->stringify, $v, qq{Should stringify to "$v"}; + + ok $sv = eval { SemVer->parse($v) }, qq{Parse "$v" should work} or diag $@; is $sv->stringify, $v, qq{Should stringify to "$v"}; } -- cgit v1.2.3 From 8ac853be00e3aff88d6b1a7a0d64022210058697 Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Fri, 8 May 2020 14:21:38 -0400 Subject: Increment to v0.10.0 --- Changes | 2 ++ README.md | 4 ++-- lib/SemVer.pm | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Changes b/Changes index 9b07e0e..fccc736 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,7 @@ Revision history for Perl extension SemVer. +0.10.0 + 0.7.0 2018-07-24T11:09:17Z - Implemented Semantic Versioning 2.0.0 specification - Updated tests for Semantic Versioning 2.0.0 diff --git a/README.md b/README.md index a6a1438..828737a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -SemVer version 0.7.0 -==================== +SemVer version 0.10.0 +===================== This module subclasses [`version`] to create semantic versions, as defined by the [Semantic Versioning 2.0.0 Specification] diff --git a/lib/SemVer.pm b/lib/SemVer.pm index e8191c1..7661f3c 100644 --- a/lib/SemVer.pm +++ b/lib/SemVer.pm @@ -12,7 +12,7 @@ use overload ( ); our @ISA = qw(version); -our $VERSION = '0.7.0'; # For Module::Build +our $VERSION = '0.10.0'; # For Module::Build sub _die { require Carp; Carp::croak(@_) } -- cgit v1.2.3 From 5ceee34aa4b63dc79e0d3eceb2461379568bcc63 Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Fri, 8 May 2020 14:34:38 -0400 Subject: Record changes --- Changes | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Changes b/Changes index fccc736..a4718b2 100644 --- a/Changes +++ b/Changes @@ -1,6 +1,20 @@ Revision history for Perl extension SemVer. 0.10.0 + - Adopted the official regular expression from the SemVer FAQ for strict + parsing by new(), as well as a modification of that regex for the more + lenient cases supported by declare() and new(). This results in the + following changes in the behavior of the parser: + + SemVers with build metadata but no prerelease are now valid, e.g. + `1.1.2+meta` + + SemVers with a numeric-only prerelease part are no longer valid + if that part has a leading zero, e.g., `1.2.3-123` is valid but + `1.2.3-0123` is not + + Restored support for prerelease and build metadata parts are in + declare() and parse() + - Added tests for the official SemVer test corpus as linked under the + FAQ about an official regular expression: + https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string 0.7.0 2018-07-24T11:09:17Z - Implemented Semantic Versioning 2.0.0 specification @@ -33,7 +47,7 @@ Revision history for Perl extension SemVer. 0.3.0 2011-05-26T04:54:50 - Made leading zeros, such as the "04" in "1.04.3" illegal when parsing via `new()`. - - Eliminted "Use of qw(...) as parentheses is deprecated" in the tests + - Eliminated "Use of qw(...) as parentheses is deprecated" in the tests when running on Perl 5.14. 0.2.0 2010-09-17T17:59:57 -- cgit v1.2.3 From 6235ed1a61b4f1b58ff584a5e7e827ceb0f29292 Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Fri, 8 May 2020 16:58:46 -0400 Subject: Drop support for Perl 5.8 --- .github/workflows/ci.yml | 2 +- Build.PL | 2 +- Changes | 1 + lib/SemVer.pm | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bf173f9..5394a19 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: strategy: matrix: os: ['ubuntu-latest', 'macos-latest'] - perl: [ '5.18', '5.12', '5.10', '5.8'] + perl: [ '5.18', '5.12', '5.10'] name: Perl ${{ matrix.perl }} on ${{ matrix.os }} runs-on: ${{ matrix.os }} steps: diff --git a/Build.PL b/Build.PL index acf9d51..c3310df 100644 --- a/Build.PL +++ b/Build.PL @@ -14,7 +14,7 @@ Module::Build->new( }, requires => { 'version' => 0.82, - 'perl' => 5.008001, + 'perl' => 5.010000, }, recommends => { 'Test::Pod' => '1.41', diff --git a/Changes b/Changes index a4718b2..aaa9dc0 100644 --- a/Changes +++ b/Changes @@ -15,6 +15,7 @@ Revision history for Perl extension SemVer. - Added tests for the official SemVer test corpus as linked under the FAQ about an official regular expression: https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string + - Dropped support for Perl 5.10. 0.7.0 2018-07-24T11:09:17Z - Implemented Semantic Versioning 2.0.0 specification diff --git a/lib/SemVer.pm b/lib/SemVer.pm index 7661f3c..9a253df 100644 --- a/lib/SemVer.pm +++ b/lib/SemVer.pm @@ -1,6 +1,6 @@ package SemVer; -use 5.008001; +use 5.010000; use strict; use version 0.82; use Scalar::Util (); -- cgit v1.2.3 From 118c51cad8319ae681edd7f2aae7d688139505cd Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Fri, 8 May 2020 18:22:09 -0400 Subject: Restore 5.8 support and work around version issues version::vpp has some divergences from how the XS version works. But rather than detect vpp in the inclue path, detect the variants in behavior and hack around them, instead. Also, have parse() and declare() return errors on invalid versions. --- .github/workflows/ci.yml | 2 +- Build.PL | 2 +- Changes | 1 - lib/SemVer.pm | 8 +++++--- t/base.t | 33 ++++++++++++++++++++------------- t/corpus.t | 9 +++++++++ 6 files changed, 36 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5394a19..bf173f9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: strategy: matrix: os: ['ubuntu-latest', 'macos-latest'] - perl: [ '5.18', '5.12', '5.10'] + perl: [ '5.18', '5.12', '5.10', '5.8'] name: Perl ${{ matrix.perl }} on ${{ matrix.os }} runs-on: ${{ matrix.os }} steps: diff --git a/Build.PL b/Build.PL index c3310df..acf9d51 100644 --- a/Build.PL +++ b/Build.PL @@ -14,7 +14,7 @@ Module::Build->new( }, requires => { 'version' => 0.82, - 'perl' => 5.010000, + 'perl' => 5.008001, }, recommends => { 'Test::Pod' => '1.41', diff --git a/Changes b/Changes index aaa9dc0..a4718b2 100644 --- a/Changes +++ b/Changes @@ -15,7 +15,6 @@ Revision history for Perl extension SemVer. - Added tests for the official SemVer test corpus as linked under the FAQ about an official regular expression: https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string - - Dropped support for Perl 5.10. 0.7.0 2018-07-24T11:09:17Z - Implemented Semantic Versioning 2.0.0 specification diff --git a/lib/SemVer.pm b/lib/SemVer.pm index 9a253df..9ba0994 100644 --- a/lib/SemVer.pm +++ b/lib/SemVer.pm @@ -1,6 +1,6 @@ package SemVer; -use 5.010000; +use 5.008001; use strict; use version 0.82; use Scalar::Util (); @@ -41,7 +41,6 @@ sub new { _die qq{Invalid semantic version string format: "$ival"} unless defined $major; - return _init($class->SUPER::new("$major.$minor.$patch"), $prerelease, $meta); } @@ -73,10 +72,13 @@ sub _lenient { (?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))? [[:space:]]*$/x ); + + _die qq{Invalid semantic version string format: "$ival"} + unless defined $v; + $v += 0 if $v && $v =~ s/_//g; # ignore underscores. my $code = $class->can("SUPER::$ctor"); return _init($code->($class, $v), $prerelease, $meta); - } sub declare { diff --git a/t/base.t b/t/base.t index 424ec5f..4e930d9 100644 --- a/t/base.t +++ b/t/base.t @@ -12,6 +12,14 @@ my $CLASS; BEGIN { $CLASS = 'SemVer'; use_ok $CLASS or die; + + unless (eval { my $x = !$CLASS->new('1.0.0') }) { + # Borked version:vpp. Overload bool. + diag 'here'; + $CLASS->overload::OVERLOAD(bool => sub { + version::vcmp(shift, $CLASS->declare('0.0.0'), 1); + }); + } } diag 'Testing with version v', version->VERSION; @@ -29,7 +37,6 @@ can_ok $CLASS, qw( # Try the basics. isa_ok my $version = $CLASS->new('0.1.0'), $CLASS, 'An instance'; isa_ok $SemVer::VERSION, $CLASS, q{SemVer's own $VERSION}; -my $is_vpp = !!grep { $_ eq 'version::vpp' } @version::ISA; for my $v (qw( 1.2.2 @@ -51,13 +58,10 @@ for my $v (qw( $str =~ s/(\d)([a-z].+)$/$1-$2/; is $semver->normal, $str, qq{$v should normalize to "$str"}; - SKIP: { - skip 'Boolean comparison broken with version::vpp', 1, $is_vpp; - if ($v =~ /0\.0\.0/) { - ok !$semver, "$v should be false"; - } else { - ok !!$semver, "$v should be true"; - } + if ($v =~ /0\.0\.0/) { + ok !$semver, "$v should be false"; + } else { + ok !!$semver, "$v should be true"; } ok $semver->is_qv, "$v should be dotted-decimal"; @@ -224,6 +228,12 @@ for my $v (qw( cmp_ok $v, 'eq', $semver, qq{"$v" eq $semver}; } +# Tweak tweak v prefix regex? Some versions of version:vpp do it differently. +my $vq = qr/^\d+[.][^.]+$/; +if ($CLASS->declare('0')->stringify eq 'v0') { + $vq = qr/^\d+([.]?[^.]+)?$/; +} + # Test declare() and parse. for my $spec ( ['1.2.2', '1.2.2'], @@ -262,11 +272,8 @@ for my $spec ( $string =~ s/^\s+//; $string =~ s/\s+$//; $string += 0 if $string =~ s/_//g; - my $vstring = $string =~ /^\d+[.][^.]+$/ ? "v$string" : $string; - SKIP: { - skip 'Stringification broken with version::vpp', 1, $is_vpp; - is $l->stringify, $vstring, qq{... And it should stringify to "$vstring"}; - } + my $vstring = $string =~ $vq ? "v$string" : $string; + is $l->stringify, $vstring, qq{... And it should stringify to "$vstring"}; is $l->normal, $spec->[1], qq{... And it should normalize to "$spec->[1]"}; # Compare the non-semantic version string to the semantic one. diff --git a/t/corpus.t b/t/corpus.t index d5dbcb2..fd21afd 100644 --- a/t/corpus.t +++ b/t/corpus.t @@ -11,6 +11,15 @@ use FindBin qw($Bin); use lib "$Bin/../lib"; use SemVer; +BEGIN { + unless (eval { my $x = !SemVer->new('1.0.0') }) { + # Borked version:vpp. Overload bool. + SemVer->overload::OVERLOAD(bool => sub { + version::vcmp(shift, SemVer->declare('0.0.0'), 1); + }); + } +} + # Valid Semantic Versions for my $v (qw( 0.0.4 -- cgit v1.2.3 From 0023a806e96670723c7475687fe3e5ffcfd9a378 Mon Sep 17 00:00:00 2001 From: "David E. Wheeler" Date: Fri, 8 May 2020 18:41:53 -0400 Subject: Explicitly overload bool operations Some implementations of version::vpp sadly raise an error by calling `SemVer->new("0")`, which is not a strictly valid SemVer. So overload it to compare to `SemVer->parse("0.0.0")`, instead, to ensure consistent behavior. --- Changes | 2 ++ lib/SemVer.pm | 13 +++++++++++++ t/base.t | 8 -------- t/corpus.t | 9 --------- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/Changes b/Changes index a4718b2..f38a336 100644 --- a/Changes +++ b/Changes @@ -15,6 +15,8 @@ Revision history for Perl extension SemVer. - Added tests for the official SemVer test corpus as linked under the FAQ about an official regular expression: https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string + - Added explicit boolean overloading to ensure consistent behavior + between different implementations of the version parent class. 0.7.0 2018-07-24T11:09:17Z - Implemented Semantic Versioning 2.0.0 specification diff --git a/lib/SemVer.pm b/lib/SemVer.pm index 9ba0994..9a7f3c9 100644 --- a/lib/SemVer.pm +++ b/lib/SemVer.pm @@ -9,6 +9,7 @@ use overload ( '""' => 'stringify', '<=>' => 'vcmp', 'cmp' => 'vcmp', + 'bool' => 'vbool', ); our @ISA = qw(version); @@ -106,6 +107,10 @@ sub normal { sub numify { _die 'Semantic versions cannot be numified'; } sub is_alpha { !!shift->{extra} } +sub vbool { + my $self = shift; + return version::vcmp($self, $self->declare("0.0.0"), 1); +} # Sort Ordering: # Precedence refers to how versions are compared to each other when ordered. Precedence MUST be calculated by @@ -356,6 +361,14 @@ Returns true if an ASCII string is appended to the end of the version string. This also means that the version number is a "special version", in the semantic versioning specification meaning of the phrase. +=head3 C + + say "Version $semver" if $semver; + say "Not a $semver" if !$semver; + +Returns true for a non-zero semantic semantic version object, without regard +to the prerelease or build metadata parts. Overload boolean operations. + =head3 C Compares the semantic version object to another version object or string and diff --git a/t/base.t b/t/base.t index 4e930d9..b0d3812 100644 --- a/t/base.t +++ b/t/base.t @@ -12,14 +12,6 @@ my $CLASS; BEGIN { $CLASS = 'SemVer'; use_ok $CLASS or die; - - unless (eval { my $x = !$CLASS->new('1.0.0') }) { - # Borked version:vpp. Overload bool. - diag 'here'; - $CLASS->overload::OVERLOAD(bool => sub { - version::vcmp(shift, $CLASS->declare('0.0.0'), 1); - }); - } } diag 'Testing with version v', version->VERSION; diff --git a/t/corpus.t b/t/corpus.t index fd21afd..d5dbcb2 100644 --- a/t/corpus.t +++ b/t/corpus.t @@ -11,15 +11,6 @@ use FindBin qw($Bin); use lib "$Bin/../lib"; use SemVer; -BEGIN { - unless (eval { my $x = !SemVer->new('1.0.0') }) { - # Borked version:vpp. Overload bool. - SemVer->overload::OVERLOAD(bool => sub { - version::vcmp(shift, SemVer->declare('0.0.0'), 1); - }); - } -} - # Valid Semantic Versions for my $v (qw( 0.0.4 -- cgit v1.2.3