From f5597f0c93b16223e046e748a6898704e7417e28 Mon Sep 17 00:00:00 2001 From: andercodder Date: Sat, 8 Nov 2025 17:03:37 +0000 Subject: [PATCH] perf: speed up language scanner tests with mini fixtures - Add mini test fixtures for short test mode - Reduce Cargo.lock from 65K to 1.8K (97% smaller) - Reduce renv.lock from 44K to 1.5K (97% smaller) - Reduce Gemfile.lock from 16K to 723B (96% smaller) - Reduce package-lock.json from 4.4K to 1.5K (66% smaller) - Add fixture path helper to select mini/full based on LONG_TESTS() - Tests now run in short mode instead of being skipped - Long tests unchanged (LONG_TESTS=1 pytest) - Maintains full coverage while improving speed Timing: 4 tests pass in ~10.6s (2.3-2.8s each) Fixes #4321 --- test/language_data_mini/Cargo.lock | 59 ++++++++++++++ test/language_data_mini/Gemfile.lock | 44 +++++++++++ test/language_data_mini/package-lock.json | 39 ++++++++++ test/language_data_mini/renv.lock | 64 +++++++++++++++ test/test_language_scanner.py | 95 +++++++++++++++-------- 5 files changed, 268 insertions(+), 33 deletions(-) create mode 100644 test/language_data_mini/Cargo.lock create mode 100644 test/language_data_mini/Gemfile.lock create mode 100644 test/language_data_mini/package-lock.json create mode 100644 test/language_data_mini/renv.lock diff --git a/test/language_data_mini/Cargo.lock b/test/language_data_mini/Cargo.lock new file mode 100644 index 0000000000..310ae744cf --- /dev/null +++ b/test/language_data_mini/Cargo.lock @@ -0,0 +1,59 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bumpalo" +version = "3.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" + +[[package]] +name = "cranelift-codegen" +version = "0.76.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48194035d2752bdd5bdae429e3ab88676e95f52a2b1355a5d4e809f9e39b1d74" + +[[package]] +name = "crossbeam-channel" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "digest" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "libc" +version = "0.2.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119" + +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" diff --git a/test/language_data_mini/Gemfile.lock b/test/language_data_mini/Gemfile.lock new file mode 100644 index 0000000000..e076e4be0d --- /dev/null +++ b/test/language_data_mini/Gemfile.lock @@ -0,0 +1,44 @@ +GIT + remote: https://github.com/matthewd/websocket-client-simple.git + revision: e161305f1a466b9398d86df3b1731b03362da91b + branch: close-race + specs: + websocket-client-simple (0.3.0) + event_emitter + websocket + +GEM + remote: https://rubygems.org/ + specs: + addressable (2.8.0) + public_suffix (>= 2.0.2, < 5.0) + builder (3.2.4) + crack (0.4.5) + rexml + digest (3.1.0) + i18n (1.8.11) + concurrent-ruby (~> 1.0) + json (2.6.1) + jwt (2.3.0) + nokogiri (1.13.1-x86_64-linux) + racc (~> 1.4) + rack (2.2.3) + rake (13.0.6) + +PLATFORMS + x86_64-linux + +DEPENDENCIES + addressable + builder + crack + digest + i18n + json + jwt + nokogiri + rack + rake + +BUNDLED WITH + 2.3.3 diff --git a/test/language_data_mini/package-lock.json b/test/language_data_mini/package-lock.json new file mode 100644 index 0000000000..792444cb94 --- /dev/null +++ b/test/language_data_mini/package-lock.json @@ -0,0 +1,39 @@ +{ + "name": "setup-python-mini", + "version": "2.2.2", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@actions/cache": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@actions/cache/-/cache-1.0.8.tgz", + "integrity": "sha512-GWNNB67w93HGJRQXlsV56YqrdAuDoP3esK/mo5mzU8WoDCVjtQgJGsTdkYUX7brswtT7xnI30bWNo1WLKQ8FZQ==", + "requires": { + "@actions/core": "^1.2.6", + "@actions/http-client": "^1.0.9" + } + }, + "@actions/core": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.6.tgz", + "integrity": "sha512-ZQYitnqiyBc3D+k7LsgSBmMDVkOVidaagDG7j3fOym77jNunWRuYx7VSHa9GNfFZh+zh61xsCjRj4JxMZlDqTA==" + }, + "@actions/http-client": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.11.tgz", + "integrity": "sha512-VRYHGQV1rqnROJqdMvGUbY/Kn8vriQe/F9HR2AlYHzmKuM/p3kjNuXhmdBfcVgsvRWTz5C5XW5xvndZrVBuAYg==" + }, + "@babel/generator": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.2.tgz", + "integrity": "sha512-1234567890abcdefghijklmnopqrstuvwxyz", + "dev": true + }, + "expect": { + "version": "27.2.5", + "resolved": "https://registry.npmjs.org/expect/-/expect-27.2.5.tgz", + "integrity": "sha512-1234567890abcdefghijklmnopqrstuvwxyz", + "dev": true + } + } +} diff --git a/test/language_data_mini/renv.lock b/test/language_data_mini/renv.lock new file mode 100644 index 0000000000..9ea942b64a --- /dev/null +++ b/test/language_data_mini/renv.lock @@ -0,0 +1,64 @@ +{ + "R": { + "Version": "4.2.0", + "Repositories": [ + { + "Name": "CRAN", + "URL": "https://cran.rstudio.com" + } + ] + }, + "Bioconductor": { + "Version": "3.15" + }, + "Packages": { + "cli": { + "Package": "cli", + "Version": "3.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "e6c5f2e8b0c4e3c7e6f5e8e8e8e8e8e8", + "Requirements": [] + }, + "curl": { + "Package": "curl", + "Version": "4.3.2", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "e6c5f2e8b0c4e3c7e6f5e8e8e8e8e8e8", + "Requirements": [] + }, + "digest": { + "Package": "digest", + "Version": "0.6.29", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "e6c5f2e8b0c4e3c7e6f5e8e8e8e8e8e8", + "Requirements": [] + }, + "openssl": { + "Package": "openssl", + "Version": "1.4.6", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "e6c5f2e8b0c4e3c7e6f5e8e8e8e8e8e8", + "Requirements": [] + }, + "yaml": { + "Package": "yaml", + "Version": "2.3.5", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "e6c5f2e8b0c4e3c7e6f5e8e8e8e8e8e8", + "Requirements": [] + }, + "zip": { + "Package": "zip", + "Version": "2.2.0", + "Source": "Repository", + "Repository": "CRAN", + "Hash": "e6c5f2e8b0c4e3c7e6f5e8e8e8e8e8e8", + "Requirements": [] + } + } +} diff --git a/test/test_language_scanner.py b/test/test_language_scanner.py index ed17b88072..0a839fcb41 100644 --- a/test/test_language_scanner.py +++ b/test/test_language_scanner.py @@ -13,11 +13,24 @@ from cve_bin_tool.log import LOGGER from cve_bin_tool.version_scanner import VersionScanner +# Module-level paths for test fixtures +TEST_FILE_PATH = Path(__file__).parent.resolve() / "language_data" +TEST_FILE_PATH_MINI = Path(__file__).parent.resolve() / "language_data_mini" + + +def get_fixture_path(filename: str) -> Path: + """Return mini fixture path for short tests, full path for long tests""" + if LONG_TESTS(): + return TEST_FILE_PATH / filename + else: + return TEST_FILE_PATH_MINI / filename + class TestLanguageScanner: """Tests for various language scanners""" - TEST_FILE_PATH = Path(__file__).parent.resolve() / "language_data" + TEST_FILE_PATH = TEST_FILE_PATH + TEST_FILE_PATH_MINI = TEST_FILE_PATH_MINI JAVASCRIPT_PRODUCTS = [ "cache", @@ -27,6 +40,14 @@ class TestLanguageScanner: "expect", ] + JAVASCRIPT_PRODUCTS_MINI = [ + "cache", + "core", + "http-client", + "generator", + "expect", + ] + RUST_PRODUCTS = [ "bumpalo", "cranelift-codegen", @@ -54,6 +75,17 @@ class TestLanguageScanner: "yaml-rust", ] + RUST_PRODUCTS_MINI = [ + "bumpalo", + "cranelift-codegen", + "crossbeam-channel", + "digest", + "generic-array", + "hex", + "libc", + "linked-hash-map", + ] + RUBY_PRODUCTS = [ "addressable", "backburner", @@ -104,6 +136,18 @@ class TestLanguageScanner: "websocket-extensions", ] + RUBY_PRODUCTS_MINI = [ + "addressable", + "builder", + "crack", + "digest", + "i18n", + "json", + "jwt", + "rack", + "rake", + ] + R_PRODUCTS = [ "cli", "clipr", @@ -124,6 +168,15 @@ class TestLanguageScanner: "zip", ] + R_PRODUCTS_MINI = [ + "cli", + "curl", + "digest", + "openssl", + "yaml", + "zip", + ] + PYTHON_PRODUCTS = [ "plotly", "zstandard", @@ -239,40 +292,22 @@ def test_language_package_none_found(self, filename: str) -> None: "filename,parser_class,products,namespace", [ pytest.param( - str(TEST_FILE_PATH / "renv.lock"), + str(get_fixture_path("renv.lock")), parsers.r.RParser, - R_PRODUCTS, + R_PRODUCTS if LONG_TESTS() else R_PRODUCTS_MINI, "cran", - marks=[ - pytest.mark.skipif( - not LONG_TESTS(), - reason="Test reduction in short tests", - ) - ], ), pytest.param( - str(TEST_FILE_PATH / "Cargo.lock"), + str(get_fixture_path("Cargo.lock")), parsers.rust.RustParser, - RUST_PRODUCTS, + RUST_PRODUCTS if LONG_TESTS() else RUST_PRODUCTS_MINI, "cargo", - marks=[ - pytest.mark.skipif( - not LONG_TESTS(), - reason="Test reduction in short tests", - ) - ], ), pytest.param( - str(TEST_FILE_PATH / "Gemfile.lock"), + str(get_fixture_path("Gemfile.lock")), parsers.ruby.RubyParser, - RUBY_PRODUCTS, + RUBY_PRODUCTS if LONG_TESTS() else RUBY_PRODUCTS_MINI, "gem", - marks=[ - pytest.mark.skipif( - not LONG_TESTS(), - reason="Test reduction in short tests", - ) - ], ), pytest.param( str(TEST_FILE_PATH / "requirements.txt"), @@ -287,16 +322,10 @@ def test_language_package_none_found(self, filename: str) -> None: ], ), pytest.param( - str(TEST_FILE_PATH / "package-lock.json"), + str(get_fixture_path("package-lock.json")), parsers.javascript.JavascriptParser, - JAVASCRIPT_PRODUCTS, + JAVASCRIPT_PRODUCTS if LONG_TESTS() else JAVASCRIPT_PRODUCTS_MINI, "npm", - marks=[ - pytest.mark.skipif( - not LONG_TESTS(), - reason="Test reduction in short tests", - ) - ], ), pytest.param( str(TEST_FILE_PATH / "go.mod"),