@@ -1,9 +1,16 @@
1#!/usr/bin/env python3
1#!/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.
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.
4from pathlib import Path
13from pathlib import Path
7import subprocess
14import subprocess
9import tempfile
16import tempfile
@@ -11,228 +18,75 @@ import tempfile
11root = Path(__file__).resolve().parent.parent
18root = Path(__file__).resolve().parent.parent
12ship = root / 'tools' / 'ship'
19ship = root / 'tools' / 'ship'
13fixtures = root / 'tests' / 'clay-fixtures'
20fixtures = root / 'tests' / 'clay-fixtures'
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',
24− print('expected:', expected.decode().rstrip(), file=sys.stderr)
25− print('actual: ', result.stdout.decode().rstrip(), file=sys.stderr)
27−print('Clay fixture builds: 7 passed')
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))
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)
50−print('Clay importer golden: 4 projections passed')
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.
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)
65−print('Clay importer/checker bridge golden passed')
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)
78−print('Clay record importer/checker bridge golden passed')
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.
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)
93−print('Clay Ford subject golden passed')
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)
106−print('Clay Ford importer golden passed')
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)
119−print('Clay recursive Ford importer golden passed')
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)
132−print('Clay nested Ford union importer golden passed')
134−try:
22+# (thread, what a mismatch says, what a pass says), in the order they run
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
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'),
58+def output(name, tmp):
59+ """What the fixture's thread prints."""
60+ thread = fixtures / (name + '.txt')
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))
135− result = subprocess.run([str(ship), 'run', str(fixtures / 'ford-nested-union-bridge.txt')],
73+ return subprocess.run([str(ship), 'run', str(thread)],
136− stdout=subprocess.PIPE, check=True)
74+ stdout=subprocess.PIPE, check=True).stdout
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)
145−print('Clay nested Ford union importer/checker bridge golden passed')
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')
153−previous = signal.signal(signal.SIGTERM, stop_for_signal)
154−previous_int = signal.getsignal(signal.SIGINT)
78+ for desk in ('my-app', 'jibb-fixtures'):
79+ subprocess.run([str(ship), 'sync', desk], check=True)
156− with tempfile.TemporaryDirectory(prefix='jibb-clay-tlon-') as tmp:
80+ with tempfile.TemporaryDirectory(prefix='jibb-clay-') as tmp:
157− scratch = Path(tmp)
81+ for name, differs, passed in FIXTURES:
158− (scratch / 'tools').mkdir()
82+ got = output(name, tmp)
159− shutil.copy2(ship, scratch / 'tools/ship')
83+ want = 'tests/clay-fixtures/%s-expected.txt' % name
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')
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()
84+ expected = (root / want).read_bytes()
172− if result.stdout != expected:
85+ if got != expected:
173− print('Clay Tlon mop import differs from tests/clay-fixtures/tlon-mop-expected.txt',
174− file=sys.stderr)
86+ print('%s from %s' % (differs, want), file=sys.stderr)
175 print('expected:', expected.decode().rstrip(), file=sys.stderr)
87 print('expected:', expected.decode().rstrip(), file=sys.stderr)
176− print('actual: ', result.stdout.decode().rstrip(), file=sys.stderr)
88+ print('actual: ', got.decode().rstrip(), file=sys.stderr)
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)
184− signal.signal(signal.SIGTERM, previous)
185− signal.signal(signal.SIGINT, previous_int)
186−print('Clay Tlon named-mold mop importer golden passed')
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)
89+ sys.exit(1)
199−print('Clay mark subject golden passed')
90+ print(passed)
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)
212−print('Clay mark importer golden passed')
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)
225−print('Clay union mark importer golden passed')
228− result = subprocess.run([str(ship), 'run', str(fixtures / 'mark-bridge.txt')],
229− stdout=subprocess.PIPE, check=True)
230except subprocess.CalledProcessError as error:
91except subprocess.CalledProcessError as error:
231 sys.exit(error.returncode)
92 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)
238−print('Clay mark importer/checker bridge golden passed')