WillTools 2026-09-09 -- clean snapshot of the development repo for review
ci / test (push) Successful in 6m14s
ci / test (push) Successful in 6m14s
History-free snapshot of the private development repository at 77a82eb. Code, docs and the public catalogue only: no plant or job knowledge layers, no client, job or producer names (tools/scrub.py clean against the widened term list; a second word-level grep clean). The intro deck is left out because its slide images cannot be checked for job drawings.
This commit is contained in:
@@ -0,0 +1,518 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Rendering a takeoff. The tests are about what the page REFUSES to hide.
|
||||
|
||||
A takeoff is trusted on sight, so the things worth testing are that an
|
||||
undercount says the word, that no quantity ever appears without both of its
|
||||
figures, and -- since the page now folds -- that folding never turns a
|
||||
caveat into an appendix. Layout is not under test; disclosure is.
|
||||
|
||||
THE FOLDING CONTRACT, WHICH IS THE POINT OF HALF OF THIS FILE
|
||||
---------------------------------------------------------------
|
||||
The owner's rule (`vibe.foldreport`) is that the answer is the headline and
|
||||
everything else starts closed, so that an OPEN section means "this needs
|
||||
you". A takeoff can break that contract in two directions and both are worse
|
||||
than the flat report was:
|
||||
|
||||
* folding something that bites -- the refusal, an undercount, an
|
||||
unclassified family -- which hides it behind a click nobody makes;
|
||||
* opening something routine, which spends the contract until an open
|
||||
section stops meaning anything.
|
||||
|
||||
So the tests assert both: a clean takeoff has NOTHING open below the
|
||||
headline, and each thing that bites opens itself and says why on its header.
|
||||
"""
|
||||
# pylint: disable=import-error,invalid-name
|
||||
from vibe import foldreport as fold
|
||||
from vibe import takeoffcore as core
|
||||
from vibe import takeoffreport as report
|
||||
|
||||
CFG = core.settings({})
|
||||
|
||||
|
||||
def cache(pieces):
|
||||
return {'schema': 3, 'built_at': 'once', 'pieces': pieces}
|
||||
|
||||
|
||||
def one(families, **extra):
|
||||
record = {'name': 'A', 'families': families, 'features': [], 'error': None,
|
||||
'design_number': None}
|
||||
record.update(extra)
|
||||
return cache({'A': record})
|
||||
|
||||
|
||||
def test_the_quantities_are_the_headline_and_carry_both_figures():
|
||||
"""The order is the answer, so it is what the page opens with."""
|
||||
out = core.build(one({'P70': 1}), CFG)
|
||||
head = report.sections(out)[0]
|
||||
assert head.headline is True
|
||||
text = '\n'.join(b[1] for b in head.blocks if b[0] == fold.MD)
|
||||
assert 'Hardware, by part number' in text
|
||||
columns = [b[1] for b in head.blocks if b[0] == fold.TABLE][0]
|
||||
assert 'per_mark' in columns and 'per_job' in columns
|
||||
|
||||
|
||||
def test_an_unsourced_multiplier_says_the_word_undercount():
|
||||
out = core.build(one({'P70': 1}), CFG)
|
||||
text = report.markdown(out, counts={
|
||||
'marks': 1, 'pieces': 1, 'multiplier': 1.0,
|
||||
'by_basis': {'assumed_one': 1}, 'assumed_one': 1})
|
||||
assert 'UNDERCOUNT' in text
|
||||
|
||||
|
||||
def test_no_multiplier_source_at_all_is_stated_rather_than_implied():
|
||||
out = core.build(one({'P70': 1}), CFG)
|
||||
assert 'undercount' in report.markdown(out, counts=None)
|
||||
|
||||
|
||||
def test_an_unclassified_family_is_named_on_the_page():
|
||||
out = core.build(one({'WIDGET_THING': 5}), CFG)
|
||||
text = report.markdown(out)
|
||||
assert 'WIDGET_THING' in text
|
||||
assert 'refuses to count' in text
|
||||
|
||||
|
||||
def test_the_unclassified_list_is_not_repeated_as_a_declared_exclusion():
|
||||
"""Two sections saying the same thing reads as two findings."""
|
||||
out = core.build(one({'WIDGET_THING': 5}), CFG)
|
||||
text = report.markdown(out)
|
||||
assert text.count('WIDGET_THING') == 1
|
||||
|
||||
|
||||
def test_an_attached_bar_appears_with_its_parent_named():
|
||||
out = core.build(one({'SR9001': 6}), CFG, attached={'SR9001': ['P70']})
|
||||
text = report.markdown(out)
|
||||
assert 'arrive attached' in text
|
||||
assert 'P70' in text.split('arrive attached')[1]
|
||||
|
||||
|
||||
def test_every_csv_row_carries_both_figures():
|
||||
out = core.build(one({'P70': 4}), CFG,
|
||||
multipliers={'A': (3, 'ticket_files')})
|
||||
lines = report.csv_totals(out).splitlines()
|
||||
assert lines[0].startswith('class,key,per_mark_total,per_job_total')
|
||||
assert lines[1].startswith('hardware,P70,4,12')
|
||||
|
||||
|
||||
def test_the_per_piece_csv_marks_a_piece_nobody_could_judge():
|
||||
record = {'name': 'A', 'families': {}, 'features': None,
|
||||
'error': 'members unreadable', 'design_number': None}
|
||||
out = core.build(cache({'A': record}), CFG)
|
||||
assert 'COULD_NOT_JUDGE' in report.csv_by_piece(out)
|
||||
|
||||
|
||||
def test_a_comma_in_a_family_name_does_not_shift_a_csv_column():
|
||||
out = core.build(one({'ODD, NAME': 1}), CFG)
|
||||
assert '"ODD, NAME"' in report.csv_by_piece(out)
|
||||
|
||||
|
||||
def test_a_nesting_suspicion_says_nothing_was_subtracted():
|
||||
pieces = {}
|
||||
for i, n in enumerate([2, 4, 8]):
|
||||
pieces['P%d' % i] = {'name': 'P%d' % i, 'features': [], 'error': None,
|
||||
'design_number': None,
|
||||
'families': {'PARENT': n, 'CHILD': n}}
|
||||
out = core.build(cache(pieces), CFG)
|
||||
text = report.markdown(out)
|
||||
assert 'Nothing has been subtracted' in text
|
||||
|
||||
|
||||
def test_an_unmapped_multiplier_role_is_printed_as_a_refusal():
|
||||
"""Not as a job in which every mark happens to be one piece."""
|
||||
out = core.build(one({'P70': 1}), CFG)
|
||||
text = report.markdown(out, counts={
|
||||
'marks': 1, 'pieces': 1, 'multiplier': 1.0,
|
||||
'by_basis': {'assumed_one': 1}, 'assumed_one': 1,
|
||||
'refusal': 'the role is not mapped'})
|
||||
assert 'could not be sourced' in text
|
||||
assert text.index('could not be sourced') < text.index('Quantities to order')
|
||||
|
||||
|
||||
def test_a_source_that_matches_nothing_is_shouted_about():
|
||||
out = core.build(one({'P70': 1}), CFG)
|
||||
text = report.markdown(out, counts={
|
||||
'marks': 1, 'pieces': 1, 'multiplier': 1.0,
|
||||
'by_basis': {'assumed_one': 1}, 'assumed_one': 1,
|
||||
'join': {'source_marks': 400, 'matched': 0,
|
||||
'unmatched_source': ['OTHER_1'], 'unsourced': ['A']}})
|
||||
assert 'do not match any mark in this takeoff' in text
|
||||
|
||||
|
||||
def test_the_categories_the_multiplier_searched_are_named_on_the_page():
|
||||
out = core.build(one({'P70': 1}), CFG)
|
||||
text = report.markdown(out, counts=None, extras={
|
||||
'multiplier': {'categories': ['OST_StructuralFraming']}})
|
||||
assert 'OST_StructuralFraming' in text
|
||||
|
||||
|
||||
def test_an_unreadable_control_field_is_named_on_the_page():
|
||||
out = core.build(one({'P70': 1}), CFG)
|
||||
text = report.markdown(out, counts=None, extras={
|
||||
'multiplier': {'unreadable': [
|
||||
{'id': 9, 'mark': 'A', 'token': 'PL 1/4', 'why': 'not readable'}]}})
|
||||
assert 'could not turn into pieces' in text
|
||||
assert 'PL 1/4' in text
|
||||
|
||||
|
||||
# ------------------------------------------------------- the declared scope
|
||||
#
|
||||
# A takeoff is an ORDER. Counting somebody else's pieces into it is not an
|
||||
# extra service, it is a wrong order -- and a takeoff that quietly counted
|
||||
# only some of the job would be worse still. So the narrowing is stated on
|
||||
# the page, above the quantities, in all three states.
|
||||
|
||||
from vibe import scope as scopelib
|
||||
from vibe import takeoffgather as gather
|
||||
|
||||
|
||||
def _pop(names):
|
||||
return {'assemblies': len(set(names)), 'assembly_instances': len(names),
|
||||
'names': list(names)}
|
||||
|
||||
|
||||
def test_apply_scope_narrows_index_and_population_together():
|
||||
"""Narrowing one and not the other makes the report claim a hole in the
|
||||
job where there is only somebody else's work."""
|
||||
the_cache = cache({'PNL001': {}, 'XX001': {}, 'YY001': {}})
|
||||
new_cache, new_pop, info = gather._apply_scope(
|
||||
scopelib.Scope(prefixes=('PNL',)), the_cache,
|
||||
_pop(['PNL001', 'XX001', 'YY001']))
|
||||
assert sorted(new_cache['pieces']) == ['PNL001']
|
||||
assert new_pop['names'] == ['PNL001']
|
||||
assert info['left_alone'] == [('XX', 1), ('YY', 1)]
|
||||
assert info['counted_marks'] == 1
|
||||
|
||||
|
||||
def test_apply_scope_never_edits_the_shared_index_in_place():
|
||||
the_cache = cache({'PNL001': {}, 'XX001': {}})
|
||||
gather._apply_scope(scopelib.Scope(prefixes=('PNL',)), the_cache,
|
||||
_pop(['PNL001', 'XX001']))
|
||||
assert sorted(the_cache['pieces']) == ['PNL001', 'XX001']
|
||||
|
||||
|
||||
def test_apply_scope_undeclared_counts_everything_and_still_says_so():
|
||||
the_cache = cache({'PNL001': {}, 'XX001': {}})
|
||||
new_cache, _pop_out, info = gather._apply_scope(
|
||||
scopelib.Scope(), the_cache, _pop(['PNL001', 'XX001']))
|
||||
assert sorted(new_cache['pieces']) == ['PNL001', 'XX001']
|
||||
assert info['declared'] is False
|
||||
assert 'No scope is declared' in info['line']
|
||||
|
||||
|
||||
def test_the_page_prints_what_it_did_not_count():
|
||||
takeoff = core.build(one({'SR100': 1}), CFG)
|
||||
_c, _p, info = gather._apply_scope(
|
||||
scopelib.Scope(prefixes=('PNL',), note='cladding only'),
|
||||
cache({'PNL001': {}, 'XX001': {}, 'XX002': {}}),
|
||||
_pop(['PNL001', 'XX001', 'XX002']))
|
||||
text = report.markdown(takeoff, extras={'scope': info})
|
||||
assert 'What this takeoff did not count' in text
|
||||
assert 'XX 2' in text
|
||||
assert 'cladding only' in text
|
||||
# And it stays separate from what the index could not see.
|
||||
assert 'What this takeoff could not see' in text
|
||||
|
||||
|
||||
def test_the_page_says_so_when_nothing_was_left_out():
|
||||
takeoff = core.build(one({'SR100': 1}), CFG)
|
||||
_c, _p, info = gather._apply_scope(
|
||||
scopelib.Scope(prefixes=('PNL',)), cache({'PNL001': {}}),
|
||||
_pop(['PNL001']))
|
||||
text = report.markdown(takeoff, extras={'scope': info})
|
||||
assert 'nothing was set aside' in text
|
||||
|
||||
|
||||
# ------------------------------------------------------- the folding contract
|
||||
#
|
||||
# "If nothing below the headline is open, nothing needs me." Everything from
|
||||
# here down is that sentence, tested from both sides.
|
||||
|
||||
def _clean():
|
||||
"""A takeoff with nothing wrong with it: every family classified, every
|
||||
mark in the index, every multiplier sourced."""
|
||||
the_cache = cache({'A': {'name': 'A', 'families': {'P70': 2},
|
||||
'features': [], 'error': None,
|
||||
'design_number': '101'}})
|
||||
return core.build(the_cache, CFG,
|
||||
multipliers={'A': (3, 'control_numbers')},
|
||||
population={'assemblies': 1, 'assembly_instances': 1,
|
||||
'names': ['A']})
|
||||
|
||||
|
||||
CLEAN_COUNTS = {'marks': 1, 'pieces': 3, 'multiplier': 3.0,
|
||||
'by_basis': {'control_numbers': 1}, 'assumed_one': 0,
|
||||
'sources': {'control_numbers': 1, 'ticket_files': 0,
|
||||
'assembly_instances': 0},
|
||||
'join': {'source_marks': 1, 'matched': 1,
|
||||
'unmatched_source': [], 'unsourced': []}}
|
||||
|
||||
|
||||
def _tail(takeoff, counts=None, extras=None):
|
||||
return report.sections(takeoff, counts, extras=extras)[1:]
|
||||
|
||||
|
||||
def _open(takeoff, counts=None, extras=None):
|
||||
return [s.key for s in fold.sections_with_problems(
|
||||
_tail(takeoff, counts, extras))]
|
||||
|
||||
|
||||
def test_a_takeoff_with_nothing_wrong_opens_nothing_below_the_headline():
|
||||
"""The contract itself. If a routine press opens a section, an open
|
||||
section stops meaning 'this needs you' and the whole page is furniture."""
|
||||
assert _open(_clean(), CLEAN_COUNTS) == []
|
||||
|
||||
|
||||
def test_every_folded_section_says_what_it_holds():
|
||||
"""A header that says only 'details' tells the reader nothing about
|
||||
whether to open it, and folded is only acceptable while it is not gone."""
|
||||
for section in _tail(_clean(), CLEAN_COUNTS):
|
||||
assert section.summary, section.key
|
||||
|
||||
|
||||
def test_the_headline_says_what_multiplied_the_per_job_column():
|
||||
"""The totals can be lifted off this page alone, so the thing that scaled
|
||||
them has to be on it -- not only in the section that owns it."""
|
||||
head = report.sections(_clean(), CLEAN_COUNTS)[0]
|
||||
text = '\n'.join(b[1] for b in head.blocks if b[0] == fold.MD)
|
||||
assert 'control_numbers 1' in text
|
||||
assert 'per_job' in text and 'per_mark' in text
|
||||
|
||||
|
||||
def test_a_refused_multiplier_is_open_and_printed_whole():
|
||||
"""The refusal is the one sentence on the page saying the numbers were
|
||||
never sourced. Folding it into an appendix would be the exact failure the
|
||||
flat report existed to prevent."""
|
||||
out = core.build(one({'P70': 1}), CFG)
|
||||
counts = dict(CLEAN_COUNTS)
|
||||
counts['refusal'] = ('the role `member.control_number` is not mapped, so '
|
||||
'the model was not asked how many pieces each mark '
|
||||
'is')
|
||||
sections = report.sections(out, counts)
|
||||
multiplier = [s for s in sections if s.key == 'multiplier'][0]
|
||||
assert multiplier.is_open is True
|
||||
assert 'not asked how many pieces' in '\n'.join(
|
||||
b[1] for b in multiplier.blocks if b[0] == fold.MD)
|
||||
# and the headline itself does not let the totals be read without it
|
||||
head = '\n'.join(b[1] for b in sections[0].blocks if b[0] == fold.MD)
|
||||
assert 'UNDERCOUNT' in head
|
||||
|
||||
|
||||
def test_a_refused_multiplier_survives_a_dead_toggle():
|
||||
"""pyRevit's panel is an IE-engine WebBrowser and the fold is an inline
|
||||
onclick. If the script does not run, an open section must still be on the
|
||||
page as plain HTML."""
|
||||
out = core.build(one({'P70': 1}), CFG)
|
||||
counts = dict(CLEAN_COUNTS)
|
||||
counts['refusal'] = 'nobody mapped the control-number role'
|
||||
html = fold.html(_tail(out, counts))
|
||||
assert 'nobody mapped the control-number role' in html
|
||||
assert 'display:block' in html
|
||||
|
||||
|
||||
def test_the_panel_html_is_one_line_and_uses_nothing_this_engine_lacks():
|
||||
html = fold.html(_tail(_clean(), CLEAN_COUNTS))
|
||||
assert chr(10) not in html
|
||||
assert '<details' not in html and 'classList' not in html
|
||||
|
||||
|
||||
def test_an_unclassified_family_opens_its_own_section():
|
||||
out = core.build(one({'WIDGET_THING': 5}), CFG)
|
||||
assert 'unclassified' in _open(out, CLEAN_COUNTS)
|
||||
|
||||
|
||||
def test_marks_counted_once_open_and_are_named():
|
||||
out = core.build(one({'P70': 1}), CFG)
|
||||
counts = dict(CLEAN_COUNTS)
|
||||
counts['assumed_one'] = 1
|
||||
section = [s for s in _tail(out, counts)
|
||||
if s.key == 'multiplier-assumed'][0]
|
||||
assert section.is_open is True
|
||||
text = '\n'.join(b[1] for b in section.blocks if b[0] == fold.MD)
|
||||
assert 'UNDERCOUNT' in text
|
||||
assert 'A' in text # the mark itself, not a tally
|
||||
|
||||
|
||||
def test_a_source_whose_marks_match_nothing_opens():
|
||||
out = core.build(one({'P70': 1}), CFG)
|
||||
counts = dict(CLEAN_COUNTS)
|
||||
counts['join'] = {'source_marks': 400, 'matched': 0,
|
||||
'unmatched_source': ['OTHER_1'], 'unsourced': ['A']}
|
||||
assert 'multiplier-join' in _open(out, counts)
|
||||
|
||||
|
||||
def test_an_unreadable_control_field_opens_rather_than_folding():
|
||||
out = core.build(one({'P70': 1}), CFG)
|
||||
assert 'multiplier-unreadable' in _open(
|
||||
out, CLEAN_COUNTS,
|
||||
{'multiplier': {'unreadable': [{'id': 9, 'mark': 'A',
|
||||
'token': 'PL 1/4',
|
||||
'why': 'not readable'}]}})
|
||||
|
||||
|
||||
def test_where_the_multiplier_swept_is_routine_and_stays_folded():
|
||||
"""Naming the categories is provenance, not a problem. It opens nothing."""
|
||||
assert _open(_clean(), CLEAN_COUNTS,
|
||||
{'multiplier': {'categories': ['OST_StructuralFraming'],
|
||||
'elements': 12}}) == []
|
||||
|
||||
|
||||
def test_a_nesting_suspicion_opens_itself():
|
||||
pieces = {}
|
||||
for i, n in enumerate([2, 4, 8]):
|
||||
pieces['P%d' % i] = {'name': 'P%d' % i, 'features': [], 'error': None,
|
||||
'design_number': None,
|
||||
'families': {'PARENT': n, 'CHILD': n}}
|
||||
out = core.build(cache(pieces), CFG)
|
||||
assert 'nesting' in _open(out, CLEAN_COUNTS)
|
||||
|
||||
|
||||
def test_a_mark_in_the_model_the_index_never_saw_opens_coverage():
|
||||
"""Those pieces are missing from the order. A hole in the job is not a
|
||||
routine coverage line, whatever else coverage usually is."""
|
||||
out = core.build(one({'P70': 1}), CFG,
|
||||
population={'assemblies': 9, 'assembly_instances': 9,
|
||||
'names': ['A']})
|
||||
assert 'coverage' in _open(out, CLEAN_COUNTS)
|
||||
|
||||
|
||||
def test_a_piece_the_index_could_not_judge_opens_coverage():
|
||||
record = {'name': 'A', 'families': {}, 'features': None,
|
||||
'error': 'members unreadable', 'design_number': None}
|
||||
out = core.build(cache({'A': record}), CFG,
|
||||
population={'assemblies': 1, 'assembly_instances': 1,
|
||||
'names': ['A']})
|
||||
assert 'coverage' in _open(out, CLEAN_COUNTS)
|
||||
|
||||
|
||||
# ------------------------------------------- what one exported ticket covers
|
||||
#
|
||||
# `nomenclature.ticket_unit` decides whether the exported tickets counted at
|
||||
# all. Declared, it is one routine sentence and opens nothing. Undeclared, it
|
||||
# is a refusal -- and it opens the section at the point where the missing
|
||||
# declaration stopped being a formality and started costing marks.
|
||||
|
||||
def _with_unit(unit, **plan):
|
||||
counts = dict(CLEAN_COUNTS)
|
||||
base = {'key': 'nomenclature.ticket_unit', 'unit': unit,
|
||||
'declared': unit is not None, 'recognised': True,
|
||||
'order': ('control_numbers', 'ticket_files',
|
||||
'assembly_instances'),
|
||||
'withdrawn': (), 'withdrawn_marks': 0, 'cost': 0,
|
||||
'reason': 'one ticket per control number, so a ticket is a piece',
|
||||
'refusal': None}
|
||||
base.update(plan)
|
||||
counts['ticket_unit'] = base
|
||||
return counts
|
||||
|
||||
|
||||
def test_a_declared_ticket_unit_is_stated_and_opens_nothing():
|
||||
"""Somebody answered the question. That is not a problem, and spending an
|
||||
open section on it would spend the folding contract."""
|
||||
counts = _with_unit('control_number')
|
||||
assert _open(_clean(), counts) == []
|
||||
text = report.markdown(_clean(), counts)
|
||||
assert 'one ticket per control number' in text
|
||||
|
||||
|
||||
def test_the_ticket_unit_is_on_the_folded_header_either_way():
|
||||
"""A reader who never opens the multiplier section still learns whether
|
||||
the run knew what a ticket was."""
|
||||
section = [s for s in _tail(_clean(), _with_unit('control_number'))
|
||||
if s.key == 'multiplier'][0]
|
||||
assert 'ticket unit: control_number' in section.summary
|
||||
undeclared = [s for s in _tail(_clean(), _with_unit(
|
||||
None, declared=False, refusal='nobody declared it',
|
||||
reason='nobody declared it', withdrawn=('ticket_files',)))
|
||||
if s.key == 'multiplier'][0]
|
||||
assert 'NOT DECLARED' in undeclared.summary
|
||||
|
||||
|
||||
def test_an_undeclared_ticket_unit_that_cost_marks_opens_the_section():
|
||||
counts = _with_unit(None, declared=False, cost=7, withdrawn_marks=9,
|
||||
withdrawn=('ticket_files',),
|
||||
order=('control_numbers', 'assembly_instances'),
|
||||
refusal='`nomenclature.ticket_unit` is not declared',
|
||||
reason='`nomenclature.ticket_unit` is not declared')
|
||||
assert 'multiplier' in _open(_clean(), counts)
|
||||
section = [s for s in _tail(_clean(), counts)
|
||||
if s.key == 'multiplier'][0]
|
||||
assert '7 mark(s) lost their only multiplier source' in section.problem
|
||||
text = '\n'.join(b[1] for b in section.blocks if b[0] == fold.MD)
|
||||
assert 'nomenclature.ticket_unit' in text
|
||||
|
||||
|
||||
def test_an_undeclared_ticket_unit_that_cost_nothing_is_said_but_not_opened():
|
||||
"""No exported tickets to withdraw means the missing declaration moved no
|
||||
number. It is still printed -- folded is not gone."""
|
||||
counts = _with_unit(None, declared=False, cost=0,
|
||||
withdrawn=('ticket_files',),
|
||||
order=('control_numbers', 'assembly_instances'),
|
||||
refusal='not declared, so the tickets were not used',
|
||||
reason='not declared, so the tickets were not used')
|
||||
assert _open(_clean(), counts) == []
|
||||
assert 'not declared, so the tickets were not used' in report.markdown(
|
||||
_clean(), counts)
|
||||
|
||||
|
||||
def test_a_withdrawn_source_is_shown_as_withdrawn_not_as_one_that_found_none():
|
||||
"""It knew 57 marks and contributed none of them. A table showing only
|
||||
what it knew would read as if it had."""
|
||||
counts = _with_unit('piece_mark', withdrawn=('ticket_files',),
|
||||
order=('control_numbers', 'assembly_instances'),
|
||||
reason='one ticket covers a whole mark')
|
||||
counts['sources'] = {'control_numbers': 1, 'ticket_files': 57,
|
||||
'assembly_instances': 0}
|
||||
section = [s for s in _tail(_clean(), counts)
|
||||
if s.key == 'multiplier'][0]
|
||||
rows = [b[2] for b in section.blocks if b[0] == fold.TABLE][-1]
|
||||
assert ['ticket_files', '57', 'NO'] in rows
|
||||
assert ['control_numbers', '1', 'yes'] in rows
|
||||
# and a declaration nobody has to act on does not open the section
|
||||
assert _open(_clean(), counts) == []
|
||||
|
||||
|
||||
def test_the_page_carries_no_ticket_unit_claim_when_it_was_given_none():
|
||||
"""The report never invents provenance it was not handed -- the same rule
|
||||
`extras` already follows."""
|
||||
text = report.markdown(_clean(), CLEAN_COUNTS)
|
||||
assert 'ticket unit' not in text
|
||||
|
||||
|
||||
def test_the_declared_scope_is_folded_because_somebody_declared_it():
|
||||
"""Work left alone on purpose is not a problem. It is still printed."""
|
||||
takeoff = _clean()
|
||||
_c, _p, info = gather._apply_scope(
|
||||
scopelib.Scope(prefixes=('A',), note='cladding only'),
|
||||
cache({'A': {}, 'XX001': {}}), _pop(['A', 'XX001']))
|
||||
keys = dict((s.key, s) for s in _tail(takeoff, CLEAN_COUNTS,
|
||||
{'scope': info}))
|
||||
assert keys['scope'].is_open is False
|
||||
assert 'left alone' in keys['scope'].summary
|
||||
|
||||
|
||||
def test_the_long_tables_stay_folded_however_long_they_get():
|
||||
"""The by-piece breakdown is the biggest thing on the page and the least
|
||||
read. Length is not a problem; it is just length."""
|
||||
pieces = dict(('P%03d' % i,
|
||||
{'name': 'P%03d' % i, 'families': {'P70': i + 1},
|
||||
'features': [], 'error': None, 'design_number': None})
|
||||
for i in range(150))
|
||||
out = core.build(cache(pieces), CFG,
|
||||
population={'assemblies': 150, 'assembly_instances': 150,
|
||||
'names': sorted(pieces)})
|
||||
counts = dict(CLEAN_COUNTS)
|
||||
counts['by_basis'] = {'control_numbers': 150}
|
||||
keys = dict((s.key, s) for s in _tail(out, counts))
|
||||
assert keys['by-piece'].is_open is False
|
||||
assert keys['judgements'].is_open is False
|
||||
assert keys['attached'].is_open is False
|
||||
assert '150 piece(s)' in keys['by-piece'].summary
|
||||
|
||||
|
||||
def test_the_written_report_still_carries_every_folded_sentence():
|
||||
"""Folded is not gone: the `.md` beside the model is the unfolded copy,
|
||||
so a fold that will not open costs a file open and never a sentence."""
|
||||
text = report.markdown(_clean(), CLEAN_COUNTS)
|
||||
assert 'rule:*' in text # the judgements section
|
||||
assert 'One row per piece' in text # the by-piece section
|
||||
assert 'None of the counted bar marks' in text # the attached section
|
||||
assert 'marks in the model but NOT indexed' in text
|
||||
Reference in New Issue
Block a user