| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'dart:convert'; |
| |
| import 'package:meta/meta.dart'; |
| |
| /// Signals that the output of golden files may diverge from `main`. |
| /// |
| /// By default, Skia Gold will compare the output of a test to the golden file |
| /// in the `main` branch. During a release branch, the output of the test may |
| /// diverge (intentionally or accidentally, i.e. a bug) from the output in |
| /// `main`. |
| /// |
| /// This class signals to Skia Gold to use a unique test name for the release. |
| @immutable |
| final class ReleaseVersion { |
| /// Creates a [ReleaseVersion] with the given [major] and [minor] version. |
| /// |
| /// For example, for the `3.21` release: |
| /// ```dart |
| /// ReleaseVersion( |
| /// major: 3, |
| /// minor: 21, |
| /// ) |
| /// ``` |
| /// |
| /// Each number must be non-negative. |
| ReleaseVersion({ |
| required this.major, |
| required this.minor, |
| }) { |
| RangeError.checkNotNegative(major, 'major'); |
| RangeError.checkNotNegative(minor, 'minor'); |
| } |
| |
| /// Parses a [ReleaseVersion] from the contents of a `release.version` file. |
| /// |
| /// Returns `null` if and only if the version is parsed as the string `none`. |
| /// |
| /// The format of the file is plaintext, and any lines that are empty |
| /// (newline characters only) or start with `#` are ignored. The first line |
| /// that is not ignored must be in the format `major.minor`, where `major` and |
| /// `minor` are non-negative integers. |
| /// |
| /// For example, the following file contents: |
| /// ```txt |
| /// # This is a comment |
| /// |
| /// 3.21 |
| /// ``` |
| /// |
| /// ... would parse to `ReleaseVersion(major: 3, minior: 21)`. |
| /// |
| /// Throws a [FormatException] if the file contents are not in the expected |
| /// format (either empty/comments only, or missing a `major.minor` line in |
| /// the format described in [ReleaseVersion.new]). |
| static ReleaseVersion? parse(String fileContents) { |
| bool parsedNone = false; |
| ReleaseVersion? parsed; |
| for (final String line in LineSplitter.split(fileContents)) { |
| final String trimmed = line.trim(); |
| if (trimmed.isEmpty || trimmed.startsWith('#')) { |
| continue; |
| } |
| if (trimmed == 'none') { |
| parsedNone = true; |
| continue; |
| } |
| final List<String> parts = trimmed.split('.'); |
| if (parts.length != 2) { |
| throw FormatException( |
| 'Expected "major.minor" format, got "$trimmed"', |
| fileContents, |
| ); |
| } |
| final int? major = int.tryParse(parts[0]); |
| final int? minor = int.tryParse(parts[1]); |
| if (major == null || minor == null) { |
| throw FormatException( |
| 'Expected "major.minor", each as an integer, got "$trimmed"', |
| fileContents, |
| ); |
| } |
| if (parsed != null) { |
| throw FormatException( |
| 'Multiple "major.minor" versions found', |
| fileContents, |
| ); |
| } |
| parsed = ReleaseVersion( |
| major: major, |
| minor: minor, |
| ); |
| } |
| if (parsed != null && parsedNone) { |
| throw FormatException( |
| 'Both "none" and a "major.minor" version found', |
| fileContents, |
| ); |
| } |
| if (parsed != null) { |
| return parsed; |
| } |
| if (parsedNone) { |
| return null; |
| } |
| throw FormatException('No "major.minor" version found', fileContents); |
| } |
| |
| /// The major version number. |
| final int major; |
| |
| /// The minor version number. |
| final int minor; |
| |
| @override |
| bool operator ==(Object other) { |
| return other is ReleaseVersion && |
| other.major == major && |
| other.minor == minor; |
| } |
| |
| @override |
| int get hashCode => Object.hash(major, minor); |
| |
| @override |
| String toString() => '$major.$minor'; |
| } |