SemVer-v0.10.0000755000765000024 013655533343 12544 5ustar00davidstaff000000000000SemVer-v0.10.0/Build.PL000444000765000024 146313655533343 14201 0ustar00davidstaff000000000000use strict; use warnings; use Module::Build; Module::Build->new( module_name => 'SemVer', license => 'perl', configure_requires => { 'Module::Build' => '0.30', }, build_requires => { 'Module::Build' => '0.30', 'Test::More' => '0.88', }, requires => { 'version' => 0.82, 'perl' => 5.008001, }, recommends => { 'Test::Pod' => '1.41', 'Test::Pod::Coverage' => '1.06', }, meta_merge => { resources => { homepage => 'https://metacpan.org/release/SemVer', bugtracker => 'https://github.com/theory/semver/issues/', repository => 'https://github.com/theory/semver', } }, )->create_build_script; SemVer-v0.10.0/Changes000444000765000024 555413655533343 14205 0ustar00davidstaff000000000000Revision history for Perl extension SemVer. 0.10.0 2020-05-09T13:58:26Z - 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 - 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 - 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 trailing decimal sign. Thanks to Andreas Koenig for the report and diagnosis. - Fixed vstring warning on Perl 5.8. - Now skip tests that fail with version::vpp. - Now skip test on version.pm 0.9911 that fails with that version. - Now skip failing tests on Perl 5.8 and version.pm less than 0.9909. - Removed all Pod tests from the distribution. 0.5.0 2013-04-02T06:57:06Z - The `new()` now throws an exception when a version string has a prerelease version with no preceding dash. This is keep its requirement for strictness consistent. - Fixed a bug where a SemVer object passed to `new()` did not properly clone the dash preceding a prerelease version. 0.4.0 2012-11-20T18:59:33Z - Updated the parsers to support an optional dash before the prerelease version, and `normal()` to always emit a dash for the prerelease version. This brings it in line with the final semver 1.0.0 specification. 0.3.0 2011-05-26T04:54:50 - Made leading zeros, such as the "04" in "1.04.3" illegal when parsing via `new()`. - 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 - Require Test::Pod 1.41 for testing POD because it supports `L`. 0.1.0 2010-09-16T19:07:04 - Initial version, created with lots of help and feedback from version.pm author John Peacock SemVer-v0.10.0/MANIFEST000444000765000024 15613655533343 14014 0ustar00davidstaff000000000000Build.PL Changes lib/SemVer.pm MANIFEST This list of files README.md t/base.t t/corpus.t META.yml META.json SemVer-v0.10.0/META.json000444000765000024 256513655533343 14332 0ustar00davidstaff000000000000{ "abstract" : "Use semantic version numbers", "author" : [ "unknown" ], "dynamic_config" : 1, "generated_by" : "Module::Build version 0.4231", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "SemVer", "prereqs" : { "build" : { "requires" : { "Module::Build" : "0.30", "Test::More" : "0.88" } }, "configure" : { "requires" : { "Module::Build" : "0.30" } }, "runtime" : { "recommends" : { "Test::Pod" : "1.41", "Test::Pod::Coverage" : "1.06" }, "requires" : { "perl" : "5.008001", "version" : "0.82" } } }, "provides" : { "SemVer" : { "file" : "lib/SemVer.pm", "version" : "v0.10.0" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/theory/semver/issues/" }, "homepage" : "https://metacpan.org/release/SemVer", "license" : [ "http://dev.perl.org/licenses/" ], "repository" : { "url" : "https://github.com/theory/semver" } }, "version" : "v0.10.0", "x_serialization_backend" : "JSON::PP version 4.02" } SemVer-v0.10.0/META.yml000444000765000024 151213655533343 14151 0ustar00davidstaff000000000000--- abstract: 'Use semantic version numbers' author: - unknown build_requires: Module::Build: '0.30' Test::More: '0.88' configure_requires: Module::Build: '0.30' dynamic_config: 1 generated_by: 'Module::Build version 0.4231, CPAN::Meta::Converter version 2.150010' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: SemVer provides: SemVer: file: lib/SemVer.pm version: v0.10.0 recommends: Test::Pod: '1.41' Test::Pod::Coverage: '1.06' requires: perl: '5.008001' version: '0.82' resources: bugtracker: https://github.com/theory/semver/issues/ homepage: https://metacpan.org/release/SemVer license: http://dev.perl.org/licenses/ repository: https://github.com/theory/semver version: v0.10.0 x_serialization_backend: 'CPAN::Meta::YAML version 0.018' SemVer-v0.10.0/README.md000444000765000024 710413655533343 14162 0ustar00davidstaff000000000000SemVer version 0.10.0 ===================== This module subclasses [`version`] to create semantic versions, as defined by the [Semantic Versioning 2.0.0 Specification] The salient points of the specification, for the purposes of version formatting, are: 1. A normal version number MUST take the form X.Y.Z where X, Y, and Z are non-negative integers, and MUST NOT contain leading zeroes. X is the major version, Y is the minor version, and Z is the patch version. Each element MUST increase numerically. For instance: `1.9.0 < 1.10.0 < 1.11.0`. 2. A pre-release version MAY be denoted by appending a hyphen and a series of dot separated identifiers immediately following the patch version. Identifiers MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-]. Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes. Pre-release versions have a lower precedence than the associated normal version. A pre-release version indicates that the version is unstable and might not satisfy the intended compatibility requirements as denoted by its associated normal version. Examples: `1.0.0-alpha, 1.0.0-alpha.1, 1.0.0-0.3.7, 1.0.0-x.7.z.92`. 3. Build metadata MAY be denoted by appending a plus sign and a series of dot separated identifiers immediately following the patch or pre-release version. Identifiers MUST comprise only ASCII alphanumerics and hyphen [0-9A-Za-z-]. Identifiers MUST NOT be empty. Build metadata SHOULD be ignored when determining version precedence. Thus two versions that differ only in the build metadata, have the same precedence. Examples: `1.0.0-alpha+001, 1.0.0+20130313144700, 1.0.0-beta+exp.sha.5114f85`. 4. 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). Precedence is determined by the first difference when comparing each of these identifiers from left to right as follows: Major, minor, and patch versions are always compared numerically. Example: `1.0.0 < 2.0.0 < 2.1.0 < 2.1.1`. When major, minor, and patch are equal, a pre-release version has lower precedence than a normal version. Example: `1.0.0-alpha < 1.0.0`. Precedence for two pre-release versions with the same major, minor, and patch version MUST be determined by comparing each dot separated identifier from left to right until a difference is found as follows: identifiers consisting of only digits are compared numerically and identifiers with letters or hyphens are compared lexically in ASCII sort order. Numeric identifiers always have lower precedence than non-numeric identifiers. A larger set of pre-release fields has a higher precedence than a smaller set, if all of the preceding identifiers are equal. Example: `1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0`. [`version`]: https://metacpan.org/pod/version [Semantic Versioning 2.0.0 Specification]: https://semver.org/spec/v2.0.0.html Installation ============ To install this module, type the following: perl Build.PL ./Build ./Build test ./Build install Dependencies ------------ SemVer requires version. Copyright and License --------------------- Copyright (c) 2010-2020 David E. Wheeler. Some Rights Reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. SemVer-v0.10.0/lib000755000765000024 013655533343 13312 5ustar00davidstaff000000000000SemVer-v0.10.0/lib/SemVer.pm000444000765000024 4175013655533343 15235 0ustar00davidstaff000000000000package SemVer; use 5.008001; use strict; use version 0.82; use Scalar::Util (); use overload ( '""' => 'stringify', '<=>' => 'vcmp', 'cmp' => 'vcmp', 'bool' => 'vbool', ); our @ISA = qw(version); our $VERSION = '0.10.0'; # For Module::Build sub _die { require Carp; Carp::croak(@_) } # Prevent version.pm from mucking with our internals. sub import {} sub new { my ($class, $ival) = @_; # Handle vstring. return $class->SUPER::new($ival) if Scalar::Util::isvstring($ival); # Let version handle cloning. if (eval { $ival->isa('version') }) { my $self = $class->SUPER::new($ival); $self->{extra} = $ival->{extra}; $self->{patch} = $ival->{patch}; $self->{prerelease} = $ival->{prerelease}; return $self; } # 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 $major; 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 /[.]/, $meta; } return $self; } $VERSION = __PACKAGE__->new($VERSION); # For ourselves. sub _lenient { my ($class, $ctor, $ival) = @_; return $class->new($ival) if Scalar::Util::isvstring($ival) or eval { $ival->isa('version') }; # 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 ); _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 { shift->_lenient('declare', @_); } sub parse { shift->_lenient('parse', @_); } sub stringify { my $self = shift; my $str = $self->SUPER::stringify; # This is purely for SemVers constructed from version objects. $str += 0 if $str =~ s/_//g; # ignore underscores. return $str . ($self->{dash} || '') . ($self->{extra} || ''); } sub normal { my $self = shift; (my $norm = $self->SUPER::normal) =~ s/^v//; $norm =~ s/_/./g; return $norm . ($self->{extra} || ''); } sub numify { _die 'Semantic versions cannot be numified'; } sub is_alpha { !!shift->{extra} } sub vbool { my $self = shift; return version::vcmp($self, $self->new("0.0.0"), 1); } # 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). # Precedence is determined by the first difference when comparing each of these identifiers from left to right as follows: # 1. Major, minor, and patch versions are always compared numerically. Example: 1.0.0 < 2.0.0 < 2.1.0 < 2.1.1. # 2. When major, minor, and patch are equal, a pre-release version has lower precedence than a normal version. # Example: 1.0.0-alpha < 1.0.0. # 3. Precedence for two pre-release versions with the same major, minor, and patch version MUST be determined by # comparing each dot separated identifier from left to right until a difference is found as follows: # 3.a. identifiers consisting of only digits are compared numerically and identifiers with letters or hyphens are # compared lexically in ASCII sort order. # 3.b. Numeric identifiers always have lower precedence than non-numeric identifiers. # 3.c. A larger set of pre-release fields has a higher precedence than a smaller set, if all of the preceding identifiers are equal. # Example: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0. sub vcmp { my $left = shift; my $right = ref($left)->declare(shift); # Reverse? ($left, $right) = shift() ? ($right, $left): ($left, $right); # Major and minor win. - case 1. if (my $ret = $left->SUPER::vcmp($right, 0)) { return $ret; } else { #cases 2, 3 my $lenLeft = 0; my $lenRight = 0; if (defined $left->{prerelease}) { $lenLeft = scalar(@{$left->{prerelease}}); } if (defined $right->{prerelease}) { $lenRight = scalar(@{$right->{prerelease}}); } my $lenMin = ($lenLeft, $lenRight)[$lenLeft > $lenRight]; if ( $lenLeft == 0) { if ($lenRight == 0) { return 0; # Neither LEFT nor RIGHT have prerelease identifiers - versions are equal } else { # Case 2: When major, minor, and patch are equal, a pre-release version has lower precedence than a normal version. return 1; # Only RIGHT has prelease - not LEFT -> LEFT wins } } else { if ($lenRight == 0) { # Case 2: When major, minor, and patch are equal, a pre-release version has lower precedence than a normal version. return -1; # Only LEFT has prelease identifiers - not RIGHT -> RIGHT wins } else { # LEFT and RIGHT have prelease identifiers - compare each part separately for (my $i = 0; $i < $lenMin; $i++) { my $isNumLeft = Scalar::Util::looks_like_number($left->{prerelease}->[$i]); my $isNumRight = Scalar::Util::looks_like_number($right->{prerelease}->[$i]); # Case 3.b: Numeric identifiers always have lower precedence than non-numeric identifiers if (!$isNumLeft && $isNumRight) { return 1; # LEFT identifier is Non-numeric - RIGHT identifier is numeric -> LEFT wins } elsif ($isNumLeft && !$isNumRight) { return -1; # LEFT identifier is numeric - RIGHT identifier is non-numeric -> RIGHT wins } elsif ($isNumLeft && $isNumRight) { # Case 3.a.1: identifiers consisting of only digits are compared numerically if ($left->{prerelease}->[$i] == $right->{prerelease}->[$i] ) { next; # LEFT identifier and RIGHT identifier are equal - step to next part } elsif ($left->{prerelease}->[$i] > $right->{prerelease}->[$i] ) { return 1; # LEFT identifier is bigger than RIGHT identifier -> LEFT wins } else { return -1; return 1; # LEFT identifier is smaller than RIGHT identifier -> RIGHT wins } } else { # Case 3.a.2: identifiers with letters or hyphens are compared lexically in ASCII sort order. if (lc $left->{prerelease}->[$i] eq lc $right->{prerelease}->[$i] ) { next; # LEFT identifier and RIGHT identifier are equal - step to next part } elsif (lc $left->{prerelease}->[$i] gt lc $right->{prerelease}->[$i] ) { return 1; # LEFT identifier is bigger than RIGHT identifier -> LEFT wins } else { return -1; return 1; # LEFT identifier is smaller than RIGHT identifier -> RIGHT wins } } } # Case 3.c: A larger set of pre-release fields has a higher precedence than a smaller set, if all of the preceding identifiers are equal if ($lenLeft > $lenRight) { return 1; # All existing identifiers are equal, but LEFT has more identifiers -> LEFT wins } elsif ($lenLeft < $lenRight) { return -1; # All existing identifiers are equal, but RIGHT has more identifiers -> RIGHT wins } # All identifiers are equal return 0; } } } } 1; __END__ =head1 Name SemVer - Use semantic version numbers =head1 Synopsis use SemVer; our $VERSION = SemVer->new('1.2.0-b1'); =head1 Description This module subclasses L to create semantic versions, as defined by the L. The three salient points of the specification, for the purposes of version formatting, are: =over =item 1. A normal version number MUST take the form X.Y.Z where X, Y, and Z are non-negative integers, and MUST NOT contain leading zeroes. X is the major version, Y is the minor version, and Z is the patch version. Each element MUST increase numerically. For instance: C<< 1.9.0 -> 1.10.0 -> 1.11.0 >>. =item 2. A pre-release version MAY be denoted by appending a hyphen and a series of dot separated identifiers immediately following the patch version. Identifiers MUST comprise only ASCII alphanumerics and hyphen C<[0-9A-Za-z-]>. Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes. Pre-release versions have a lower precedence than the associated normal version. A pre-release version indicates that the version is unstable and might not satisfy the intended compatibility requirements as denoted by its associated normal version: C<< 1.0.0-alpha, 1.0.0-alpha.1, 1.0.0-0.3.7, 1.0.0-x.7.z.92 >> =item 3. Build metadata MAY be denoted by appending a plus sign and a series of dot separated identifiers immediately following the patch or pre-release version. Identifiers MUST comprise only ASCII alphanumerics and hyphen C<[0-9A-Za-z-]>. Identifiers MUST NOT be empty. Build metadata SHOULD be ignored when determining version precedence. Thus two versions that differ only in the build metadata, have the same precedence. Examples: C<< 1.0.0-alpha+001, 1.0.0+20130313144700, 1.0.0-beta+exp.sha.5114f85 >>. =back =head2 Usage For strict parsing of semantic version numbers, use the C constructor. If you need something more flexible, use C. And if you need something more comparable with what L expects, try C. Compare how these constructors deal with various version strings (with values shown as returned by C: Argument | new | declare | parse -------------+----------+--------------------------- '1.0.0' | 1.0.0 | 1.0.0 | 1.0.0 '5.5.2-b1' | 5.5.2-b1 | 5.5.2-b1 | 5.5.2-b1 '1.05.0' | | 1.5.0 | 1.5.0 '1.0' | | 1.0.0 | 1.0.0 ' 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.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 '9' | | 9.0.0 | 9.0.0 '1-b' | | 1.0.0-b | 1.0.0-b 0 | | 0.0.0 | 0.0.0 '0-rc1' | | 0.0.0-rc1 | 0.0.0-rc1 '1.02_30' | | 1.23.0 | 1.23.0 1.02_30 | | 1.23.0 | 1.23.0 Note that, unlike in L, the C and C methods ignore underscores. That is, version strings with underscores are treated as decimal numbers. Hence, the last two examples yield exactly the same semantic versions. As with L objects, the comparison and stringification operators are all overloaded, so that you can compare semantic versions. You can also compare semantic versions with version objects (but not the other way around, alas). Boolean operators are also overloaded, such that all semantic version objects except for those consisting only of zeros (ignoring prerelease and metadata) are considered true. =head1 Interface =head2 Constructors =head3 C my $semver = SemVer->new('1.2.2'); Performs a validating parse of the version string and returns a new semantic version object. If the version string does not adhere to the semantic version specification an exception will be thrown. See C and C for more forgiving constructors. =head3 C my $semver = SemVer->declare('1.2'); # 1.2.0 This parser strips out any underscores from the version string and passes it to to C's C constructor, which always creates dotted-integer version objects. This is the most flexible way to declare versions. Consider using it to normalize version strings. =head3 C my $semver = SemVer->parse('1.2'); # 1.200.0 This parser dispatches to C's C constructor, which tries to be more flexible in how it converts simple decimal strings and numbers. Not really recommended, since it's treatment of decimals is quite different from the dotted-integer format of semantic version strings, and thus can lead to inconsistencies. Included only for proper compatibility with L. =head2 Instance Methods =head3 C SemVer->declare('v1.2')->normal; # 1.2.0 SemVer->parse('1.2')->normal; # 1.200.0 SemVer->declare('1.02.0-b1')->normal; # 1.2.0-b1 SemVer->parse('1.02_30')->normal # 1.230.0 SemVer->parse(1.02_30)->normal # 1.23.0 Returns a normalized representation of the version string. This string will always be a strictly-valid dotted-integer semantic version string suitable for passing to C. Unlike L's C method, there will be no leading "v". =head3 C SemVer->declare('v1.2')->stringify; # v1.2 SemVer->parse('1.200')->stringify; # v1.200 SemVer->declare('1.2-r1')->stringify; # v1.2-r1 SemVer->parse(1.02_30)->stringify; # v1.0230 SemVer->parse(1.02_30)->stringify; # v1.023 Returns a string that is as close to the original representation as possible. If the original representation was a numeric literal, it will be returned the way perl would normally represent it in a string. This method is used whenever a version object is interpolated into a string. =head3 C Throws an exception. Semantic versions cannot be numified. Just don't go there. =head3 C my $is_alpha = $semver->is_alpha; Returns true if a prerelease and/or metadata 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. Overloads boolean operations. =head3 C Compares the semantic version object to another version object or string and returns 0 if they're the same, -1 if the invocant is smaller than the argument, and 1 if the invocant is greater than the argument. Mostly you don't need to worry about this: Just use the comparison operators instead: if ($semver < $another_semver) { die "Need $another_semver or higher"; } Note that in addition to comparing other semantic version objects, you can also compare regular L objects: if ($semver < $version) { die "Need $version or higher"; } You can also pass in a version string. It will be turned into a semantic version object using C. So if you're using numeric versions, you may or may not get what you want: my $semver = version::Semver->new('1.2.0'); my $version = '1.2'; my $bool = $semver == $version; # true If that's not what you want, pass the string to C first: my $semver = Semver->new('1.2.0'); my $version = Semver->parse('1.2'); # 1.200.0 my $bool = $semver == $version; # false =head1 See Also =over =item * L. =item * L =item * L =back =head1 Support This module is managed in an open L. Feel free to fork and contribute, or to clone L and send patches! Found a bug? Please L a report! =head1 Acknowledgements Many thanks to L author John Peacock for his suggestions and debugging help. =head1 Authors =over =item * David E. Wheeler =item * Johannes Kilian =back =head1 Copyright and License Copyright (c) 2010-2020 David E. Wheeler. Some Rights Reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut SemVer-v0.10.0/t000755000765000024 013655533343 13007 5ustar00davidstaff000000000000SemVer-v0.10.0/t/base.t000444000765000024 3104013655533343 14261 0ustar00davidstaff000000000000#!/usr/bin/perl -w use strict; use warnings; use Test::More tests => 941; #use Test::More 'no_plan'; use FindBin qw($Bin); use lib "$Bin/../lib"; my $CLASS; BEGIN { $CLASS = 'SemVer'; use_ok $CLASS or die; } diag 'Testing with version v', version->VERSION; can_ok $CLASS, qw( new declare parse normal numify is_alpha is_qv vcmp ); # 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}; for my $v (qw( 1.2.2 0.2.2 1.2.2 0.0.0 0.1.999 9999.9999999.823823 1.0.0-beta1 1.0.0-beta2 1.0.0 0.0.0-rc1 v1.2.2 999993333.0.0 )) { 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/; is $semver->normal, $str, qq{$v should normalize to "$str"}; 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"; my $is_alpha = $semver->is_alpha; if ($v =~ /[.]\d+-?[a-z]/) { ok $is_alpha, "$v should be alpha"; } else { ok !$is_alpha, "$v should not be alpha"; } } local $@; eval { $CLASS->new('') }; ok $@, 'Empty string should be an invalid version'; for my $bv (qw( 1.2 0 0.0 1.2b 1.b 1.04.0 1.65.r2 )) { local $@; eval { $CLASS->new($bv) }; like $@, qr{Invalid semantic version string format: "$bv"}, qq{"$bv" should be an invalid semver}; } # Try a vstring. isa_ok $version = $CLASS->new(v2.3.2), $CLASS, 'vstring version'; is $version->stringify, 'v2.3.2', 'vestring should stringify with "v"'; is $version->normal, '2.3.2', 'vstring should normalize without "v"'; # Try a shorter vstring. my $no_2digitvst = $] < 5.10 && version->VERSION < 0.9910; SKIP: { skip 'Two-integer vstrings weak on Perl 5.8', 3 if $no_2digitvst; isa_ok $version = $CLASS->new(v2.3), $CLASS, 'vstring version'; is $version->stringify, 'v2.3', 'short vestring should stringify with "v"'; is $version->normal, '2.3.0', 'short vstring should normalize without required 0'; } # Try another SemVer. isa_ok my $cloned = $CLASS->new($version), $CLASS, 'Cloned SemVer'; is $cloned->stringify, $version->stringify, 'Cloned stringify like original'; is $cloned->normal, $version->normal, 'Cloned should normalize like original'; # Try a SemVer with alpha. isa_ok $version = $CLASS->new('2.3.2-b1'), $CLASS, 'new version'; isa_ok $cloned = $CLASS->new($version), $CLASS, 'Second cloned SemVer'; is $cloned->stringify, $version->stringify, 'Second cloned stringify like original'; is $cloned->normal, $version->normal, 'Second cloned should normalize like original'; # Numify should die local $@; eval { $version->numify }; like $@, qr{Semantic versions cannot be numified}, 'Should get error from numify()'; # Now do some comparisons. Start with equivalents. for my $spec ( [ '1.2.2', '1.2.2' ], [ '1.2.23', '1.2.23' ], [ '0.0.0', '0.0.0' ], [ '999.888.7777', '999.888.7777' ], [ '0.1.2-beta3', '0.1.2-beta3' ], [ '1.0.0-rc-1', '1.0.0-RC-1' ], ) { my $l = $CLASS->new($spec->[0]); my $r = $CLASS->new($spec->[1]); is $l->vcmp($r), 0, "$l->vcmp($r) == 0"; is $l <=> $r, 0, "$l <=> $r == 0"; is $r <=> $l, 0, "$r <=> $l == 0"; cmp_ok $l, '==', $r, "$l == $r"; cmp_ok $l, '==', $r, "$l == $r"; cmp_ok $l, '<=', $r, "$l <= $r"; cmp_ok $l, '>=', $r, "$l >= $r"; is $l cmp $r, 0, "$l cmp $r == 0"; is $r cmp $l, 0, "$r cmp $l == 0"; cmp_ok $l, 'eq', $r, "$l eq $r"; cmp_ok $l, 'eq', $r, "$l eq $r"; cmp_ok $l, 'le', $r, "$l le $r"; cmp_ok $l, 'ge', $r, "$l ge $r"; } # Test equivalents with declare() for my $spec ( [ '1.2.0', '1.2' ], [ '0.0.0', '0' ], [ '1.4_0', '1.4' ], [ '1.08', '1.8' ], [ 1.02_30, '1.23.0' ], [ '1.02_30', '1.23.0' ], [ '999.888.7777-alpha.3', '999.888.7777-alpha.3' ], [ '0.1.2-beta3', '0.1.2-beta3' ], [ '1.0.0-rc-1', '1.0.0-RC-1' ], ) { my $l = $CLASS->declare($spec->[0]); my $r = $CLASS->declare($spec->[1]); is $l->vcmp($r), 0, "declare $l->vcmp($r) == 0"; is $l <=> $r, 0, "declare $l <=> $r == 0"; is $r <=> $l, 0, "declare $r <=> $l == 0"; cmp_ok $l, '==', $r, "declare $l == $r"; cmp_ok $l, '==', $r, "declare $l == $r"; cmp_ok $l, '<=', $r, "declare $l <= $r"; cmp_ok $l, '>=', $r, "declare $l >= $r"; is $l cmp $r, 0, "declare $l cmp $r == 0"; is $r cmp $l, 0, "declare $r cmp $l == 0"; cmp_ok $l, 'eq', $r, "declare $l eq $r"; cmp_ok $l, 'eq', $r, "declare $l eq $r"; cmp_ok $l, 'le', $r, "declare $l le $r"; cmp_ok $l, 'ge', $r, "declare $l ge $r"; } # Test equivalents with parse() for my $spec ( [ '1.02.0', '1.2.0' ], [ '0.0.0', '0' ], [ '1.4_0', '1.4' ], [ 1.02_30, '1.23.0' ], [ '1.02_30', '1.23.0' ], [ '999.888.7777-alpha.3', '999.888.7777-alpha.3' ], [ '0.1.2-beta3', '0.1.2-beta3' ], [ '1.0.0-rc-1', '1.0.0-RC-1' ], ) { my $l = $CLASS->parse($spec->[0]); my $r = $CLASS->parse($spec->[1]); is $l->vcmp($r), 0, "parse $l->vcmp($r) == 0"; is $l <=> $r, 0, "parse $l <=> $r == 0"; is $r <=> $l, 0, "parse $r <=> $l == 0"; cmp_ok $l, '==', $r, "parse $l == $r"; cmp_ok $l, '==', $r, "parse $l == $r"; cmp_ok $l, '<=', $r, "parse $l <= $r"; cmp_ok $l, '>=', $r, "parse $l >= $r"; is $l cmp $r, 0, "parse $l cmp $r == 0"; is $r cmp $l, 0, "parse $r cmp $l == 0"; cmp_ok $l, 'eq', $r, "parse $l eq $r"; cmp_ok $l, 'eq', $r, "parse $l eq $r"; cmp_ok $l, 'le', $r, "parse $l le $r"; cmp_ok $l, 'ge', $r, "parse $l ge $r"; } # Test not equal. for my $spec ( ['1.2.2', '1.2.3'], ['0.0.1', '1.0.0'], ['1.0.1', '1.1.0'], ['1.1.1', '1.1.0'], ['1.2.3-b', '1.2.3'], ['1.2.3', '1.2.3-b'], ['1.2.3-a', '1.2.3-b'], ['1.2.3-aaaaaaa1', '1.2.3-aaaaaaa2'], ) { my $l = $CLASS->new($spec->[0]); my $r = $CLASS->new($spec->[1]); cmp_ok $l->vcmp($r), '!=', 0, "$l->vcmp($r) != 0"; cmp_ok $l, '!=', $r, "$l != $r"; cmp_ok $l, 'ne', $r, "$l ne $r"; } # Test not equal with declare. for my $spec ( ['1.2.2', '1.2.3'], ['0.0.1', '1.0.0'], ['1.0.1', '1.1.0'], ['1.1.1', '1.1.0'], ['1.2.3-b', '1.2.3'], ['1.2.3', '1.2.3-b'], ['1.2.3-a', '1.2.3-b'], ['1.2_0', '1.20' ], ['1.2.3-aaaaaaa1', '1.2.3-aaaaaaa2'], ) { my $l = $CLASS->declare($spec->[0]); my $r = $CLASS->declare($spec->[1]); cmp_ok $l->vcmp($r), '!=', 0, "declare $l->vcmp($r) != 0"; cmp_ok $l, '!=', $r, "declare $l != $r"; cmp_ok $l, 'ne', $r, "declare $l ne $r"; } # Test not equal with parse. for my $spec ( ['1.2.2', '1.2.3'], ['0.0.1', '1.0.0'], ['1.0.1', '1.1.0'], ['1.1.1', '1.1.0'], ['1.2.3-b', '1.2.3'], ['1.2.3', '1.2.3-b'], ['1.2.3-a', '1.2.3-b'], ['1.2_0', '1.2.0' ], ['1.2.3-aaaaaaa1', '1.2.3-aaaaaaa2'], ) { my $l = $CLASS->parse($spec->[0]); my $r = $CLASS->parse($spec->[1]); cmp_ok $l->vcmp($r), '!=', 0, "parse $l->vcmp($r) != 0"; cmp_ok $l, '!=', $r, "parse $l != $r"; cmp_ok $l, 'ne', $r, "parse $l ne $r"; } # Test >, >=, <, and <=. for my $spec ( ['2.2.2', '1.1.1'], ['2.2.2', '2.1.1'], ['2.2.2', '2.2.1'], ['2.2.2-b', '2.2.1'], ['2.2.2', '2.2.2-b'], ['2.2.2-c', '2.2.2-b'], ['2.2.2-rc-2', '2.2.2-RC-1'], ['0.9.10', '0.9.9'], ) { my $l = $CLASS->new($spec->[0]); my $r = $CLASS->new($spec->[1]); cmp_ok $l->vcmp($r), '>', 0, "$l->vcmp($r) > 0"; cmp_ok $r->vcmp($l), '<', 0, "$r->vcmp($l) < 0"; cmp_ok $l, '>', $r, "$l > $r"; cmp_ok $l, '>=', $r, "$l >= $r"; cmp_ok $r, '<', $l, "$r < $l"; cmp_ok $r, '<=', $l, "$r <= $l"; cmp_ok $l, 'gt', $r, "$l gt $r"; cmp_ok $l, 'ge', $r, "$l ge $r"; cmp_ok $r, 'lt', $l, "$r lt $l"; cmp_ok $r, 'le', $l, "$r le $l"; } # Compare Versions from SemVer 2.0 my @ver = ("0.9.0","1.0.0-alpha","1.0.0-alpha.1","1.0.0-alpha.beta","1.0.0-beta","1.0.0-beta.2","1.0.0-beta.11","1.0.0-beta.31","1.0.0-beta.200","1.0.0-rc.1","1.0.0","2.0.0","2.1.0","2.1.1"); for (my $i = 0; $i < (scalar(@ver)-1); $i++) { for (my $j = $i+1; $j < scalar(@ver); $j++) { my $l = SemVer->new($ver[$i]); my $r = SemVer->new($ver[$j]); cmp_ok $l, '<', $r, "$l < $r"; } } # Compare to version objects. my $semver = $CLASS->new('1.2.0'); for my $v (qw( 1.002 1.2.0 v1.002 v1.2.0 )) { my $version = version->new($v); ok $semver == $version, "$semver == $version"; } # Compare to strings. for my $v (qw( 1.2.0 v1.2.0 )) { my $semver = $CLASS->new($v); cmp_ok $semver, '==', $v, qq{$semver == "$v"}; cmp_ok $v, '==', $semver, qq{"$v" == $semver}; cmp_ok $semver, 'eq', $v, qq{$semver eq "$v"}; 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'], ['01.2.2', '1.2.2'], ['1.02.2', '1.2.2'], ['1.2.02', '1.2.2'], ['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.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'], [1.02_30, '1.23.0'], [3.4, '3.4.0', '3.400.0'], [3.04, '3.4.0', '3.40.0' ], ['3.04', '3.4.0', '3.40.0' ], [v3.4, '3.4.0' ], [9, '9.0.0' ], ['9', '9.0.0' ], ['0', '0.0.0' ], [0, '0.0.0' ], ['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]); my $r = $CLASS->new($spec->[1]); isa_ok my $l = SemVer->declare($spec->[0]), $CLASS, "Declared $spec->[0]"; my $string = Scalar::Util::isvstring($spec->[0]) ? join '.', map { ord } split // => $spec->[0] : $spec->[0]; $string =~ s/^\s+//; $string =~ s/\s+$//; $string += 0 if $string =~ s/_//g; 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. cmp_ok $spec->[0], '==', $r, qq{$r == "$spec->[0]"}; if ($spec->[0] && $spec->[0] !~ /^[a-z]/ && $spec->[0] !~ /[.]{2}/) { my $exp = $spec->[2] || $spec->[1]; isa_ok $l = SemVer->parse($spec->[0]), $CLASS, "Parsed $spec->[0]"; $string = "v$string" if Scalar::Util::isvstring($spec->[0]); $string =~ s/_//; is $l->stringify, $string, "... And it should stringify to $string"; is $l->normal, $exp, "... And it should normalize to $exp"; # Try with the parsed version. $r = $CLASS->new($spec->[2]) if $spec->[2]; cmp_ok $l, '==', $r, qq{$l == $r} unless $string =~ /_/; } # Try creating as a version object and cloning. if ($spec->[0] !~ /[a-z]/i) { isa_ok my $v = version->parse($spec->[0]), 'version', "base version $spec->[0]"; isa_ok my $sv = SemVer->new($v), 'SemVer', "SemVer from base version $spec->[0]"; is $sv->stringify, $string, qq{... And it should stringify to "$vstring"}; SKIP: { skip 'version 0.9911 broke alpha->normal()', 1, if $spec->[0] =~ /_/ && version->VERSION == 0.9911; is $sv->normal, $l->normal, '... And it should normalize to "' . $l->normal . '"'; } } }} for my $v (qw( 1.2.02b 1.2.02beta-3 1.02.02rc1 1.1b1 1b 9.0beta4 0rc1 )) { eval { my $l = SemVer->declare($v); }; if ($@) { is 1, 1, $v.' is not a SemVer'; } else { is 1, 0, $v.' should not be not a SemVer'; } } SemVer-v0.10.0/t/corpus.t000444000765000024 513213655533343 14645 0ustar00davidstaff000000000000#!/usr/bin/perl -w # Test the SemVer corpus from https://regex101.com/r/Ly7O1x/3/. use strict; use warnings; use Test::More tests => 222; #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 1.0.0-0A.is.legal )) { local $@; 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"}; } 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 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}; }