Skip to content
This repository was archived by the owner on Mar 9, 2026. It is now read-only.

Commit 51dba3c

Browse files
committed
Add basic tests of example excavation/output + groundwork for unified run.
Make the minimal set of changes such that we are able to trigger the excavation to a known directory of the in-tree example file. Check that the expected HTML files were written for this known excavation and assert some basic properties of the excavated artifacts. The commit includes reworking of run_example wherein its excavation is declared as a class and its core logic separated out into a run.py file. This is done both to allow the tests to easily make use of the logic and as preliminary bits for subsequent work to provide a unified entry point.
1 parent df73880 commit 51dba3c

File tree

6 files changed

+187
-27
lines changed

6 files changed

+187
-27
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ venv/
1212
# Temporary files
1313
_.*
1414
/output/
15+
/tests/_scratch/

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,6 @@ default: example
22

33
example:
44
./venv/bin/python3 run_example.py -d .
5+
6+
test:
7+
@./venv/bin/python3 -m unittest

run.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import argparse
2+
import os
3+
import sys
4+
5+
from autoarchaeologist import Excavation
6+
7+
def parse_arguments(argv=None):
8+
parser = argparse.ArgumentParser()
9+
parser.add_argument("-d", "--dir", default="/tmp/_autoarchaologist")
10+
parser.add_argument('filename')
11+
12+
return parser.parse_args(args=argv)
13+
14+
def process_arguments(args):
15+
if args.dir == ".":
16+
args.dir = os.path.join(os.getcwd(), "output", "_autoarchaologist")
17+
if args.filename is not None:
18+
args.filename = os.path.abspath(args.filename)
19+
else:
20+
raise ValueError()
21+
22+
return args
23+
24+
def perform_excavation(args, action_tuple):
25+
match action_tuple:
26+
case "excavator", AnExcavation:
27+
assert issubclass(AnExcavation, Excavation)
28+
ctx = AnExcavation(html_dir=args.dir)
29+
case action, _:
30+
raise NotImplementedError(f"action: {action}")
31+
32+
ff = ctx.add_file_artifact(args.filename)
33+
34+
ctx.start_examination()
35+
36+
return ctx
37+
38+
if __name__ == "__main__":
39+
args = process_arguments(parse_arguments())
40+
41+
try:
42+
os.mkdir(args.dir)
43+
except FileExistsError:
44+
pass
45+
46+
ctx = perform_excavation(args, ("none", None))
47+
ctx.produce_html()
48+
print("Now point your browser at", ctx.filename_for(ctx).link)

run_example.py

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,32 @@
1-
2-
import argparse
31
import os
42
import sys
3+
from types import SimpleNamespace
54

6-
import autoarchaeologist
7-
5+
from run import parse_arguments, process_arguments, perform_excavation
6+
from autoarchaeologist.base.excavation import Excavation
87
from autoarchaeologist.generic.bigtext import BigText
98
from autoarchaeologist.generic.samesame import SameSame
109
from autoarchaeologist.data_general.absbin import AbsBin
1110
from autoarchaeologist.data_general.papertapechecksum import DGC_PaperTapeCheckSum
1211

13-
def parse_arguments(argv=None):
14-
parser = argparse.ArgumentParser()
15-
parser.add_argument("-d", "--dir", default="/tmp/_autoarchaologist")
1612

17-
args = parser.parse_args(args=argv)
18-
if args.dir == ".":
19-
args.dir = os.path.join(os.getcwd(), "output", "_autoarchaologist")
20-
return args
21-
22-
if __name__ == "__main__":
23-
args = parse_arguments()
13+
class ExampleExcavation(Excavation):
14+
def __init__(self, **kwargs):
15+
super().__init__(**kwargs)
2416

25-
try:
26-
os.mkdir(args.dir)
27-
except FileExistsError:
28-
pass
17+
self.add_examiner(BigText)
18+
self.add_examiner(AbsBin)
19+
self.add_examiner(DGC_PaperTapeCheckSum)
20+
self.add_examiner(SameSame)
2921

30-
ctx = autoarchaeologist.Excavation(html_dir=args.dir)
3122

32-
ctx.add_examiner(BigText)
33-
ctx.add_examiner(AbsBin)
34-
ctx.add_examiner(DGC_PaperTapeCheckSum)
35-
ctx.add_examiner(SameSame)
36-
37-
ff = ctx.add_file_artifact("examples/30001393.bin")
38-
39-
ctx.start_examination()
23+
if __name__ == "__main__":
24+
argv = sys.argv[1:]
25+
# force the example as the filename
26+
argv.append("examples/30001393.bin")
27+
args = process_arguments(parse_arguments(argv=argv))
4028

29+
ctx = perform_excavation(args, ("excavator", ExampleExcavation))
4130
ctx.produce_html()
4231

4332
print("Now point your browser at", ctx.filename_for(ctx).link)

tests/__init__.py

Whitespace-only changes.

tests/test_run_example.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import importlib
2+
import os
3+
import shutil
4+
import sys
5+
from types import SimpleNamespace
6+
import unittest
7+
8+
TESTS_DIR = os.path.dirname(os.path.abspath(__file__))
9+
SCRATCH_DIR = os.path.join(TESTS_DIR, "_scratch")
10+
ROOT_DIR = os.path.normpath(os.path.join(TESTS_DIR, ".."))
11+
12+
sys.path.append(TESTS_DIR)
13+
14+
from run import perform_excavation
15+
from run_example import ExampleExcavation
16+
from autoarchaeologist.base.artifact import ArtifactBase, ArtifactStream
17+
18+
19+
def example_arguments(output_dir):
20+
example_arguments = SimpleNamespace()
21+
example_arguments.dir = output_dir
22+
example_arguments.filename = "examples/30001393.bin"
23+
return example_arguments
24+
25+
26+
class Test_RunExampleBasicHtml(unittest.TestCase):
27+
"""
28+
Ensure run_example produces expected HTML files for the example input.
29+
"""
30+
31+
DIR_TREE = None
32+
33+
@classmethod
34+
def setUpClass(cls):
35+
args = example_arguments(SCRATCH_DIR)
36+
shutil.rmtree(args.dir, ignore_errors=True)
37+
os.makedirs(args.dir, exist_ok=True)
38+
ctx = perform_excavation(args, ("excavator", ExampleExcavation))
39+
ctx.produce_html()
40+
cls.DIR_TREE = list(os.walk(args.dir))
41+
42+
def toplevel(self):
43+
return self.__class__.DIR_TREE[0]
44+
45+
def toplevel_dirnames(self):
46+
_, dirs, __ = self.toplevel()
47+
dirs.sort()
48+
return dirs
49+
50+
def toplevel_filenames(self):
51+
_, __, filenames = self.toplevel()
52+
return filenames
53+
54+
def test_produces_top_level_index(self):
55+
toplevel_filenames = self.toplevel_filenames()
56+
self.assertTrue("index.html" in toplevel_filenames)
57+
self.assertTrue("index.css" in toplevel_filenames)
58+
59+
def test_produces_digest_directories(self):
60+
toplevel_dirnames = self.toplevel_dirnames()
61+
self.assertEqual(toplevel_dirnames, ['08', '79', 'fa'])
62+
63+
64+
class Test_RunExampleBasicArtifacts(unittest.TestCase):
65+
"""
66+
Ensure run_example excavates the expected artifacts for the example input.
67+
"""
68+
69+
CTX = None
70+
71+
@classmethod
72+
def setUpClass(cls):
73+
args = example_arguments(SCRATCH_DIR)
74+
shutil.rmtree(args.dir, ignore_errors=True)
75+
os.makedirs(args.dir, exist_ok=True)
76+
ctx = perform_excavation(args, ("excavator", ExampleExcavation))
77+
cls.CTX = ctx
78+
79+
def assertArtifactIsChild(self, artifact, parent):
80+
assert issubclass(artifact.__class__, ArtifactBase)
81+
self.assertEqual(list(artifact.parents), [parent])
82+
83+
def excavation(self):
84+
return self.__class__.CTX
85+
86+
def test_excavated_three_total_artifacts(self):
87+
arfifact_hash_keys = list(self.excavation().hashes.keys())
88+
self.assertEqual(len(arfifact_hash_keys), 3)
89+
90+
def test_excavated_one_top_level_artifact(self):
91+
excavatoin_child_count = len(self.excavation().children)
92+
self.assertEqual(excavatoin_child_count, 1)
93+
94+
def test_produces_top_level_artifact(self):
95+
excavation = self.excavation()
96+
artifact = self.excavation().children[0]
97+
self.assertIsInstance(artifact, ArtifactStream)
98+
self.assertEqual(artifact.digest, '083a3d5e3098aec38ee5d9bc9f9880d3026e120ff8f058782d49ee3ccafd2a6c')
99+
self.assertTrue(artifact.digest in excavation.hashes)
100+
101+
def test_produces_top_level_artifact_whose_parent_is_excavation(self):
102+
artifact = self.excavation().children[0]
103+
self.assertArtifactIsChild(artifact, self.excavation())
104+
105+
def test_produces_two_children_of_the_top_level(self):
106+
excavation = self.excavation()
107+
artifact = excavation.children[0]
108+
artifact_children = sorted(artifact.children, key=lambda a: a.digest)
109+
self.assertEqual(len(artifact_children), 2)
110+
self.assertTrue(artifact_children[0].digest in excavation.hashes)
111+
self.assertTrue(artifact_children[0].digest.startswith('79'))
112+
self.assertArtifactIsChild(artifact_children[0], artifact)
113+
self.assertTrue(artifact_children[1].digest in excavation.hashes)
114+
self.assertTrue(artifact_children[1].digest.startswith('fa'))
115+
self.assertArtifactIsChild(artifact_children[1], artifact)
116+
117+
118+
if __name__ == '__main__':
119+
unittest.main()

0 commit comments

Comments
 (0)