@@ -1,9 +1,16 @@
11#!/usr/bin/env python3
2−"""Build and inspect example /sur and mark fixtures through Clay."""
2+"""Build and inspect example /sur and mark fixtures through Clay.
3+
4+Each fixture is a thread in tests/clay-fixtures, NAME.txt, which
5+tools/ship runs on the ship; what it prints must equal NAME-expected.txt.
6+The threads build my-app's own /sur and marks on the my-app desk. The
7+files only they need, a /sur with a Ford import, a mark and the real
8+Tlon notes /sur (a link to a golden case's fixture), are in
9+tests/clay-fixtures/desk, which the jibb-fixtures desk adds to my-app.
10+Both desks are synced first.
11+"""
312
413from pathlib import Path
5−import shutil
6−import signal
714import subprocess
815import sys
916import tempfile
@@ -11,228 +18,75 @@ import tempfile
1118root = Path(__file__).resolve().parent.parent
1219ship = root / 'tools' / 'ship'
1320fixtures = root / 'tests' / 'clay-fixtures'
14−try:
15− subprocess.run([str(ship), 'sync', 'my-app'], check=True)
16− result = subprocess.run([str(ship), 'run', str(fixtures / 'build.txt')],
17− stdout=subprocess.PIPE, check=True)
18−except subprocess.CalledProcessError as error:
19− sys.exit(error.returncode)
20−expected = (fixtures / 'expected.txt').read_bytes()
21−if result.stdout != expected:
22− print('Clay fixture builds differ from tests/clay-fixtures/expected.txt',
23− file=sys.stderr)
24− print('expected:', expected.decode().rstrip(), file=sys.stderr)
25− print('actual: ', result.stdout.decode().rstrip(), file=sys.stderr)
26− sys.exit(1)
27−print('Clay fixture builds: 7 passed')
28−
29−# Inject the reader rather than copying it into the fixture: Clay supplies
30−# each compiled /sur type graph, and the current reader examines its molds.
31−reader = (root / 'jibb-dev/lib/jibb-import.hoon').read_text()
32−template = (fixtures / 'import.txt').read_text()
33−if template.count('@@READER@@') != 1:
34− sys.exit('Clay import fixture needs exactly one reader placeholder')
35−binding = '=/ reader\n' + ''.join(' ' + line for line in reader.splitlines(keepends=True))
36−with tempfile.TemporaryDirectory(prefix='jibb-clay-import-') as tmp:
37− program = Path(tmp) / 'import.hoon'
38− program.write_text(template.replace('@@READER@@', binding))
39− try:
40− result = subprocess.run([str(ship), 'run', str(program)],
41− stdout=subprocess.PIPE, check=True)
42− except subprocess.CalledProcessError as error:
43− sys.exit(error.returncode)
44−expected = (fixtures / 'import-expected.txt').read_bytes()
45−if result.stdout != expected:
46− print('Clay imports differ from tests/clay-fixtures/import-expected.txt', file=sys.stderr)
47− print('expected:', expected.decode().rstrip(), file=sys.stderr)
48− print('actual: ', result.stdout.decode().rstrip(), file=sys.stderr)
49− sys.exit(1)
50−print('Clay importer golden: 4 projections passed')
51−
52−# This second report uses Clay-built library cores as well as a Clay-built
53−# /sur, then passes the selected reader result into the partial checker.
54−try:
55− result = subprocess.run([str(ship), 'run', str(fixtures / 'bridge.txt')],
56− stdout=subprocess.PIPE, check=True)
57−except subprocess.CalledProcessError as error:
58− sys.exit(error.returncode)
59−expected = (fixtures / 'bridge-expected.txt').read_bytes()
60−if result.stdout != expected:
61− print('Clay bridge differs from tests/clay-fixtures/bridge-expected.txt', file=sys.stderr)
62− print('expected:', expected.decode().rstrip(), file=sys.stderr)
63− print('actual: ', result.stdout.decode().rstrip(), file=sys.stderr)
64− sys.exit(1)
65−print('Clay importer/checker bridge golden passed')
66−
67−try:
68− result = subprocess.run([str(ship), 'run', str(fixtures / 'record-bridge.txt')],
69− stdout=subprocess.PIPE, check=True)
70−except subprocess.CalledProcessError as error:
71− sys.exit(error.returncode)
72−expected = (fixtures / 'record-bridge-expected.txt').read_bytes()
73−if result.stdout != expected:
74− print('Clay record bridge differs from tests/clay-fixtures/record-bridge-expected.txt', file=sys.stderr)
75− print('expected:', expected.decode().rstrip(), file=sys.stderr)
76− print('actual: ', result.stdout.decode().rstrip(), file=sys.stderr)
77− sys.exit(1)
78−print('Clay record importer/checker bridge golden passed')
79−
80−# A /sur with a Ford import has a different parent subject. This golden
81−# records the actual shape for provenance work without claiming support.
82−try:
83− result = subprocess.run([str(ship), 'run', str(fixtures / 'ford.txt')],
84− stdout=subprocess.PIPE, check=True)
85−except subprocess.CalledProcessError as error:
86− sys.exit(error.returncode)
87−expected = (fixtures / 'ford-expected.txt').read_bytes()
88−if result.stdout != expected:
89− print('Clay Ford subject differs from tests/clay-fixtures/ford-expected.txt', file=sys.stderr)
90− print('expected:', expected.decode().rstrip(), file=sys.stderr)
91− print('actual: ', result.stdout.decode().rstrip(), file=sys.stderr)
92− sys.exit(1)
93−print('Clay Ford subject golden passed')
94−
95−try:
96− result = subprocess.run([str(ship), 'run', str(fixtures / 'ford-import.txt')],
97− stdout=subprocess.PIPE, check=True)
98−except subprocess.CalledProcessError as error:
99− sys.exit(error.returncode)
100−expected = (fixtures / 'ford-import-expected.txt').read_bytes()
101−if result.stdout != expected:
102− print('Clay Ford import differs from tests/clay-fixtures/ford-import-expected.txt', file=sys.stderr)
103− print('expected:', expected.decode().rstrip(), file=sys.stderr)
104− print('actual: ', result.stdout.decode().rstrip(), file=sys.stderr)
105− sys.exit(1)
106−print('Clay Ford importer golden passed')
107−
108−try:
109− result = subprocess.run([str(ship), 'run', str(fixtures / 'ford-recursive.txt')],
110− stdout=subprocess.PIPE, check=True)
111−except subprocess.CalledProcessError as error:
112− sys.exit(error.returncode)
113−expected = (fixtures / 'ford-recursive-expected.txt').read_bytes()
114−if result.stdout != expected:
115− print('Clay recursive Ford import differs from tests/clay-fixtures/ford-recursive-expected.txt', file=sys.stderr)
116− print('expected:', expected.decode().rstrip(), file=sys.stderr)
117− print('actual: ', result.stdout.decode().rstrip(), file=sys.stderr)
118− sys.exit(1)
119−print('Clay recursive Ford importer golden passed')
120−
121−try:
122− result = subprocess.run([str(ship), 'run', str(fixtures / 'ford-nested-union.txt')],
123− stdout=subprocess.PIPE, check=True)
124−except subprocess.CalledProcessError as error:
125− sys.exit(error.returncode)
126−expected = (fixtures / 'ford-nested-union-expected.txt').read_bytes()
127−if result.stdout != expected:
128− print('Clay nested union import differs from tests/clay-fixtures/ford-nested-union-expected.txt', file=sys.stderr)
129− print('expected:', expected.decode().rstrip(), file=sys.stderr)
130− print('actual: ', result.stdout.decode().rstrip(), file=sys.stderr)
131− sys.exit(1)
132−print('Clay nested Ford union importer golden passed')
13321
134−try:
135− result = subprocess.run([str(ship), 'run', str(fixtures / 'ford-nested-union-bridge.txt')],
136− stdout=subprocess.PIPE, check=True)
137−except subprocess.CalledProcessError as error:
138− sys.exit(error.returncode)
139−expected = (fixtures / 'ford-nested-union-bridge-expected.txt').read_bytes()
140−if result.stdout != expected:
141− print('Clay nested union bridge differs from tests/clay-fixtures/ford-nested-union-bridge-expected.txt', file=sys.stderr)
142− print('expected:', expected.decode().rstrip(), file=sys.stderr)
143− print('actual: ', result.stdout.decode().rstrip(), file=sys.stderr)
144− sys.exit(1)
145−print('Clay nested Ford union importer/checker bridge golden passed')
22+# (thread, what a mismatch says, what a pass says), in the order they run
23+FIXTURES = [
24+ ('build', 'Clay fixture builds differ', 'Clay fixture builds: 7 passed'),
25+ ('import', 'Clay imports differ',
26+ 'Clay importer golden: 4 projections passed'),
27+ # Clay-built library cores as well as a Clay-built /sur; the selected
28+ # reader result goes to the partial checker.
29+ ('bridge', 'Clay bridge differs',
30+ 'Clay importer/checker bridge golden passed'),
31+ ('record-bridge', 'Clay record bridge differs',
32+ 'Clay record importer/checker bridge golden passed'),
33+ # A /sur with a Ford import has a different parent subject. This
34+ # records the actual shape for provenance work without claiming
35+ # support.
36+ ('ford', 'Clay Ford subject differs', 'Clay Ford subject golden passed'),
37+ ('ford-import', 'Clay Ford import differs',
38+ 'Clay Ford importer golden passed'),
39+ ('ford-recursive', 'Clay recursive Ford import differs',
40+ 'Clay recursive Ford importer golden passed'),
41+ ('ford-nested-union', 'Clay nested union import differs',
42+ 'Clay nested Ford union importer golden passed'),
43+ ('ford-nested-union-bridge', 'Clay nested union bridge differs',
44+ 'Clay nested Ford union importer/checker bridge golden passed'),
45+ ('tlon-mop', 'Clay Tlon mop import differs',
46+ 'Clay Tlon named-mold mop importer golden passed'),
47+ ('mark-shape', 'Clay mark subject differs',
48+ 'Clay mark subject golden passed'),
49+ ('mark-import', 'Clay mark import differs',
50+ 'Clay mark importer golden passed'),
51+ ('mark-update-import', 'Clay union mark import differs',
52+ 'Clay union mark importer golden passed'),
53+ ('mark-bridge', 'Clay mark bridge differs',
54+ 'Clay mark importer/checker bridge golden passed'),
55+]
56+
57+
58+def output(name, tmp):
59+ """What the fixture's thread prints."""
60+ thread = fixtures / (name + '.txt')
61+ if name == 'import':
62+ # Inject the reader rather than copying it into the fixture: Clay
63+ # supplies each compiled /sur type graph, and the current reader
64+ # examines its molds.
65+ reader = (root / 'jibb-dev/lib/jibb-import.hoon').read_text()
66+ template = thread.read_text()
67+ if template.count('@@READER@@') != 1:
68+ sys.exit('Clay import fixture needs exactly one reader placeholder')
69+ binding = '=/ reader\n' + ''.join(
70+ ' ' + line for line in reader.splitlines(keepends=True))
71+ thread = Path(tmp) / 'import.hoon'
72+ thread.write_text(template.replace('@@READER@@', binding))
73+ return subprocess.run([str(ship), 'run', str(thread)],
74+ stdout=subprocess.PIPE, check=True).stdout
14675
147−# Build the real Tlon source in a temporary desk assembly. It lives in the
148−# golden corpus rather than an app desk; the normal desk is restored even if
149−# the temporary sync, thread, or comparison fails.
150−def stop_for_signal(signum, frame):
151− raise InterruptedError('interrupted during temporary Clay fixture')
15276
153−previous = signal.signal(signal.SIGTERM, stop_for_signal)
154−previous_int = signal.getsignal(signal.SIGINT)
15577try:
156− with tempfile.TemporaryDirectory(prefix='jibb-clay-tlon-') as tmp:
157− scratch = Path(tmp)
158− (scratch / 'tools').mkdir()
159− shutil.copy2(ship, scratch / 'tools/ship')
160− shutil.copy2(root / 'tools/golden_case.py', scratch / 'tools/golden_case.py')
161− for folder in ('vendor', 'jibb-dev', 'tests'):
162− (scratch / folder).symlink_to(root / folder, target_is_directory=True)
163− shutil.copytree(root / 'my-app', scratch / 'my-app')
164− shutil.copy2(root / 'tests/golden/types-import-tlon-notes/sur/notes.txt',
165− scratch / 'my-app/sur/mop-tlon-probe.hoon')
166− try:
167− subprocess.run([sys.executable, str(scratch / 'tools/ship'),
168− 'sync', 'my-app'], check=True)
169− result = subprocess.run([str(ship), 'run', str(fixtures / 'tlon-mop.txt')],
170− stdout=subprocess.PIPE, check=True)
171− expected = (fixtures / 'tlon-mop-expected.txt').read_bytes()
172− if result.stdout != expected:
173− print('Clay Tlon mop import differs from tests/clay-fixtures/tlon-mop-expected.txt',
174− file=sys.stderr)
78+ for desk in ('my-app', 'jibb-fixtures'):
79+ subprocess.run([str(ship), 'sync', desk], check=True)
80+ with tempfile.TemporaryDirectory(prefix='jibb-clay-') as tmp:
81+ for name, differs, passed in FIXTURES:
82+ got = output(name, tmp)
83+ want = 'tests/clay-fixtures/%s-expected.txt' % name
84+ expected = (root / want).read_bytes()
85+ if got != expected:
86+ print('%s from %s' % (differs, want), file=sys.stderr)
17587 print('expected:', expected.decode().rstrip(), file=sys.stderr)
176− print('actual: ', result.stdout.decode().rstrip(), file=sys.stderr)
177− raise SystemExit(1)
178− finally:
179− # A second termination request must not interrupt desk restoration.
180− signal.signal(signal.SIGTERM, signal.SIG_IGN)
181− signal.signal(signal.SIGINT, signal.SIG_IGN)
182− subprocess.run([str(ship), 'sync', 'my-app'], check=True)
183−finally:
184− signal.signal(signal.SIGTERM, previous)
185− signal.signal(signal.SIGINT, previous_int)
186−print('Clay Tlon named-mold mop importer golden passed')
187−
188−try:
189− result = subprocess.run([str(ship), 'run', str(fixtures / 'mark-shape.txt')],
190− stdout=subprocess.PIPE, check=True)
191−except subprocess.CalledProcessError as error:
192− sys.exit(error.returncode)
193−expected = (fixtures / 'mark-shape-expected.txt').read_bytes()
194−if result.stdout != expected:
195− print('Clay mark subject differs from tests/clay-fixtures/mark-shape-expected.txt', file=sys.stderr)
196− print('expected:', expected.decode().rstrip(), file=sys.stderr)
197− print('actual: ', result.stdout.decode().rstrip(), file=sys.stderr)
198− sys.exit(1)
199−print('Clay mark subject golden passed')
200−
201−try:
202− result = subprocess.run([str(ship), 'run', str(fixtures / 'mark-import.txt')],
203− stdout=subprocess.PIPE, check=True)
204−except subprocess.CalledProcessError as error:
205− sys.exit(error.returncode)
206−expected = (fixtures / 'mark-import-expected.txt').read_bytes()
207−if result.stdout != expected:
208− print('Clay mark import differs from tests/clay-fixtures/mark-import-expected.txt', file=sys.stderr)
209− print('expected:', expected.decode().rstrip(), file=sys.stderr)
210− print('actual: ', result.stdout.decode().rstrip(), file=sys.stderr)
211− sys.exit(1)
212−print('Clay mark importer golden passed')
213−
214−try:
215− result = subprocess.run([str(ship), 'run', str(fixtures / 'mark-update-import.txt')],
216− stdout=subprocess.PIPE, check=True)
217−except subprocess.CalledProcessError as error:
218− sys.exit(error.returncode)
219−expected = (fixtures / 'mark-update-import-expected.txt').read_bytes()
220−if result.stdout != expected:
221− print('Clay union mark import differs from tests/clay-fixtures/mark-update-import-expected.txt', file=sys.stderr)
222− print('expected:', expected.decode().rstrip(), file=sys.stderr)
223− print('actual: ', result.stdout.decode().rstrip(), file=sys.stderr)
224− sys.exit(1)
225−print('Clay union mark importer golden passed')
226−
227−try:
228− result = subprocess.run([str(ship), 'run', str(fixtures / 'mark-bridge.txt')],
229− stdout=subprocess.PIPE, check=True)
88+ print('actual: ', got.decode().rstrip(), file=sys.stderr)
89+ sys.exit(1)
90+ print(passed)
23091except subprocess.CalledProcessError as error:
23192 sys.exit(error.returncode)
232−expected = (fixtures / 'mark-bridge-expected.txt').read_bytes()
233−if result.stdout != expected:
234− print('Clay mark bridge differs from tests/clay-fixtures/mark-bridge-expected.txt', file=sys.stderr)
235− print('expected:', expected.decode().rstrip(), file=sys.stderr)
236− print('actual: ', result.stdout.decode().rstrip(), file=sys.stderr)
237− sys.exit(1)
238−print('Clay mark importer/checker bridge golden passed')