-
Notifications
You must be signed in to change notification settings - Fork 326
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move logic to de-duplicate extensions into a helper file for shared u…
…se (#7959)
- Loading branch information
1 parent
712504f
commit adf025b
Showing
6 changed files
with
212 additions
and
168 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
87 changes: 87 additions & 0 deletions
87
packages/devtools_app/lib/src/extensions/extension_service_helpers.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
// Copyright 2024 The Chromium 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:devtools_app_shared/utils.dart'; | ||
import 'package:devtools_shared/devtools_extensions.dart'; | ||
import 'package:devtools_shared/devtools_shared.dart'; | ||
import 'package:flutter/foundation.dart'; | ||
import 'package:logging/logging.dart'; | ||
|
||
/// De-duplicates extensions by ignoring all that are not the latest version | ||
/// when there are duplicates. | ||
void deduplicateExtensionsAndTakeLatest( | ||
List<DevToolsExtensionConfig> extensions, { | ||
required void Function(DevToolsExtensionConfig ext, {required bool ignore}) | ||
onSetIgnored, | ||
Logger? logger, | ||
String extensionType = '', | ||
}) { | ||
final deduped = <String>{}; | ||
for (final ext in extensions) { | ||
if (deduped.contains(ext.name)) continue; | ||
deduped.add(ext.name); | ||
|
||
// This includes [ext] itself. | ||
final matchingExtensions = extensions.where((e) => e.name == ext.name); | ||
if (matchingExtensions.length > 1) { | ||
logger?.fine( | ||
'detected duplicate $extensionType extensions for ${ext.name}', | ||
); | ||
|
||
// Ignore all matching extensions and then mark the [latest] as | ||
// unignored after the loop is finished. | ||
var latest = ext; | ||
for (final ext in matchingExtensions) { | ||
onSetIgnored(ext, ignore: true); | ||
latest = takeLatestExtension(latest, ext); | ||
} | ||
onSetIgnored(latest, ignore: false); | ||
|
||
logger?.fine( | ||
'ignored ${matchingExtensions.length - 1} duplicate $extensionType ' | ||
'${pluralize('extension', matchingExtensions.length - 1)} in favor of ' | ||
'${latest.identifier} at ${latest.devtoolsOptionsUri}', | ||
); | ||
} else { | ||
logger?.fine( | ||
'no duplicates found for $extensionType extension ${ext.name}', | ||
); | ||
} | ||
} | ||
} | ||
|
||
/// Compares the versions of extension configurations [a] and [b] and returns | ||
/// the extension configuration with the latest version, following semantic | ||
/// versioning rules. | ||
@visibleForTesting | ||
DevToolsExtensionConfig takeLatestExtension( | ||
DevToolsExtensionConfig a, | ||
DevToolsExtensionConfig b, | ||
) { | ||
bool exceptionParsingA = false; | ||
bool exceptionParsingB = false; | ||
SemanticVersion? versionA; | ||
SemanticVersion? versionB; | ||
try { | ||
versionA = SemanticVersion.parse(a.version); | ||
} catch (_) { | ||
exceptionParsingA = true; | ||
} | ||
|
||
try { | ||
versionB = SemanticVersion.parse(b.version); | ||
} catch (_) { | ||
exceptionParsingB = true; | ||
} | ||
|
||
if (exceptionParsingA || exceptionParsingB) { | ||
if (exceptionParsingA) { | ||
return b; | ||
} | ||
return a; | ||
} | ||
|
||
final versionCompare = versionA!.compareTo(versionB!); | ||
return versionCompare >= 0 ? a : b; | ||
} |
104 changes: 104 additions & 0 deletions
104
packages/devtools_app/test/extensions/extension_service_helpers_test.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
// Copyright 2024 The Chromium 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:devtools_app/devtools_app.dart'; | ||
import 'package:devtools_app/src/shared/development_helpers.dart'; | ||
import 'package:devtools_shared/devtools_extensions.dart'; | ||
import 'package:flutter_test/flutter_test.dart'; | ||
|
||
void main() { | ||
group('takeLatestExtension', () { | ||
test('returns newer extension', () { | ||
expect( | ||
takeLatestExtension( | ||
StubDevToolsExtensions.barExtension, | ||
StubDevToolsExtensions.newerBarExtension, | ||
), | ||
StubDevToolsExtensions.newerBarExtension, | ||
); | ||
}); | ||
|
||
test('handles parsing errors', () { | ||
// Returns 'b' when 'a' has parsing errors. | ||
var a = DevToolsExtensionConfig.parse({ | ||
DevToolsExtensionConfig.nameKey: 'bar', | ||
DevToolsExtensionConfig.issueTrackerKey: 'www.google.com', | ||
DevToolsExtensionConfig.versionKey: 'this-will-not-parse', | ||
DevToolsExtensionConfig.materialIconCodePointKey: 0xe638, | ||
DevToolsExtensionConfig.requiresConnectionKey: 'false', | ||
DevToolsExtensionConfig.extensionAssetsPathKey: '/absolute/path/to/bar', | ||
DevToolsExtensionConfig.devtoolsOptionsUriKey: | ||
'file:///path/to/options/file', | ||
DevToolsExtensionConfig.isPubliclyHostedKey: 'false', | ||
DevToolsExtensionConfig.detectedFromStaticContextKey: 'true', | ||
}); | ||
var b = DevToolsExtensionConfig.parse({ | ||
DevToolsExtensionConfig.nameKey: 'bar', | ||
DevToolsExtensionConfig.issueTrackerKey: 'www.google.com', | ||
DevToolsExtensionConfig.versionKey: '2.1.0', | ||
DevToolsExtensionConfig.materialIconCodePointKey: 0xe638, | ||
DevToolsExtensionConfig.requiresConnectionKey: 'false', | ||
DevToolsExtensionConfig.extensionAssetsPathKey: '/absolute/path/to/bar', | ||
DevToolsExtensionConfig.devtoolsOptionsUriKey: | ||
'file:///path/to/options/file', | ||
DevToolsExtensionConfig.isPubliclyHostedKey: 'false', | ||
DevToolsExtensionConfig.detectedFromStaticContextKey: 'true', | ||
}); | ||
expect(takeLatestExtension(a, b), b); | ||
|
||
// Returns 'a' when 'b' has parsing errors. | ||
a = DevToolsExtensionConfig.parse({ | ||
DevToolsExtensionConfig.nameKey: 'bar', | ||
DevToolsExtensionConfig.issueTrackerKey: 'www.google.com', | ||
DevToolsExtensionConfig.versionKey: '2.1.0', | ||
DevToolsExtensionConfig.materialIconCodePointKey: 0xe638, | ||
DevToolsExtensionConfig.requiresConnectionKey: 'false', | ||
DevToolsExtensionConfig.extensionAssetsPathKey: '/absolute/path/to/bar', | ||
DevToolsExtensionConfig.devtoolsOptionsUriKey: | ||
'file:///path/to/options/file', | ||
DevToolsExtensionConfig.isPubliclyHostedKey: 'false', | ||
DevToolsExtensionConfig.detectedFromStaticContextKey: 'true', | ||
}); | ||
b = DevToolsExtensionConfig.parse({ | ||
DevToolsExtensionConfig.nameKey: 'bar', | ||
DevToolsExtensionConfig.issueTrackerKey: 'www.google.com', | ||
DevToolsExtensionConfig.versionKey: 'this-will-not-parse', | ||
DevToolsExtensionConfig.materialIconCodePointKey: 0xe638, | ||
DevToolsExtensionConfig.requiresConnectionKey: 'false', | ||
DevToolsExtensionConfig.extensionAssetsPathKey: '/path/to/bar', | ||
DevToolsExtensionConfig.devtoolsOptionsUriKey: '/path/to/options/file', | ||
DevToolsExtensionConfig.isPubliclyHostedKey: 'false', | ||
DevToolsExtensionConfig.detectedFromStaticContextKey: 'true', | ||
}); | ||
expect(takeLatestExtension(a, b), a); | ||
|
||
// Returns 'a' when both 'a' and 'b' have parsing errors. | ||
a = DevToolsExtensionConfig.parse({ | ||
DevToolsExtensionConfig.nameKey: 'bar', | ||
DevToolsExtensionConfig.issueTrackerKey: 'www.google.com', | ||
DevToolsExtensionConfig.versionKey: 'this-will-not-parse', | ||
DevToolsExtensionConfig.materialIconCodePointKey: 0xe638, | ||
DevToolsExtensionConfig.requiresConnectionKey: 'false', | ||
DevToolsExtensionConfig.extensionAssetsPathKey: '/absolute/path/to/bar', | ||
DevToolsExtensionConfig.devtoolsOptionsUriKey: | ||
'file:///path/to/options/file', | ||
DevToolsExtensionConfig.isPubliclyHostedKey: 'false', | ||
DevToolsExtensionConfig.detectedFromStaticContextKey: 'true', | ||
}); | ||
b = DevToolsExtensionConfig.parse({ | ||
DevToolsExtensionConfig.nameKey: 'bar', | ||
DevToolsExtensionConfig.issueTrackerKey: 'www.google.com', | ||
DevToolsExtensionConfig.versionKey: 'this-will-not-parse', | ||
DevToolsExtensionConfig.materialIconCodePointKey: 0xe638, | ||
DevToolsExtensionConfig.requiresConnectionKey: 'false', | ||
DevToolsExtensionConfig.extensionAssetsPathKey: '/absolute/path/to/bar', | ||
DevToolsExtensionConfig.devtoolsOptionsUriKey: | ||
'file:///path/to/options/file', | ||
DevToolsExtensionConfig.isPubliclyHostedKey: 'false', | ||
DevToolsExtensionConfig.detectedFromStaticContextKey: 'true', | ||
}); | ||
expect(takeLatestExtension(a, b), a); | ||
}); | ||
}); | ||
} |
Oops, something went wrong.