tests: inline more test logic

This commit is contained in:
Willi Ballenthin
2026-04-21 17:04:41 +03:00
parent d71c6ee9e5
commit 0be96ed04f
4 changed files with 37 additions and 132 deletions

View File

@@ -44,8 +44,7 @@ from capa.features.extractors.dnfile.extractor import DnfileFeatureExtractor
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
CD = Path(__file__).resolve().parent CD = Path(__file__).resolve().parent
FIXTURE_MANIFEST_DIR = CD / "fixtures" / "features" FIXTURE_MANIFEST_DIR = CD / "fixtures" / "features"
DOTNET_DIR = CD / "data" / "dotnet" DNFILE_TESTFILES = CD / "data" / "dotnet" / "dnfile-testfiles"
DNFILE_TESTFILES = DOTNET_DIR / "dnfile-testfiles"
def parse_feature_string(s: str) -> Feature | ceng.Range | ceng.Statement: def parse_feature_string(s: str) -> Feature | ceng.Range | ceng.Statement:
@@ -366,44 +365,6 @@ def run_feature_fixture(
assert actual == fixture.expected, msg assert actual == fixture.expected, msg
@contextlib.contextmanager
def xfail(condition, reason: str = ""):
"""
context manager that wraps a block that is expected to fail in some cases.
when it does fail (and is expected), then mark this as pytest.xfail.
if its unexpected, raise an exception, so the test fails.
example::
# this test:
# - passes on Linux if foo() works
# - fails on Linux if foo() fails
# - xfails on Windows if foo() fails
# - fails on Windows if foo() works
with xfail(sys.platform == "win32", reason="doesn't work on Windows"):
foo()
"""
try:
# do the block
yield
except Exception:
if condition:
# we expected the test to fail, so raise and register this via pytest
pytest.xfail(reason or "")
else:
# we don't expect an exception, so the test should fail
raise
else:
if not condition:
# here we expect the block to run successfully,
# and we've received no exception,
# so this is good
pass
else:
# we expected an exception, but didn't find one. that's an error.
raise RuntimeError("expected to fail, but didn't")
def extract_global_features(extractor): def extract_global_features(extractor):
features = collections.defaultdict(set) features = collections.defaultdict(set)
for feature, va in extractor.extract_global_features(): for feature, va in extractor.extract_global_features():
@@ -671,11 +632,6 @@ def resolve_scope(scope):
raise ValueError("unexpected scope fixture") raise ValueError("unexpected scope fixture")
@pytest.fixture
def scope(request):
return resolve_scope(request.param)
def make_test_id(values): def make_test_id(values):
return "-".join(map(str, values)) return "-".join(map(str, values))
@@ -692,58 +648,7 @@ def parametrize(params, values, **kwargs):
return pytest.mark.parametrize(params, values, ids=ids, **kwargs) return pytest.mark.parametrize(params, values, ids=ids, **kwargs)
def get_result_doc(path: Path):
return capa.render.result_document.ResultDocument.from_file(path)
@pytest.fixture
def pma0101_rd():
# python -m capa.main tests/data/Practical\ Malware\ Analysis\ Lab\ 01-01.dll_ --json > tests/data/rd/Practical\ Malware\ Analysis\ Lab\ 01-01.dll_.json
return get_result_doc(CD / "data" / "rd" / "Practical Malware Analysis Lab 01-01.dll_.json")
@pytest.fixture
def dotnet_1c444e_rd():
# .NET sample
# python -m capa.main tests/data/dotnet/1c444ebeba24dcba8628b7dfe5fec7c6.exe_ --json > tests/data/rd/1c444ebeba24dcba8628b7dfe5fec7c6.exe_.json
return get_result_doc(CD / "data" / "rd" / "1c444ebeba24dcba8628b7dfe5fec7c6.exe_.json")
@pytest.fixture
def a3f3bbc_rd():
# python -m capa.main tests/data/3f3bbcf8fd90bdcdcdc5494314ed4225.exe_ --json > tests/data/rd/3f3bbcf8fd90bdcdcdc5494314ed4225.exe_.json
return get_result_doc(CD / "data" / "rd" / "3f3bbcf8fd90bdcdcdc5494314ed4225.exe_.json")
@pytest.fixture
def al_khaserx86_rd():
# python -m capa.main tests/data/al-khaser_x86.exe_ --json > tests/data/rd/al-khaser_x86.exe_.json
return get_result_doc(CD / "data" / "rd" / "al-khaser_x86.exe_.json")
@pytest.fixture
def al_khaserx64_rd():
# python -m capa.main tests/data/al-khaser_x64.exe_ --json > tests/data/rd/al-khaser_x64.exe_.json
return get_result_doc(CD / "data" / "rd" / "al-khaser_x64.exe_.json")
@pytest.fixture
def a076114_rd():
# python -m capa.main tests/data/0761142efbda6c4b1e801223de723578.dll_ --json > tests/data/rd/0761142efbda6c4b1e801223de723578.dll_.json
return get_result_doc(CD / "data" / "rd" / "0761142efbda6c4b1e801223de723578.dll_.json")
@pytest.fixture
def dynamic_a0000a6_rd():
# python -m capa.main tests/data/dynamic/cape/v2.2/0000a65749f5902c4d82ffa701198038f0b4870b00a27cfca109f8f933476d82.json --json > tests/data/rd/0000a65749f5902c4d82ffa701198038f0b4870b00a27cfca109f8f933476d82.json
# gzip tests/data/rd/0000a65749f5902c4d82ffa701198038f0b4870b00a27cfca109f8f933476d82.json
return get_result_doc(
CD / "data" / "rd" / "0000a65749f5902c4d82ffa701198038f0b4870b00a27cfca109f8f933476d82.json.gz"
)
PMA1601 = CD / "data" / "Practical Malware Analysis Lab 16-01.exe_" PMA1601 = CD / "data" / "Practical Malware Analysis Lab 16-01.exe_"
z9324 = CD / "data" / "9324d1a8ae37a36ae560c37448c9705a.exe_"
# used by test_viv_features # used by test_viv_features
@@ -786,7 +691,7 @@ def get_viv_extractor(path: Path):
@pytest.fixture @pytest.fixture
def z9324d_extractor(): def z9324d_extractor():
return get_viv_extractor(z9324) return get_viv_extractor(CD / "data" / "9324d1a8ae37a36ae560c37448c9705a.exe_")
@pytest.fixture @pytest.fixture

View File

@@ -14,10 +14,10 @@
import textwrap import textwrap
import capa.rules
import capa.features.common
import fixtures import fixtures
import capa.rules
import capa.features.common
import capa.capabilities.common import capa.capabilities.common
import capa.features.extractors.null import capa.features.extractors.null
from capa.features.address import AbsoluteVirtualAddress from capa.features.address import AbsoluteVirtualAddress

View File

@@ -14,6 +14,7 @@
import copy import copy
from typing import Any from typing import Any
from pathlib import Path
import pytest import pytest
@@ -28,20 +29,21 @@ import capa.render.result_document as rd
import capa.features.freeze.features import capa.features.freeze.features
from capa.helpers import assert_never from capa.helpers import assert_never
CD = Path(__file__).resolve().parent
@pytest.mark.parametrize( STATIC_RD_FILES = [
"rd_file", pytest.param(CD / "data" / "rd" / "3f3bbcf8fd90bdcdcdc5494314ed4225.exe_.json", id="a3f3bbc"),
[ pytest.param(CD / "data" / "rd" / "al-khaser_x86.exe_.json", id="al_khaserx86"),
pytest.param("a3f3bbc_rd"), pytest.param(CD / "data" / "rd" / "al-khaser_x64.exe_.json", id="al_khaserx64"),
pytest.param("al_khaserx86_rd"), pytest.param(CD / "data" / "rd" / "0761142efbda6c4b1e801223de723578.dll_.json", id="a076114"),
pytest.param("al_khaserx64_rd"), pytest.param(CD / "data" / "rd" / "Practical Malware Analysis Lab 01-01.dll_.json", id="pma0101"),
pytest.param("a076114_rd"), pytest.param(CD / "data" / "rd" / "1c444ebeba24dcba8628b7dfe5fec7c6.exe_.json", id="dotnet_1c444e"),
pytest.param("pma0101_rd"), ]
pytest.param("dotnet_1c444e_rd"),
],
) @pytest.mark.parametrize("rd_path", STATIC_RD_FILES)
def test_doc_to_pb2(request, rd_file): def test_doc_to_pb2(rd_path):
src: rd.ResultDocument = request.getfixturevalue(rd_file) src = rd.ResultDocument.from_file(rd_path)
dst = capa.render.proto.doc_to_pb2(src) dst = capa.render.proto.doc_to_pb2(src)
assert_meta(src.meta, dst.meta) assert_meta(src.meta, dst.meta)
@@ -401,17 +403,15 @@ def assert_round_trip(doc: rd.ResultDocument):
@pytest.mark.parametrize( @pytest.mark.parametrize(
"rd_file", "rd_path",
[ STATIC_RD_FILES
pytest.param("a3f3bbc_rd"), + [
pytest.param("al_khaserx86_rd"), pytest.param(
pytest.param("al_khaserx64_rd"), CD / "data" / "rd" / "0000a65749f5902c4d82ffa701198038f0b4870b00a27cfca109f8f933476d82.json.gz",
pytest.param("a076114_rd"), id="dynamic_a0000a6",
pytest.param("pma0101_rd"), ),
pytest.param("dotnet_1c444e_rd"),
pytest.param("dynamic_a0000a6_rd"),
], ],
) )
def test_round_trip(request, rd_file): def test_round_trip(rd_path):
doc: rd.ResultDocument = request.getfixturevalue(rd_file) doc = rd.ResultDocument.from_file(rd_path)
assert_round_trip(doc) assert_round_trip(doc)

View File

@@ -268,18 +268,18 @@ def assert_round_trip(rd: rdoc.ResultDocument):
@pytest.mark.parametrize( @pytest.mark.parametrize(
"rd_file", "rd_path",
[ [
pytest.param("a3f3bbc_rd"), pytest.param(fixtures.CD / "data" / "rd" / "3f3bbcf8fd90bdcdcdc5494314ed4225.exe_.json", id="a3f3bbc"),
pytest.param("al_khaserx86_rd"), pytest.param(fixtures.CD / "data" / "rd" / "al-khaser_x86.exe_.json", id="al_khaserx86"),
pytest.param("al_khaserx64_rd"), pytest.param(fixtures.CD / "data" / "rd" / "al-khaser_x64.exe_.json", id="al_khaserx64"),
pytest.param("a076114_rd"), pytest.param(fixtures.CD / "data" / "rd" / "0761142efbda6c4b1e801223de723578.dll_.json", id="a076114"),
pytest.param("pma0101_rd"), pytest.param(fixtures.CD / "data" / "rd" / "Practical Malware Analysis Lab 01-01.dll_.json", id="pma0101"),
pytest.param("dotnet_1c444e_rd"), pytest.param(fixtures.CD / "data" / "rd" / "1c444ebeba24dcba8628b7dfe5fec7c6.exe_.json", id="dotnet_1c444e"),
], ],
) )
def test_round_trip(request, rd_file): def test_round_trip(rd_path):
rd: rdoc.ResultDocument = request.getfixturevalue(rd_file) rd = rdoc.ResultDocument.from_file(rd_path)
assert_round_trip(rd) assert_round_trip(rd)