Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[package_config] Implement relational operators for LanguageVersion #2016

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion pkgs/package_config/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
## 2.1.2-wip
## 2.2.0-wip

- Add relational operators to `LanguageVersion` with extension methods
exported under `LanguageVersionRelationalOperators`.

- Include correct parameter names in errors when validating
the `major` and `minor` versions in the `LanguageVersion.new` constructor.
Expand Down
7 changes: 6 additions & 1 deletion pkgs/package_config/lib/package_config_types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,9 @@ library;

export 'src/errors.dart' show PackageConfigError;
export 'src/package_config.dart'
show InvalidLanguageVersion, LanguageVersion, Package, PackageConfig;
show
InvalidLanguageVersion,
LanguageVersion,
LanguageVersionRelationalOperators,
Package,
PackageConfig;
89 changes: 88 additions & 1 deletion pkgs/package_config/lib/src/package_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ abstract class LanguageVersion implements Comparable<LanguageVersion> {
/// Two language versions are considered equal if they have the
/// same major and minor version numbers.
///
/// A language version is greater then another if the former's major version
/// A language version is greater than another if the former's major version
/// is greater than the latter's major version, or if they have
/// the same major version and the former's minor version is greater than
/// the latter's.
Expand Down Expand Up @@ -406,3 +406,90 @@ abstract class InvalidLanguageVersion implements LanguageVersion {
@override
String toString();
}

/// Relational operators for [LanguageVersion] that
/// compare valid versions with [LanguageVersion.compareTo].
///
/// If either operand is an [InvalidLanguageVersion], a [StateError] is thrown.
/// Versions should be verified as valid after parsing and before using them.
extension LanguageVersionRelationalOperators on LanguageVersion {
/// Whether this language version is less than [other].
///
/// If either version being compared is an [InvalidLanguageVersion],
/// a [StateError] is thrown. Verify versions are valid before comparing them.
///
/// For details on how valid language versions are compared,
/// check out [LanguageVersion.compareTo].
bool operator <(LanguageVersion other) {
// Throw an error if comparing as or with an invalid language version.
if (this is InvalidLanguageVersion) {
_throwThisInvalid();
} else if (other is InvalidLanguageVersion) {
_throwOtherInvalid();
}

return compareTo(other) < 0;
}

/// Whether this language version is less than or equal to [other].
///
/// If either version being compared is an [InvalidLanguageVersion],
/// a [StateError] is thrown. Verify versions are valid before comparing them.
///
/// For details on how valid language versions are compared,
/// check out [LanguageVersion.compareTo].
bool operator <=(LanguageVersion other) {
// Throw an error if comparing as or with an invalid language version.
if (this is InvalidLanguageVersion) {
_throwThisInvalid();
} else if (other is InvalidLanguageVersion) {
_throwOtherInvalid();
}

return compareTo(other) <= 0;
}

/// Whether this language version is greater than [other].
///
/// If either version being compared is an [InvalidLanguageVersion],
/// a [StateError] is thrown. Verify versions are valid before comparing them.
///
/// For details on how valid language versions are compared,
/// check out [LanguageVersion.compareTo].
bool operator >(LanguageVersion other) {
// Throw an error if comparing as or with an invalid language version.
if (this is InvalidLanguageVersion) {
_throwThisInvalid();
} else if (other is InvalidLanguageVersion) {
_throwOtherInvalid();
}

return compareTo(other) > 0;
}

/// Whether this language version is greater than or equal to [other].
///
/// If either version being compared is an [InvalidLanguageVersion],
/// a [StateError] is thrown. Verify versions are valid before comparing them.
///
/// For details on how valid language versions are compared,
/// check out [LanguageVersion.compareTo].
bool operator >=(LanguageVersion other) {
// Throw an error if comparing as or with an invalid language version.
if (this is InvalidLanguageVersion) {
_throwThisInvalid();
} else if (other is InvalidLanguageVersion) {
_throwOtherInvalid();
}

return compareTo(other) >= 0;
}

static Never _throwThisInvalid() => throw StateError(
'Can\'t compare an invalid language version to another language version. '
'Verify language versions are valid after parsing.');

static Never _throwOtherInvalid() => throw StateError(
'Can\'t compare a language version to an invalid language version. '
'Verify language versions are valid after parsing.');
}
2 changes: 1 addition & 1 deletion pkgs/package_config/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: package_config
version: 2.1.2-wip
version: 2.2.0-wip
description: Support for reading and writing Dart Package Configuration files.
repository: https://github.com/dart-lang/tools/tree/main/pkgs/package_config
issue_tracker: https://github.com/dart-lang/tools/labels/package%3Apackage_config
Expand Down
80 changes: 80 additions & 0 deletions pkgs/package_config/test/package_config_impl_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,86 @@ void main() {
failParse('WhiteSpace 2', '1 .1');
failParse('WhiteSpace 3', '1. 1');
failParse('WhiteSpace 4', '1.1 ');

test('compareTo valid', () {
var version = LanguageVersion(3, 5);

for (var (otherVersion, matcher) in [
(version, isZero), // Identical.
(LanguageVersion(3, 5), isZero), // Same major, same minor.
(LanguageVersion(3, 4), isPositive), // Same major, lower minor.
(LanguageVersion(3, 6), isNegative), // Same major, greater minor.
(LanguageVersion(2, 5), isPositive), // Lower major, same minor.
(LanguageVersion(2, 4), isPositive), // Lower major, lower minor.
(LanguageVersion(2, 6), isPositive), // Lower major, greater minor.
(LanguageVersion(4, 5), isNegative), // Greater major, same minor.
(LanguageVersion(4, 4), isNegative), // Greater major, lower minor.
(LanguageVersion(4, 6), isNegative), // Greater major, greater minor.
]) {
expect(version.compareTo(otherVersion), matcher);
}
});

test('compareTo invalid', () {
var validVersion = LanguageVersion(3, 5);
var invalidVersion = LanguageVersion.parse('', onError: (_) {});

expect(validVersion.compareTo(invalidVersion), isPositive);
expect(invalidVersion.compareTo(validVersion), isNegative);
});

test('relational valid', () {
/// Test that the relational comparisons between two valid versions
/// match the results of `compareTo`.
void testComparisons(
LanguageVersion version, LanguageVersion otherVersion) {
expect(version == otherVersion, version.compareTo(otherVersion) == 0);

expect(version < otherVersion, version.compareTo(otherVersion) < 0);
expect(version <= otherVersion, version.compareTo(otherVersion) <= 0);

expect(version > otherVersion, version.compareTo(otherVersion) > 0);
expect(version >= otherVersion, version.compareTo(otherVersion) >= 0);
}

var version = LanguageVersion(3, 5);

// Check relational comparisons of a version to itself.
testComparisons(version, version);

// Check relational comparisons of a version to versions with all
// possible combinations of minor and major versions that are
// the same, lower, and greater.
for (final major in [2, 3, 4]) {
for (final minor in [4, 5, 6]) {
testComparisons(version, LanguageVersion(major, minor));
}
}
});

test('relational invalid', () {
void testComparisonsWithInvalid(
LanguageVersion version,
LanguageVersion otherVersion,
) {
expect(version == otherVersion, identical(version, otherVersion));

expect(() => version < otherVersion, throwsA(isA<StateError>()));
expect(() => version <= otherVersion, throwsA(isA<StateError>()));

expect(() => version > otherVersion, throwsA(isA<StateError>()));
expect(() => version >= otherVersion, throwsA(isA<StateError>()));
}

var validVersion = LanguageVersion(3, 5);
var invalidVersion = LanguageVersion.parse('', onError: (_) {});
var differentInvalidVersion = LanguageVersion.parse('-', onError: (_) {});

testComparisonsWithInvalid(validVersion, invalidVersion);
testComparisonsWithInvalid(invalidVersion, validVersion);
testComparisonsWithInvalid(invalidVersion, invalidVersion);
testComparisonsWithInvalid(invalidVersion, differentInvalidVersion);
});
});

group('Package', () {
Expand Down