From 5d7bf9b1aa2128ed1143e622acc10754bb7aa0a8 Mon Sep 17 00:00:00 2001 From: Istvan Soos Date: Wed, 24 Sep 2025 16:48:48 +0200 Subject: [PATCH 1/2] Test for known license range matches. --- .../license_detection/license_detector.dart | 5 +- test/licenses/license_coverage_test.dart | 226 ++++++++++++++++++ 2 files changed, 229 insertions(+), 2 deletions(-) create mode 100644 test/licenses/license_coverage_test.dart diff --git a/lib/src/license_detection/license_detector.dart b/lib/src/license_detection/license_detector.dart index 9e4ef725e..8925e84fe 100644 --- a/lib/src/license_detection/license_detector.dart +++ b/lib/src/license_detection/license_detector.dart @@ -25,7 +25,8 @@ const _defaultSpdxLicenseDir = 'lib/src/third_party/spdx/licenses'; // Load corpus licenses. List? _cachedLicenses; -Future> _getDefaultLicenses() async { +@visibleForTesting +Future> listDefaultLicenses() async { if (_cachedLicenses == null) { final uri = await Isolate.resolvePackageUri( Uri.parse(_defaultSpdxLicenseDir.replaceFirst('lib/', 'package:pana/')), @@ -69,7 +70,7 @@ Future detectLicense(String text, double threshold) async { final possibleLicenses = filter( unknownLicense.tokenFrequency, - await _getDefaultLicenses(), + await listDefaultLicenses(), ).map((e) => LicenseWithNGrams.parse(e, granularity)); var result = []; diff --git a/test/licenses/license_coverage_test.dart b/test/licenses/license_coverage_test.dart new file mode 100644 index 000000000..3d47dde55 --- /dev/null +++ b/test/licenses/license_coverage_test.dart @@ -0,0 +1,226 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. 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:pana/src/license.dart'; +import 'package:pana/src/license_detection/license_detector.dart'; +import 'package:test/test.dart'; + +void main() { + group('License coverage', () { + test('Coverage of all reference text', () async { + final allLicenses = await listDefaultLicenses(); + expect(allLicenses, hasLength(greaterThan(30))); + for (final license in allLicenses) { + final detected = await detectLicenseInContent( + license.content, + relativePath: 'LICENSE', + ); + // TODO: fix detection and return at least one match + if (license.identifier == 'SSH-OpenSSH') { + continue; + } + final match = detected + .where((l) => l.spdxIdentifier == license.identifier) + .single; + + // TODO: fix coverage calculation as 1:1 matches shouldn't have too many ranges + final excessiveLicenses = [ + 'AFL-2.0', + 'AFL-2.1', + 'AFL-3.0', + 'AGPL-1.0', + 'AGPL-3.0', + 'AGPL-3.0', + 'APL-1.0', + 'APSL-1.0', + 'APSL-1.1', + 'APSL-1.2', + 'APSL-2.0', + 'Aladdin', + 'Apache-2.0', + 'Apache-2.0', + 'Arphic-1999', + 'Artistic-1.0', + 'Artistic-1.0-Perl', + 'Artistic-1.0-cl8', + 'Artistic-2.0', + 'BSD-Protection', + 'BitTorrent-1.0', + 'BitTorrent-1.1', + 'CAL-1.0-Combined-Work-Exception', + 'CC-BY-1.0', + 'CC-BY-2.0', + 'CC-BY-2.5', + 'CC-BY-2.5-AU', + 'CC-BY-3.0', + 'CC-BY-3.0-AT', + 'CC-BY-3.0-DE', + 'CC-BY-3.0-NL', + 'CC-BY-3.0-US', + 'CC-BY-4.0', + 'CC-BY-NC-1.0', + 'CC-BY-NC-2.0', + 'CC-BY-NC-2.5', + 'CC-BY-NC-3.0', + 'CC-BY-NC-3.0-DE', + 'CC-BY-NC-4.0', + 'CC-BY-NC-ND-1.0', + 'CC-BY-NC-ND-2.0', + 'CC-BY-NC-ND-2.5', + 'CC-BY-NC-ND-3.0', + 'CC-BY-NC-ND-3.0-DE', + 'CC-BY-NC-ND-3.0-IGO', + 'CC-BY-NC-ND-4.0', + 'CC-BY-NC-SA-1.0', + 'CC-BY-NC-SA-2.0', + 'CC-BY-NC-SA-2.0-FR', + 'CC-BY-NC-SA-2.0-UK', + 'CC-BY-NC-SA-2.5', + 'CC-BY-NC-SA-3.0', + 'CC-BY-NC-SA-3.0-DE', + 'CC-BY-NC-SA-3.0-IGO', + 'CC-BY-NC-SA-4.0', + 'CC-BY-ND-1.0', + 'CC-BY-ND-2.0', + 'CC-BY-ND-2.5', + 'CC-BY-ND-3.0', + 'CC-BY-ND-3.0-DE', + 'CC-BY-ND-4.0', + 'CC-BY-SA-1.0', + 'CC-BY-SA-2.0', + 'CC-BY-SA-2.0-UK', + 'CC-BY-SA-2.5', + 'CC-BY-SA-3.0', + 'CC-BY-SA-3.0-AT', + 'CC-BY-SA-3.0-DE', + 'CC-BY-SA-4.0', + 'CDDL-1.0', + 'CDDL-1.1', + 'CDL-1.0', + 'CDLA-Permissive-1.0', + 'CDLA-Sharing-1.0', + 'CECILL-1.0', + 'CECILL-1.1', + 'CECILL-2.1', + 'CERN-OHL-1.2', + 'CERN-OHL-S-2.0', + 'CERN-OHL-W-2.0', + 'CPAL-1.0', + 'CPL-1.0', + 'CPOL-1.02', + 'CUA-OPL-1.0', + 'ClArtistic', + 'Community-Spec-1.0', + 'D-FSL-1.0', + 'DL-DE-BY-2.0', + 'ECL-2.0', + 'ECL-2.0', + 'EPL-1.0', + 'EPL-2.0', + 'EUPL-1.0', + 'EUPL-1.2', + 'ErlPL-1.1', + 'FTL', + 'Frameworx-1.0', + 'FreeImage', + 'GFDL-1.1', + 'GFDL-1.2', + 'GFDL-1.3', + 'GPL-1.0', + 'GPL-2.0', + 'GPL-2.0', + 'GPL-3.0', + 'GPL-3.0', + 'Glide', + 'IPA', + 'IPL-1.0', + 'Interbase-1.0', + 'LAL-1.2', + 'LAL-1.3', + 'LGPL-2.0', + 'LGPL-2.0', + 'LGPL-2.1', + 'LGPL-2.1', + 'LGPL-3.0', + 'LGPLLR', + 'LGPLLR', + 'LPL-1.0', + 'LPL-1.02', + 'LPPL-1.0', + 'LPPL-1.1', + 'LPPL-1.2', + 'LPPL-1.3a', + 'LPPL-1.3c', + 'LiLiQ-P-1.1', + 'LiLiQ-R-1.1', + 'LiLiQ-Rplus-1.1', + 'MPL-1.0', + 'MPL-1.1', + 'MPL-2.0', + 'Motosoto', + 'MulanPSL-1.0', + 'MulanPSL-2.0', + 'NASA-1.3', + 'NBPL-1.0', + 'NOSL', + 'NPL-1.0', + 'NPL-1.1', + 'NPOSL-3.0', + 'Nokia', + 'OCCT-PL', + 'ODC-By-1.0', + 'ODbL-1.0', + 'OGDL-Taiwan-1.0', + 'OLDAP-1.1', + 'OLDAP-1.2', + 'OLDAP-1.3', + 'OLDAP-1.4', + 'OPL-1.0', + 'OPUBL-1.0', + 'OSET-PL-2.1', + 'OSL-1.0', + 'OSL-1.1', + 'OSL-2.0', + 'OSL-2.1', + 'OSL-3.0', + 'PDDL-1.0', + 'Parity-7.0.0', + 'Python-2.0', + 'RHeCos-1.1', + 'RPL-1.1', + 'RPL-1.5', + 'RPSL-1.0', + 'RSCPL', + 'SCEA', + 'SGI-B-1.0', + 'SGI-B-1.1', + 'SHL-0.5', + 'SHL-0.5', + 'SHL-0.51', + 'SHL-0.51', + 'SISSL', + 'SNIA', + 'SPL-1.0', + 'SSPL-1.0', + 'SSPL-1.0', + 'Sendmail', + 'Sendmail-8.23', + 'SugarCRM-1.1.3', + 'TAPR-OHL-1.0', + 'UCL-1.0', + 'Unicode-TOU', + 'Watcom-1.0', + 'copyleft-next-0.3.0', + 'copyleft-next-0.3.1', + 'etalab-2.0', + 'gSOAP-1.3b', + ]; + if (excessiveLicenses.contains(license.identifier)) { + continue; + } + expect(match.range!.coverages, hasLength(lessThan(50))); + } + }); + }); +} From 820e35a64d18e1d50c8ae760eacae3ca0178cef8 Mon Sep 17 00:00:00 2001 From: Istvan Soos Date: Thu, 25 Sep 2025 13:47:45 +0200 Subject: [PATCH 2/2] issues --- test/licenses/license_coverage_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/licenses/license_coverage_test.dart b/test/licenses/license_coverage_test.dart index 3d47dde55..ce390f9c5 100644 --- a/test/licenses/license_coverage_test.dart +++ b/test/licenses/license_coverage_test.dart @@ -16,7 +16,7 @@ void main() { license.content, relativePath: 'LICENSE', ); - // TODO: fix detection and return at least one match + // TODO: fix detection and return at least one match (https://github.com/dart-lang/pana/issues/1484) if (license.identifier == 'SSH-OpenSSH') { continue; } @@ -24,7 +24,7 @@ void main() { .where((l) => l.spdxIdentifier == license.identifier) .single; - // TODO: fix coverage calculation as 1:1 matches shouldn't have too many ranges + // TODO: fix coverage calculation as 1:1 matches shouldn't have too many ranges (https://github.com/dart-lang/pana/issues/1481) final excessiveLicenses = [ 'AFL-2.0', 'AFL-2.1',