Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions specs/001-pathway-pdfs/contracts/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,21 @@ pathway, and a file on one side only is reported as a difference.

- **Output:** a JSON report of the differences by part and kind, with counts. Names
appear only as counts and hashes (FR-013).
- **`--expected <file>`:** the recorded differences. With it, the exit is 1 only for a
difference not recorded.
- Each difference has its `file`, its `part` (`file`, `cover`, `introduction`,
`analysis`, `sections`, `section`, `figure`, `contents` or `short-entries`), a `field`
where the part has several (a section's `summation`, the cover's `names`), its `kind`
(`value`, `count`, `text`, `order`, `missing` from the new side, `extra` on it, `kind`
or `target`), a `count`, and the sections it is in by stable identifier.
- Each side's value is given only where it names no one: the cover's title and flags,
the introduction's numbers, the analysis's properties, and the cover's names as a
count and a hash. A section's text is never printed.
- A list in another order is a difference of its own kind, `order`.
- **`--expected <file>`:** the recorded differences, as `{ "differences": [...] }`, each
with a `part`, a `kind` and a `reason`, and optionally a `field`, a `file` and `events`
(a record naming events covers only those). Each difference it covers is reported with
its reason. With it, the exit is 1 only for a difference not recorded.
- **Exit:** 0 with no difference (or none not recorded), 1 with one, 2 on bad arguments, a
reference and a new side not both PDFs or both folders, or a file that cannot be read.

## `pdf-export serve`

Expand Down
19 changes: 18 additions & 1 deletion specs/001-pathway-pdfs/tasks.md
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ passes.

**Goal:** FR-009 in the image (US4, SC-006), and FR-010, FR-011, FR-012, SC-002, SC-003, SC-007 and SC-010 (US5).

- [ ] T035 [US5] FR-012's comparison, `src/compare/`: `pdf-export compare <today's folder> <new.pdf>` (contracts/cli.md).
- [x] T035 [US5] FR-012's comparison, `src/compare/`: `pdf-export compare <today's folder> <new.pdf>` (contracts/cli.md).
- Both sides are reduced to FR-012's parts:
- the covers' distinct names, counted, across today's files;
- the introduction's release and counts;
Expand All @@ -599,6 +599,23 @@ passes.

Tests on fixture PDFs: each part's difference is caught, and no name is printed.

- **Done (#165):** `pdf-export compare <reference> <new> [--expected <file>]`, wired into `src/cli.ts`, as contracts/cli.md now has it rather than this task's `<today's folder> <new.pdf>`: the book is a folder of PDFs, so each side is a PDF or a folder, a folder compared file by file by name, and a file on one side only is a difference.
- **Reading** (`src/compare/read.ts`): pdf.js's lines of text (pieces sharing a baseline), links and figures. A page's top and bottom 50 pt (running headers, page numbers) and the text inside a figure (a vector diagram's labels) are left out. A figure is a cluster of images and painted paths, neither thin nor the whole page, at least 60 pt each way, so a raster diagram and a vector one are both found.
- **Reducing** (`src/compare/reduce.ts`), with no knowledge of which program wrote the file:
- the cover: its title (its largest text), a diagram below it, an excerpt note, and its distinct names, counted and hashed (the names are a run of characters joined by U+2060 or no-break spaces where U+2060 is there to read, research R2; otherwise parted by semicolons, as pdf-export writes them);
- the introduction's release and counts, the analysis's properties by label (not its explanatory text);
- each section, found at its `Stable identifier` label, its name the larger text above it: its parts by their labels, each taken only after the parts before it in FR-005's order, so a summation beginning with a label's word stays a summation; the summation is the unlabelled text after the facts (after the figure, or at the section's left edge);
- each part's items: lists split at their separators, literature at its numbers, the edit history at its dates; the literature and edit history, which name people, held as one hash an item;
- the figure's kind, `diagram`, `selection` or `reaction` from its link to the website, `reaction` for a section with a type, or else `figure` (present, kind not read), which matches any kind;
- the contents list's entries (each up to the line ending in its page number) and the section or front part each lands on;
- short entries at later places (FR-004), counted apart.
Text is compared folded: NFKC, with white space and FR-017's characters never drawn (U+2060 among them) left out.
- **Comparing** (`src/compare/compare.ts`): a JSON report, each difference by part, field and kind (`value`, `count`, `text`, `order`, `missing`, `extra`, `kind`, `target`), with a count and its sections' stable identifiers; a list with the same items in another order is `order`, never `text`. Values are printed only where they name no one; a section's text never is, and the cover's names only as a count and a hash. `--expected` takes `{ "differences": [...] }`, each with a part, kind and reason and optionally a field, file and events (covering only those events); the exit is 1 only for a difference not recorded, 2 on bad arguments or an unreadable file. contracts/cli.md says so.
- **Tests:** `test/compare/compare.test.ts` (19, on Chromium; no graph). A sample document (`test/compare/samples.ts`, placeholder names only) is printed by pdf-export's layout and, as the reference, as HTML in `event-pdf`'s shape (contents last, `Label: value` parts, raster figures and logo, running headers, names with U+2060 and no-break spaces), with wording written anew. Real `event-pdf` files are never used. The same document in both differs only in the analysis's resource. Each part's difference is caught: the cover's names (count, and another name of the same count), title, diagram and excerpt; the release and counts; three analysis properties; a summation's text, a part missing, a list's order, a section missing, the sections' order, the literature's and edit history's hashes; the contents' entries, order and targets; a figure missing and of another kind; a short entry. `--expected` passes recorded differences and fails on the one left out; a record for some events or another field covers only those. No placeholder's name or part of one appears in any report, error or reduced form, and no assertion prints a text read from a PDF.
- **Mutants, each caught:** names not read as U+2060 runs; white space kept in folding; names not made distinct; the names' hash not compared; names printed in the clear in place of the hash (the no-name test); literature not hashed; the release not read; analysis values not compared; an order read as text; a section missing not reported; contents targets not compared; links landing nowhere; figures not compared; a figure's text kept; the margins kept; labels taken in any order; short entries not set apart; a record's field ignored; recorded differences failing the run; the summation not split from the facts; a diagram anywhere on the cover (above the title, a logo).
- **Found:** pdf-export's analysis part does not print the resource (`TOTAL`), which FR-012 lists among the properties: `analysis resource missing` until it does, for T040 to record or fix.
- **Known gaps, for T040:** `event-pdf`'s own labels, its cover's names' separator (whether U+2060 survives pdf.js's reading of its files), its margins and its contents' form are not known here, so the reference's shape is the Background's and R2's, and its wording this task's. A label not known leaves its part unread, which shows as a difference, never a pass; T040 adds `event-pdf`'s labels to `RULES` and `PROPERTIES` in `src/compare/reduce.ts`. A fact's value wrapped to the section's left edge would be read as the summation's start. A summation and an analysis's found tables are compared as text and rows, not by their markup.

- [ ] T036 [US5] The fixture's output as a baseline (the constitution's quality gate for a change to what is written).
- `ci/fixture/expected.json` holds the fixture book and a fixture single-event PDF, reduced by T035's `compare`; `compare` gains `--reduced <file>` to take a reduced form as the reference, added to contracts/cli.md in this task (the fixture PDFs are not committed).
- CI compares each PR's output with it, and fails on any difference that the PR does not also update.
Expand Down
9 changes: 8 additions & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
// pdf-export's command line (contracts/cli.md). Its commands come with spec 001's tasks:
// serve (T022) runs the single-event service; preflight (T024) runs the book's preflight
// alone; figures (T046) draws the book's figures into a folder; book (T029) writes the
// book; check-book (T030) checks a written book for FR-011's faults; compare is to come.
// book; check-book (T030) checks a written book for FR-011's faults; compare (T035)
// compares with event-pdf's output by FR-012.
import { constants as osConstants } from 'node:os';

import { privateTemporary } from './print/temporary.ts';
Expand Down Expand Up @@ -93,6 +94,12 @@ if (import.meta.main) {
out: (text) => process.stdout.write(text),
err: (text) => process.stderr.write(text),
});
} else if (args[0] === 'compare') {
const { compareCommand } = await import('./compare/compare.ts');
process.exitCode = await compareCommand(args.slice(1), {
out: (text) => process.stdout.write(text),
err: (text) => process.stderr.write(text),
});
} else if (args[0] === 'figures') {
const { figuresCommand } = await import('./figures/step.ts');
process.exitCode = await figuresCommand(args.slice(1), process.env, {
Expand Down
Loading
Loading