| // Copyright 2020 The Fuchsia 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:test/test.dart'; |
| import 'package:fxtest/fxtest.dart'; |
| |
| void main() { |
| group('fuchsia-package URLs are correctly parsed', () { |
| test('when all components are present', () { |
| PackageUrl packageUrl = PackageUrl.fromString( |
| 'fuchsia-pkg://myroot.com/pkg-name/VARIANT?hash=asdf#OMG.cmx'); |
| expect(packageUrl.host, 'myroot.com'); |
| expect(packageUrl.packageName, 'pkg-name'); |
| expect(packageUrl.packageVariant, 'VARIANT'); |
| expect(packageUrl.hash, 'asdf'); |
| expect(packageUrl.fullComponentName, 'OMG.cmx'); |
| expect(packageUrl.componentName, 'OMG'); |
| }); |
| |
| test('when all components are present plus meta', () { |
| PackageUrl packageUrl = PackageUrl.fromString( |
| 'fuchsia-pkg://myroot.com/pkg-name/VARIANT?hash=asdf#meta/OMG.cmx'); |
| expect(packageUrl.host, 'myroot.com'); |
| expect(packageUrl.packageName, 'pkg-name'); |
| expect(packageUrl.packageVariant, 'VARIANT'); |
| expect(packageUrl.hash, 'asdf'); |
| expect(packageUrl.fullComponentName, 'OMG.cmx'); |
| expect(packageUrl.componentName, 'OMG'); |
| }); |
| |
| test('when the variant is missing', () { |
| PackageUrl packageUrl = PackageUrl.fromString( |
| 'fuchsia-pkg://myroot.com/pkg-name?hash=asdf#OMG.cmx'); |
| expect(packageUrl.host, 'myroot.com'); |
| expect(packageUrl.packageName, 'pkg-name'); |
| expect(packageUrl.packageVariant, null); |
| expect(packageUrl.hash, 'asdf'); |
| expect(packageUrl.fullComponentName, 'OMG.cmx'); |
| expect(packageUrl.componentName, 'OMG'); |
| }); |
| |
| test('when the hash is missing', () { |
| PackageUrl packageUrl = PackageUrl.fromString( |
| 'fuchsia-pkg://myroot.com/pkg-name/VARIANT#OMG.cmx'); |
| expect(packageUrl.host, 'myroot.com'); |
| expect(packageUrl.packageName, 'pkg-name'); |
| expect(packageUrl.packageVariant, 'VARIANT'); |
| expect(packageUrl.hash, null); |
| expect(packageUrl.fullComponentName, 'OMG.cmx'); |
| expect(packageUrl.componentName, 'OMG'); |
| }); |
| |
| test('when the resource path is missing', () { |
| PackageUrl packageUrl = PackageUrl.fromString( |
| 'fuchsia-pkg://myroot.com/pkg-name/VARIANT?hash=asdf'); |
| expect(packageUrl.host, 'myroot.com'); |
| expect(packageUrl.packageName, 'pkg-name'); |
| expect(packageUrl.packageVariant, 'VARIANT'); |
| expect(packageUrl.hash, 'asdf'); |
| expect(packageUrl.fullComponentName, ''); |
| expect(packageUrl.componentName, ''); |
| }); |
| |
| test('when the variant and hash are missing', () { |
| PackageUrl packageUrl = |
| PackageUrl.fromString('fuchsia-pkg://myroot.com/pkg-name#OMG.cmx'); |
| expect(packageUrl.host, 'myroot.com'); |
| expect(packageUrl.packageName, 'pkg-name'); |
| expect(packageUrl.packageVariant, null); |
| expect(packageUrl.hash, null); |
| expect(packageUrl.fullComponentName, 'OMG.cmx'); |
| expect(packageUrl.componentName, 'OMG'); |
| }); |
| test('when the variant and resource path are missing', () { |
| PackageUrl packageUrl = |
| PackageUrl.fromString('fuchsia-pkg://myroot.com/pkg-name?hash=asdf'); |
| expect(packageUrl.host, 'myroot.com'); |
| expect(packageUrl.packageName, 'pkg-name'); |
| expect(packageUrl.packageVariant, null); |
| expect(packageUrl.hash, 'asdf'); |
| expect(packageUrl.fullComponentName, ''); |
| expect(packageUrl.componentName, ''); |
| }); |
| |
| test('when the hash and resource path are missing', () { |
| PackageUrl packageUrl = |
| PackageUrl.fromString('fuchsia-pkg://myroot.com/pkg-name/VARIANT'); |
| expect(packageUrl.host, 'myroot.com'); |
| expect(packageUrl.packageName, 'pkg-name'); |
| expect(packageUrl.packageVariant, 'VARIANT'); |
| expect(packageUrl.hash, null); |
| expect(packageUrl.fullComponentName, ''); |
| expect(packageUrl.componentName, ''); |
| }); |
| |
| test('when the variant, hash, and resource path are missing', () { |
| PackageUrl packageUrl = |
| PackageUrl.fromString('fuchsia-pkg://myroot.com/pkg-name'); |
| expect(packageUrl.host, 'myroot.com'); |
| expect(packageUrl.packageName, 'pkg-name'); |
| expect(packageUrl.packageVariant, null); |
| expect(packageUrl.hash, null); |
| expect(packageUrl.fullComponentName, ''); |
| expect(packageUrl.componentName, ''); |
| }); |
| }); |
| |
| group('slash splitter', () { |
| var sep = '/'; |
| test('correctly handles null', () { |
| expect(chunkOnSlashes(null, null, sep: sep), <String>[]); |
| expect(chunkOnSlashes('asdf', null, sep: sep), <String>['asdf']); |
| expect(chunkOnSlashes(null, 'asdf', sep: sep), <String>[]); |
| }); |
| |
| test('correctly handles empty strings', () { |
| expect(chunkOnSlashes('', '', sep: sep), <String>['']); |
| expect(chunkOnSlashes('', 'asdf', sep: sep), <String>['']); |
| expect(chunkOnSlashes('asdf', '', sep: sep), <String>['asdf']); |
| |
| expect(chunkOnSlashes('', 'asdf/asdf', sep: sep), <String>['']); |
| expect(chunkOnSlashes('asdf/asdf', '', sep: sep), <String>['asdf']); |
| expect( |
| chunkOnSlashes('asdf/ASDF', '', sep: sep), <String>['asdf', 'ASDF']); |
| }); |
| |
| test('correctly handles one slash in needle', () { |
| expect(chunkOnSlashes('', 'asdf/asdf', sep: sep), <String>['']); |
| expect(chunkOnSlashes('TEST', 'asdf/asdf', sep: sep), <String>['TEST']); |
| expect(chunkOnSlashes('abc/abc', 'asdf/asdf', sep: sep), |
| <String>['abc/abc']); |
| expect(chunkOnSlashes('abc/ABC/xyz', 'asdf/asdf', sep: sep), <String>[ |
| 'abc/ABC', |
| 'ABC/xyz', |
| ]); |
| expect(chunkOnSlashes('abc/ABC/xyz/abc', 'asdf/asdf', sep: sep), <String>[ |
| 'abc/ABC', |
| 'ABC/xyz', |
| 'xyz/abc', |
| ]); |
| }); |
| |
| test('correctly handles more than one slash in needle', () { |
| expect(chunkOnSlashes('', 'asdf/asdf/asdf', sep: sep), <String>['']); |
| expect( |
| chunkOnSlashes('TEST', 'asdf/asdf/asdf', sep: sep), <String>['TEST']); |
| expect(chunkOnSlashes('abc/abc', 'asdf/asdf/asdf', sep: sep), |
| <String>['abc/abc']); |
| expect( |
| chunkOnSlashes('abc/ABC/xyz', 'asdf/asdf/asdf', sep: sep), <String>[ |
| 'abc/ABC/xyz', |
| ]); |
| expect( |
| chunkOnSlashes('abc/ABC/xyz/abc', 'asdf/asdf/asdf', sep: sep), |
| <String>[ |
| 'abc/ABC/xyz', |
| 'ABC/xyz/abc', |
| ]); |
| }); |
| |
| test('correctly handles leading slashes', () { |
| expect( |
| chunkOnSlashes('//some/test', 'a/a'), |
| <String>['/', '/some', 'some/test'], |
| ); |
| }); |
| }); |
| |
| group('fuzzy comparer correctly evaluates', () { |
| var fuzzy = FuzzyComparer(threshold: 4); |
| test('equals with no slashes', () { |
| expect(fuzzy.equals('asdf', 'asdF').isMatch, true); |
| expect(fuzzy.equals('asdf', 'asDF').isMatch, true); |
| expect(fuzzy.equals('asdf', 'aSDF').isMatch, true); |
| expect(fuzzy.equals('asdf', 'ASDF').isMatch, false); |
| }); |
| test('equals with one slash in haystack', () { |
| expect(fuzzy.equals('ASDF/asdf', 'asdf').isMatch, false); |
| expect(fuzzy.equals('/asdf', 'asdf').isMatch, true); |
| expect(fuzzy.equals('/asdf', 'asdF').isMatch, true); |
| }); |
| test('equals with one slash in needle', () { |
| expect(fuzzy.equals('asdf', 'asdf/ASDF').isMatch, false); |
| expect(fuzzy.equals('asdf/asdF', 'asdf/ASDF').isMatch, true); |
| expect(fuzzy.equals('asdf/asdf', 'asdf/ASDF').isMatch, false); |
| }); |
| test('contains with zero slashes in the needle', () { |
| expect(fuzzy.contains('xyz/ASDF/XYZ', 'asdf').isMatch, false); |
| expect(fuzzy.contains('xyz/ASDF/abc', 'asdf').isMatch, |
| true); // abc -> asdf == 3 |
| expect(fuzzy.contains('xyz/aSDF/abc', 'asdf').isMatch, true); |
| expect(fuzzy.contains('xyz/asDF/abc', 'asdf').isMatch, true); |
| expect(fuzzy.contains('xyz/asdF/abc', 'asdf').isMatch, true); |
| expect(fuzzy.contains('xyz/asdf/abc', 'asdf').isMatch, true); |
| }); |
| test('contains with one slash in the needle', () { |
| expect(fuzzy.contains('abc/asdf/ASDF', 'asdf/ASDF').isMatch, true); |
| expect(fuzzy.contains('abc/asdf/aSDF', 'asdf/ASDF').isMatch, true); |
| expect(fuzzy.contains('abc/asdf/asDF', 'asdf/ASDF').isMatch, true); |
| expect(fuzzy.contains('abc/asdf/asdF', 'asdf/ASDF').isMatch, true); |
| expect(fuzzy.contains('abc/asdf/asdf', 'asdf/ASDF').isMatch, false); |
| }); |
| test('contains with two slashes in the needle', () { |
| expect( |
| fuzzy.contains('abc/asdf/ASDF/xyz', 'asdf/ASDF/xyz').isMatch, true); |
| expect( |
| fuzzy.contains('abc/asdf/aSDF/xyZ', 'asdf/ASDF/xyz').isMatch, true); |
| expect(fuzzy.contains('abc/asdf/asdf', 'asdf/ASDF/xyz').isMatch, false); |
| expect(fuzzy.contains('abc/asdf', 'asdf/ASDF/xyz').isMatch, false); |
| expect(fuzzy.contains('abc', 'asdf/ASDF/xyz').isMatch, false); |
| expect(fuzzy.contains('abc/abc/abc', 'asdf/ASDF/xyz').isMatch, false); |
| expect(fuzzy.contains('asdf/ASDF/xyz', 'asdf/ASDF/xyz').isMatch, true); |
| expect(fuzzy.contains('asdf/ASDF/XYZ', 'asdf/ASDF/xyz').isMatch, true); |
| }); |
| test('endsWith with zero slashes in the needle', () { |
| expect(fuzzy.endsWith('asdf/ABC', 'asdf').isMatch, false); |
| expect(fuzzy.endsWith('xyz/asdf', 'asdf').isMatch, true); |
| expect(fuzzy.endsWith('xyz/aSDF', 'asdf').isMatch, true); |
| expect(fuzzy.endsWith('xyz/asdf', 'asdf').isMatch, true); |
| }); |
| test('endsWith with more than 1 slash in the needle', () { |
| expect(fuzzy.endsWith('asdf/ABC', 'asdf/ABC').isMatch, true); |
| expect(fuzzy.endsWith('asdf/aaabc', 'asdf/ABC').isMatch, false); |
| expect(fuzzy.endsWith('asdf/abc', 'asdf/ABC').isMatch, true); |
| expect(fuzzy.endsWith('asdf/aBC', 'asdf/ABC').isMatch, true); |
| expect(fuzzy.endsWith('xyz/asdf/ABC', 'asdf/ABC').isMatch, true); |
| expect(fuzzy.endsWith('xyz/asdf/aaabc', 'asdf/ABC').isMatch, false); |
| expect(fuzzy.endsWith('xyz/asdf/abc', 'asdf/ABC').isMatch, true); |
| expect(fuzzy.endsWith('xyz/asdf/aBC', 'asdf/ABC').isMatch, true); |
| expect(fuzzy.endsWith('asdf/ABC/xyz', 'asdf/ABC').isMatch, false); |
| expect(fuzzy.endsWith('asdf/aaabc/xyz', 'asdf/ABC').isMatch, false); |
| expect(fuzzy.endsWith('asdf/abc/xyz', 'asdf/ABC').isMatch, false); |
| expect(fuzzy.endsWith('asdf/aBC/xyz', 'asdf/ABC').isMatch, false); |
| }); |
| test('startsWith with zero slashes in the needle', () { |
| expect(fuzzy.startsWith('asdf/ABC', 'asdf').isMatch, true); |
| expect(fuzzy.startsWith('xyz/asdf', 'asdf').isMatch, false); |
| expect(fuzzy.startsWith('aSDF/xyz', 'asdf').isMatch, true); |
| expect(fuzzy.startsWith('asdf/xyz', 'asdf').isMatch, true); |
| }); |
| test('startsWith with more than 1 slash in the needle', () { |
| expect(fuzzy.startsWith('asdf/ABC', 'asdf/ABC').isMatch, true); |
| expect(fuzzy.startsWith('asdf/aaabc', 'asdf/ABC').isMatch, false); |
| expect(fuzzy.startsWith('asdf/abc', 'asdf/ABC').isMatch, true); |
| expect(fuzzy.startsWith('asdf/aBC', 'asdf/ABC').isMatch, true); |
| expect(fuzzy.startsWith('xyz/asdf/ABC', 'asdf/ABC').isMatch, false); |
| expect(fuzzy.startsWith('xyz/asdf/aaabc', 'asdf/ABC').isMatch, false); |
| expect(fuzzy.startsWith('xyz/asdf/abc', 'asdf/ABC').isMatch, false); |
| expect(fuzzy.startsWith('xyz/asdf/aBC', 'asdf/ABC').isMatch, false); |
| expect(fuzzy.startsWith('asdf/ABC/xyz', 'asdf/ABC').isMatch, true); |
| expect(fuzzy.startsWith('asdf/aaabc/xyz', 'asdf/ABC').isMatch, false); |
| expect(fuzzy.startsWith('asdf/abc/xyz', 'asdf/ABC').isMatch, true); |
| expect(fuzzy.startsWith('asdf/aBC/xyz', 'asdf/ABC').isMatch, true); |
| }); |
| |
| test('when needle has bookend slashes', () { |
| // The Levenshtein distance of abc -> ABC is 3, which is the maximum |
| // allowed given our threshold of 4. This tests verifies that the |
| // trailing slash in our needle does not ruin results. |
| expect(fuzzy.endsWith('abc/asdf/ABC', 'asdf/abc/').isMatch, true); |
| expect(fuzzy.startsWith('asdf/ABC', '/asdf/abc/').isMatch, true); |
| }); |
| }); |
| |
| group('confidence computed correctly', () { |
| test('with threshold of 5', () { |
| var comparer = FuzzyComparer(threshold: 5); |
| expect(comparer.computeConfidence(1), 0.8); |
| expect(comparer.computeConfidence(2), 0.6); |
| expect(comparer.computeConfidence(3), 0.4); |
| expect(comparer.computeConfidence(4), 0.2); |
| expect(comparer.computeConfidence(5), 0); |
| }); |
| }); |
| group('getMapPath works with', () { |
| Map<String, dynamic> data = { |
| 'top': 'level', |
| 'nested': { |
| 'data': 'is cool too', |
| }, |
| 'numbers': { |
| 'one': 1, |
| 'two': '2', |
| }, |
| 'true': true, |
| 'false': false, |
| 'list': [ |
| {'key': 'value'}, |
| {'KEY': 'VALUE'}, |
| ], |
| 'listWithLists': [ |
| 1, |
| [2, 3], |
| ], |
| }; |
| test('too many keys', () { |
| expect( |
| // `top` key exists, but `extra` and `keys` cannot be satisfied |
| () => getMapPath(data, ['top', 'extra', 'keys']), |
| throwsA(TypeMatcher<BadMapPathException>()), |
| ); |
| }); |
| test('lists', () { |
| expect(getMapPath(data, ['list', 1, 'KEY']), 'VALUE'); |
| }); |
| test('listsWithLists', () { |
| expect(getMapPath(data, ['listWithLists', 1, 1]), 3); |
| }); |
| test('listsWithLists that ends before terminal node', () { |
| expect(getMapPath<List<int>>(data, ['listWithLists', 1]), <int>[2, 3]); |
| }); |
| test('returning whole lists', () { |
| expect(getMapPath(data, ['list']), [ |
| {'key': 'value'}, |
| {'KEY': 'VALUE'}, |
| ]); |
| }); |
| test( |
| 'top-level children', |
| () => expect(getMapPath<String>(data, ['top']), 'level'), |
| ); |
| test( |
| 'nested children', |
| () => expect(getMapPath<String>(data, ['nested', 'data']), 'is cool too'), |
| ); |
| test( |
| 'missing children', |
| () => expect(getMapPath<String>(data, ['missing key']), null), |
| ); |
| test( |
| 'partially correct paths', |
| () => expect(getMapPath<String>(data, ['nested', 'missing key']), null), |
| ); |
| test( |
| 'return types honored', |
| () { |
| expect(getMapPath<int>(data, ['numbers', 'one']), 1); |
| expect( |
| getMapPath<int>(data, ['numbers', 'two'], |
| // (this lambda is actually necessary because `parse` takes other |
| // optional parameters which break type safety if applied to |
| // `caster`) |
| // ignore: unnecessary_lambdas |
| caster: (val) => int.parse(val)), |
| 2); |
| }, |
| ); |
| test( |
| 'when T is redundant', |
| () { |
| expect( |
| getMapPath<int>(data, ['numbers', 'one'], |
| // (this lambda is actually necessary because `parse` takes other |
| // optional parameters which break type safety if applied to |
| // `caster`) |
| // ignore: unnecessary_lambdas |
| caster: (val) => int.parse(val)), |
| 1); |
| }, |
| ); |
| test( |
| 'when T == bool', () => expect(getMapPath<bool>(data, ['true']), true)); |
| test('no input', () => expect(getMapPath(null, ['asdf']), null)); |
| }); |
| } |