Skip to content

Windows version check in doctor #3

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

Merged
merged 9 commits into from
Sep 6, 2022
Merged
1 change: 0 additions & 1 deletion packages/flutter_tools/lib/src/doctor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,6 @@ class _DefaultDoctorValidatorsProvider implements DoctorValidatorsProvider {
];
final ProxyValidator proxyValidator = ProxyValidator(platform: platform);
_validators = <DoctorValidator>[
if (platform.isWindows) const WindowsVersionValidator(),
FlutterValidator(
fileSystem: globals.fs,
platform: globals.platform,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,39 @@ import 'package:process/process.dart';
import '../base/io.dart';
import '../doctor_validator.dart';

const List<String> unsupportedVersions = <String>[
'6',
'7',
'8',
];

/// Validator to be run with `flutter doctor` to check
/// Windows host machines if they are running supported versions.
class WindowsVersionValidator extends DoctorValidator {
/// Validator to be run with `flutter doctor` to check
/// Windows host machines if they are running supported versions,
/// current unsupported versions = 7, 8
const WindowsVersionValidator({required ProcessManager processManager})
: _processManager = processManager,
super('Windows Version');

final ProcessManager _processManager;

/// Provide a literal string as the Regex pattern
/// and a string to validate and get a boolean determining
/// if the string has at least one match
static Iterable<RegExpMatch> validateString(String pattern, String str,
{bool multiLine = true}) {
final RegExp regex = RegExp(
pattern,
multiLine: multiLine,
);

return regex.allMatches(str);
}

@override
Future<ValidationResult> validate() async {
final ProcessResult result;
try {
result = _processManager.runSync(<String>['systeminfo']);
result = await _processManager.run(<String>['systeminfo']);
} on ProcessException {
return const ValidationResult(
ValidationType.missing,
Expand All @@ -41,65 +59,19 @@ class WindowsVersionValidator extends DoctorValidator {

final String resultStdout = result.stdout as String;

// Define the major versions that are not supported
const List<String> unsupportedVersions = <String>[
'7',
'8',
];

final List<String> systemInfoElements = resultStdout.split('\n');

// Regular expression pattern for identifying
// semantic versioned strings
// (ie. 10.5.4123)
final RegExp regex = RegExp(r'^([0-9]+)\.([0-9]+)\.([0-9]+)$');

// Define the list that will contain the matches;
// if ran successfully, this list should have only
// one item
final List<String> versionList = <String>[];

// Use two booleans to identify when you have found
// the word 'version' and a version number that matches
// the regex pattern above; only once both are found do
// we report back a valid version
bool versionText = false;
bool versionSemver = false;
String? version;
for (final String curLine in systemInfoElements) {
final List<String> lineElems = curLine.split(' ');

for (final String elem in lineElems){
final bool match = regex.hasMatch(elem);

if (match) {
versionSemver = true;
version = elem;
}

if (elem.toLowerCase().contains('version')) {
versionText = true;
}
}

// Once both booleans are true, add
// the version to the list that will contain
// at most, one element if ran as anticipated
if (versionText && versionSemver && version != null) {
versionList.add(version);
}

// Reset the boolean values for the next line
versionText = false;
versionSemver = false;
version = null;
}
final Iterable<RegExpMatch> matches = validateString(
r'^(OS Version:\s*)([0-9]+\.[0-9]+\.[0-9]+)(.*)$', resultStdout);

// Use the string split method to extract the major version
// and check against the [unsupportedVersions] list
final ValidationType windowsVersionStatus;
String statusInfo;
if (versionList.length == 1 &&
if (matches.length == 1 &&
!unsupportedVersions
.contains(versionList.elementAt(0).split('.').elementAt(0))) {
.contains(matches.elementAt(0).group(2)?.split('.').elementAt(0))) {
windowsVersionStatus = ValidationType.installed;
statusInfo = 'Installed version of Windows is version 10 or higher';
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
// Copyright 2014 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 'package:flutter_tools/src/doctor_validator.dart';
import 'package:flutter_tools/src/windows/windows_version_validator.dart';

import '../src/common.dart';
import '../src/fake_process_manager.dart';

/// Example output from `systeminfo` from a Windows 10 host
const String validWindows10StdOut = r'''
Host Name: XXXXXXXXXXXX
OS Name: Microsoft Windows 10 Enterprise
OS Version: 10.0.19044 N/A Build 19044
OS Manufacturer: Microsoft Corporation
OS Configuration: Member Workstation
OS Build Type: Multiprocessor Free
Registered Owner: N/A
Registered Organization: N/A
Product ID: XXXXXXXXXXXX
Original Install Date: 8/4/2022, 2:51:28 PM
System Boot Time: 8/10/2022, 1:03:10 PM
System Manufacturer: Google
System Model: Google Compute Engine
System Type: x64-based PC
Processor(s): 1 Processor(s) Installed.
[01]: AMD64 Family 23 Model 49 Stepping 0 AuthenticAMD ~2250 Mhz
BIOS Version: Google Google, 6/29/2022
Windows Directory: C:\\Windows
System Directory: C:\\Windows\\system32
Boot Device: \\Device\\HarddiskVolume2
System Locale: en-us;English (United States)
Input Locale: en-us;English (United States)
Time Zone: (UTC-08:00) Pacific Time (US & Canada)
Total Physical Memory: 32,764 MB
Available Physical Memory: 17,852 MB
Virtual Memory: Max Size: 33,788 MB
Virtual Memory: Available: 18,063 MB
Virtual Memory: In Use: 15,725 MB
Page File Location(s): C:\\pagefile.sys
Domain: ad.corp.google.com
Logon Server: \\CBF-DC-8
Hotfix(s): 7 Hotfix(s) Installed.
[01]: KB5013624
[02]: KB5003791
[03]: KB5012170
[04]: KB5016616
[05]: KB5014032
[06]: KB5014671
[07]: KB5015895
Hyper-V Requirements: A hypervisor has been detected. Features required for Hyper-V will not be displayed.
''';

/// Example output from `systeminfo` from version != 10
const String invalidWindowsStdOut = r'''
Host Name: XXXXXXXXXXXX
OS Name: Microsoft Windows 8.1 Enterprise
OS Version: 6.3.9600 Build 9600
OS Manufacturer: Microsoft Corporation
OS Configuration: Member Workstation
OS Build Type: Multiprocessor Free
Registered Owner: N/A
Registered Organization: N/A
Product ID: XXXXXXXXXXXX
Original Install Date: 8/4/2022, 2:51:28 PM
System Boot Time: 8/10/2022, 1:03:10 PM
System Manufacturer: Google
System Model: Google Compute Engine
System Type: x64-based PC
Processor(s): 1 Processor(s) Installed.
[01]: AMD64 Family 23 Model 49 Stepping 0 AuthenticAMD ~2250 Mhz
BIOS Version: Google Google, 6/29/2022
Windows Directory: C:\\Windows
System Directory: C:\\Windows\\system32
Boot Device: \\Device\\HarddiskVolume2
System Locale: en-us;English (United States)
Input Locale: en-us;English (United States)
Time Zone: (UTC-08:00) Pacific Time (US & Canada)
Total Physical Memory: 32,764 MB
Available Physical Memory: 17,852 MB
Virtual Memory: Max Size: 33,788 MB
Virtual Memory: Available: 18,063 MB
Virtual Memory: In Use: 15,725 MB
Page File Location(s): C:\\pagefile.sys
Domain: ad.corp.google.com
Logon Server: \\CBF-DC-8
Hotfix(s): 7 Hotfix(s) Installed.
[01]: KB5013624
[02]: KB5003791
[03]: KB5012170
[04]: KB5016616
[05]: KB5014032
[06]: KB5014671
[07]: KB5015895
Hyper-V Requirements: A hypervisor has been detected. Features required for Hyper-V will not be displayed.
''';

/// The expected validation result object for
/// a passing windows version test
const ValidationResult validWindows10ValidationResult = ValidationResult(
ValidationType.installed,
<ValidationMessage>[],
statusInfo: 'Installed version of Windows is version 10 or higher',
);

/// The expected validation result object for
/// a failing exit code (!= 0)
const ValidationResult failedValidationResult = ValidationResult(
ValidationType.missing,
<ValidationMessage>[],
statusInfo: 'Exit status from running `systeminfo` was unsuccessful',
);

/// The expected validation result object for
/// a passing windows version test
const ValidationResult invalidWindowsValidationResult = ValidationResult(
ValidationType.missing,
<ValidationMessage>[],
statusInfo: 'Unable to confirm if installed Windows version is 10 or greater',
);

void main() {
testWithoutContext('Successfully running windows version check on windows 10',
() async {
final WindowsVersionValidator windowsVersionValidator =
WindowsVersionValidator(
processManager: FakeProcessManager.list(
<FakeCommand>[
const FakeCommand(
command: <String>['systeminfo'],
stdout: validWindows10StdOut,
),
],
),
);

final ValidationResult result = await windowsVersionValidator.validate();

expect(result.type, validWindows10ValidationResult.type,
reason: 'The ValidationResult type should be the same (installed)');
expect(result.statusInfo, validWindows10ValidationResult.statusInfo,
reason: 'The ValidationResult statusInfo messages should be the same');
});

testWithoutContext('Failing to invoke the `systeminfo` command', () async {
final WindowsVersionValidator windowsVersionValidator =
WindowsVersionValidator(
processManager: FakeProcessManager.list(
<FakeCommand>[
const FakeCommand(
command: <String>['systeminfo'],
stdout: validWindows10StdOut,
exitCode: 1,
),
],
),
);

final ValidationResult result = await windowsVersionValidator.validate();

expect(result.type, failedValidationResult.type,
reason: 'The ValidationResult type should be the same (missing)');
expect(result.statusInfo, failedValidationResult.statusInfo,
reason: 'The ValidationResult statusInfo messages should be the same');
});

testWithoutContext('Identifying a windows version before 10', () async {
final WindowsVersionValidator windowsVersionValidator =
WindowsVersionValidator(
processManager: FakeProcessManager.list(
<FakeCommand>[
const FakeCommand(
command: <String>['systeminfo'],
stdout: invalidWindowsStdOut,
),
],
),
);

final ValidationResult result = await windowsVersionValidator.validate();

expect(result.type, invalidWindowsValidationResult.type,
reason: 'The ValidationResult type should be the same (missing)');
expect(result.statusInfo, invalidWindowsValidationResult.statusInfo,
reason: 'The ValidationResult statusInfo messages should be the same');
});

testWithoutContext('Unit testing on a regex pattern validator', () async {
const String regexPattern =
r'^(OS Version:\s*)([0-9]+\.[0-9]+\.[0-9]+)(.*)$';
const String testStr = r'''
OS Version: 10.0.19044 N/A Build 19044
OSz Version: 10.0.19044 N/A Build 19044
OS 6Version: 10.0.19044 N/A Build 19044
OxS Version: 10.0.19044 N/A Build 19044
OS Version: 10.19044 N/A Build 19044
OS Version: 10.x.19044 N/A Build 19044
OS Version: 10.0.19044 N/A Build 19044
OS Version: .0.19044 N/A Build 19044
''';

final Iterable<RegExpMatch> matches =
WindowsVersionValidator.validateString(regexPattern, testStr);

expect(matches.length, 2,
reason: 'There should be only two matches for the pattern provided');
});
}