png_conform 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +101 -80
  3. data/CONTRIBUTING.md +22 -0
  4. data/Gemfile +4 -0
  5. data/README.adoc +7 -0
  6. data/Rakefile +8 -1
  7. data/TODO.structured-diagnostics/00-overview.md +98 -0
  8. data/TODO.structured-diagnostics/01-shared-contract.md +36 -0
  9. data/TODO.structured-diagnostics/02-requirement-catalog.md +37 -0
  10. data/TODO.structured-diagnostics/03a-png-apng-emitters.md +45 -0
  11. data/TODO.structured-diagnostics/03b-mng-jng-emitters.md +34 -0
  12. data/TODO.structured-diagnostics/03c-format-aware-orchestration.md +38 -0
  13. data/TODO.structured-diagnostics/04-report-migration.md +40 -0
  14. data/TODO.structured-diagnostics/05-corpus-goldens.md +42 -0
  15. data/TODO.structured-diagnostics/90-progress.md +22 -0
  16. data/benchmarks/lib/benchmark_runner.rb +1 -1
  17. data/benchmarks/lib/metrics_collector.rb +3 -3
  18. data/benchmarks/lib/report_generator.rb +8 -9
  19. data/config/diagnostics.yml +2906 -0
  20. data/docs/ARCHITECTURE.adoc +13 -4
  21. data/docs/CLI_OPTIONS.adoc +126 -0
  22. data/lib/png_conform/analyzers/comparison_analyzer.rb +5 -5
  23. data/lib/png_conform/analyzers/optimization_analyzer.rb +6 -6
  24. data/lib/png_conform/bindata/chunk_structure.rb +8 -1
  25. data/lib/png_conform/cli.rb +0 -7
  26. data/lib/png_conform/commands/check_command.rb +3 -2
  27. data/lib/png_conform/diagnostics/builder.rb +52 -0
  28. data/lib/png_conform/diagnostics/catalog.rb +242 -0
  29. data/lib/png_conform/diagnostics/golden_generator.rb +92 -0
  30. data/lib/png_conform/diagnostics/location.rb +45 -0
  31. data/lib/png_conform/models/chunk_info.rb +1 -1
  32. data/lib/png_conform/models/file_analysis.rb +2 -0
  33. data/lib/png_conform/models/validation_error.rb +96 -1
  34. data/lib/png_conform/models/validation_result.rb +20 -2
  35. data/lib/png_conform/readers/chunk_framing.rb +79 -0
  36. data/lib/png_conform/readers/format_dispatch.rb +49 -0
  37. data/lib/png_conform/readers/full_load_reader.rb +51 -1
  38. data/lib/png_conform/readers/streaming_reader.rb +25 -5
  39. data/lib/png_conform/reporters/summary_reporter.rb +1 -1
  40. data/lib/png_conform/reporters/verbose_reporter.rb +3 -1
  41. data/lib/png_conform/services/chunk_processor.rb +11 -11
  42. data/lib/png_conform/services/file_signature.rb +2 -2
  43. data/lib/png_conform/services/profile_manager.rb +77 -28
  44. data/lib/png_conform/services/result_builder.rb +9 -14
  45. data/lib/png_conform/services/validation_orchestrator.rb +175 -49
  46. data/lib/png_conform/services/zlib_validator.rb +2 -2
  47. data/lib/png_conform/validators/ancillary/bkgd_validator.rb +12 -12
  48. data/lib/png_conform/validators/ancillary/chrm_validator.rb +10 -10
  49. data/lib/png_conform/validators/ancillary/cicp_validator.rb +15 -9
  50. data/lib/png_conform/validators/ancillary/gama_validator.rb +10 -10
  51. data/lib/png_conform/validators/ancillary/hist_validator.rb +17 -17
  52. data/lib/png_conform/validators/ancillary/iccp_validator.rb +27 -27
  53. data/lib/png_conform/validators/ancillary/idot_validator.rb +5 -5
  54. data/lib/png_conform/validators/ancillary/itxt_validator.rb +24 -24
  55. data/lib/png_conform/validators/ancillary/mdcv_validator.rb +15 -7
  56. data/lib/png_conform/validators/ancillary/offs_validator.rb +10 -10
  57. data/lib/png_conform/validators/ancillary/pcal_validator.rb +28 -28
  58. data/lib/png_conform/validators/ancillary/phys_validator.rb +8 -8
  59. data/lib/png_conform/validators/ancillary/sbit_validator.rb +13 -13
  60. data/lib/png_conform/validators/ancillary/scal_validator.rb +13 -13
  61. data/lib/png_conform/validators/ancillary/splt_validator.rb +21 -21
  62. data/lib/png_conform/validators/ancillary/srgb_validator.rb +9 -9
  63. data/lib/png_conform/validators/ancillary/ster_validator.rb +9 -9
  64. data/lib/png_conform/validators/ancillary/text_validator.rb +12 -12
  65. data/lib/png_conform/validators/ancillary/time_validator.rb +11 -11
  66. data/lib/png_conform/validators/ancillary/trns_validator.rb +15 -15
  67. data/lib/png_conform/validators/ancillary/ztxt_validator.rb +16 -16
  68. data/lib/png_conform/validators/apng/actl_validator.rb +5 -5
  69. data/lib/png_conform/validators/apng/fctl_validator.rb +13 -11
  70. data/lib/png_conform/validators/apng/fdat_validator.rb +9 -9
  71. data/lib/png_conform/validators/base_validator.rb +72 -39
  72. data/lib/png_conform/validators/critical/idat_validator.rb +7 -4
  73. data/lib/png_conform/validators/critical/iend_validator.rb +8 -5
  74. data/lib/png_conform/validators/critical/ihdr_validator.rb +18 -18
  75. data/lib/png_conform/validators/critical/plte_validator.rb +10 -10
  76. data/lib/png_conform/validators/jng/jdat_validator.rb +5 -5
  77. data/lib/png_conform/validators/jng/jhdr_validator.rb +10 -10
  78. data/lib/png_conform/validators/jng/jsep_validator.rb +7 -7
  79. data/lib/png_conform/validators/mng/back_validator.rb +7 -13
  80. data/lib/png_conform/validators/mng/clip_validator.rb +5 -7
  81. data/lib/png_conform/validators/mng/clon_validator.rb +4 -6
  82. data/lib/png_conform/validators/mng/defi_validator.rb +9 -15
  83. data/lib/png_conform/validators/mng/dhdr_validator.rb +11 -17
  84. data/lib/png_conform/validators/mng/disc_validator.rb +5 -7
  85. data/lib/png_conform/validators/mng/endl_validator.rb +8 -10
  86. data/lib/png_conform/validators/mng/fram_validator.rb +5 -7
  87. data/lib/png_conform/validators/mng/loop_validator.rb +5 -9
  88. data/lib/png_conform/validators/mng/mend_validator.rb +3 -3
  89. data/lib/png_conform/validators/mng/mhdr_validator.rb +4 -4
  90. data/lib/png_conform/validators/mng/move_validator.rb +5 -7
  91. data/lib/png_conform/validators/mng/save_validator.rb +4 -4
  92. data/lib/png_conform/validators/mng/seek_validator.rb +5 -5
  93. data/lib/png_conform/validators/mng/show_validator.rb +4 -6
  94. data/lib/png_conform/validators/mng/term_validator.rb +8 -14
  95. data/lib/png_conform/version.rb +1 -1
  96. data/lib/png_conform.rb +44 -0
  97. data/script/generate_mng_jng_fixtures +90 -0
  98. data/script/regenerate_diagnostic_goldens +9 -0
  99. metadata +37 -5
  100. data/png_conform.gemspec +0 -44
@@ -0,0 +1,38 @@
1
+ # 03c — Make orchestration format-aware
2
+
3
+ ## Purpose
4
+
5
+ Route complete PNG, MNG, and JNG files through their own signatures and top-level sequence requirements so valid non-PNG inputs do not receive false PNG diagnostics.
6
+
7
+ ## Work
8
+
9
+ - Give the active reader/orchestrator path one exact signature-to-format dispatch table for PNG (`89 50 4e 47 0d 0a 1a 0a`), MNG (`8a 4d 4e 47 0d 0a 1a 0a`), and JNG (`8b 4a 4e 47 0d 0a 1a 0a`). Preserve the detected format in validation context/result construction; an unknown signature emits its own cataloged signature diagnostic and does not trigger an arbitrary format's required-chunk findings.
10
+ - Refactor `lib/png_conform/services/validation_orchestrator.rb` to dispatch top-level rules by detected format: PNG/APNG requires first `IHDR`, last `IEND`, and at least one `IDAT`; MNG requires first `MHDR` and last `MEND`; JNG requires first `JHDR`, last `IEND`, and at least one `JDAT`. Check actual first/last positions as well as presence, and emit the card 02 catalog IDs and format-specific citations through the canonical constructor.
11
+ - Make `lib/png_conform/readers/streaming_reader.rb` recognize all three signatures and stop at the detected format's outer terminator (`IEND` for PNG/JNG, `MEND` for MNG), rather than rejecting or truncating an otherwise valid MNG/JNG stream. Keep `FullLoadReader` behavior covered so path-based validation continues to parse all three generic chunk streams.
12
+ - Extend `spec/png_conform/services/validation_orchestrator_spec.rb` for each recognized and unknown signature, each format's missing/misplaced header and terminator, PNG's missing `IDAT`, JNG's missing `JDAT`, and the absence of irrelevant cross-format findings.
13
+ - Add proposed `spec/png_conform/readers/format_dispatch_spec.rb`, `spec/integration/mng_validation_spec.rb`, and `spec/integration/jng_validation_spec.rb`. Build minimal complete files with valid CRCs in the focused specs; prove the public full-file path accepts valid `MHDR`/`MEND` and `JHDR`/`JDAT`/`IEND` streams with an empty diagnostic set. Keep `spec/integration/png_validation_spec.rb` as the PNG behavior-preservation proof.
14
+
15
+ ## Dependencies
16
+
17
+ - Cards 01, 02, 03a, and 03b. This card owns `ValidationOrchestrator` and the reader format-dispatch specs after the shared diagnostic constructor and format validators are available.
18
+ - Card 04 consumes the detected format and canonical top-level findings; card 05 consumes the full-file MNG/JNG integration path for goldens.
19
+
20
+ ## Done when
21
+
22
+ - Exact PNG, MNG, and JNG signatures select the correct format, while an unknown signature yields a cataloged signature diagnostic without unrelated PNG/MNG/JNG missing-chunk findings.
23
+ - Valid minimal PNG, MNG, and JNG files pass through the public full-file validation path with no diagnostics and the correct reported format.
24
+ - Missing or misplaced PNG `IHDR`/`IEND`/`IDAT`, MNG `MHDR`/`MEND`, and JNG `JHDR`/`IEND`/`JDAT` produce only the applicable stable IDs, citations, severities, and chunk locations.
25
+ - Streaming validation recognizes all three signatures, reads through the applicable outer terminator, and does not stop at an embedded MNG `IEND` before `MEND`.
26
+
27
+ ```bash
28
+ bundle exec rspec spec/png_conform/services/validation_orchestrator_spec.rb \
29
+ spec/png_conform/readers/format_dispatch_spec.rb \
30
+ spec/integration/png_validation_spec.rb \
31
+ spec/integration/mng_validation_spec.rb \
32
+ spec/integration/jng_validation_spec.rb
33
+ ```
34
+
35
+ ## Status
36
+
37
+ State: done
38
+ PR: https://github.com/claricle/png_conform/pull/18
@@ -0,0 +1,40 @@
1
+ # 04 — Unify Ruby, JSON, YAML, and text reports
2
+
3
+ ## Purpose
4
+
5
+ Make the canonical diagnostic the only source for public machine and human output while adding it to existing report shapes with compatibility aliases for one documented transition.
6
+
7
+ ## Work
8
+
9
+ - Update `PngConform::Models::ValidationResult` and `FileAnalysis#to_h` to expose the shared diagnostic projection without rebuilding or discarding fields. Add the upstream schema version at the contract-defined report location.
10
+ - Update `JsonReporter` and `YamlReporter` to serialize that projection with stable diagnostic ordering and explicit absent-field policy. Normalize or omit volatile timestamps and host-specific paths as prescribed by the shared contract.
11
+ - Update `TextReporter`, `SummaryReporter`, `VerboseReporter`, `VeryVerboseReporter`, `QuietReporter`, and `ColorReporter` to call the diagnostic’s human renderer, with reporter-specific filtering/color outside the diagnostic model.
12
+ - Preserve check-command exit behavior: error-severity diagnostics make a result invalid; warnings/info do not. Keep missing-file and unexpected-process failures on the operational error path.
13
+ - Preserve the existing Ruby readers `severity`, `message`, `chunk_type`, `chunk_offset`, `error_type`, `expected`, and `actual` as compatibility aliases for one documented transition.
14
+ - Preserve the JSON fields actually emitted by `FileAnalysis#to_h` today: `severity` and `message`, with optional `chunk_type`, `expected`, and `actual`. Add the canonical diagnostic projection without removing or repurposing those fields. Treat any flattened `chunk_offset` or `error_type` JSON fields as deliberate new additive projections—not legacy keys—and document and pin their exact placement if they are introduced in addition to the canonical location/kind fields.
15
+ - Update `README.adoc`, `docs/CLI_OPTIONS.adoc`, and `docs/ARCHITECTURE.adoc` with the schema/version, a structured example, location/citation semantics, and migration notes.
16
+
17
+ ## Dependencies
18
+
19
+ - Cards 01 and 03a–03c.
20
+
21
+ ## Done when
22
+
23
+ - Ruby API, JSON, and YAML expose the same diagnostic identity, severity, kind, citation, location, message, and context with JSON-safe values.
24
+ - Focused model/reporter specs preserve every existing Ruby reader and the current five-field legacy JSON projection, and explicitly assert any newly added flattened `chunk_offset` or `error_type` projection as additive.
25
+ - Text-family reporters render from the same object and preserve their documented filtering/color responsibilities.
26
+ - Error/warning validity and CLI exit status remain covered, including operational failures that are not conformance diagnostics.
27
+ - The documented examples match executable reporter output, identify every compatibility alias, define the transition boundary, and state the schema version.
28
+
29
+ ```bash
30
+ bundle exec rspec spec/png_conform/models/validation_error_spec.rb \
31
+ spec/png_conform/models/validation_result_spec.rb \
32
+ spec/png_conform/models/file_analysis_spec.rb \
33
+ spec/png_conform/reporters \
34
+ spec/png_conform/commands/check_command_spec.rb
35
+ ```
36
+
37
+ ## Status
38
+
39
+ State: done
40
+ PR: https://github.com/claricle/png_conform/pull/19
@@ -0,0 +1,42 @@
1
+ # 05 — Pin corpus JSON and the release gate
2
+
3
+ ## Purpose
4
+
5
+ Make the one-time schema transition reviewable and ensure future rule/report changes cannot silently alter diagnostics across the fixture corpus.
6
+
7
+ ## Work
8
+
9
+ - Add `script/regenerate_diagnostic_goldens` with explicit `--write` and non-mutating `--check` modes. Process fixtures in bytewise sorted repo-relative order and reject unknown options or a dirty/partial target set.
10
+ - Mirror all 180 PNG inputs under `spec/fixtures/diagnostics/pngsuite/` as canonical pretty JSON. Normalize input names to repo-relative paths and follow LensCore's timestamp/optional-field policy so repeated generation is byte-identical.
11
+ - Add a small redistributable fixture set under the proposed `spec/fixtures/mng/` and `spec/fixtures/jng/` paths, with source, license, and expected-validity metadata recorded beside the fixtures. Include representative valid and invalid MNG/JNG structures that exercise their format-specific citations.
12
+ - Mirror the MNG/JNG inputs under proposed `spec/fixtures/diagnostics/mng/` and `spec/fixtures/diagnostics/jng/` paths, and add proposed `spec/structured_diagnostics/corpus_spec.rb` to assert one-to-one cross-format input/golden coverage, parseability, schema version, deterministic ordering, and exact output equality. Include valid files with empty findings and invalid/edge files, using card 03c's format-aware full-file path rather than bypassing orchestration.
13
+ - Perform exactly one intentional `--write` regeneration after cards 01, 02, 03a–03c, and 04 have `State: done` and the shared schema, format dispatch, and rendering policy are frozen. Then review the complete artifact diff once as a dedicated transition step; subsequent CI uses `--check` and never rewrites files.
14
+ - Add the check to the existing Rake/CI path only after its runtime and memory usage are measured. Keep a targeted representative fixture subset for fast local development if the full corpus is too slow, but do not weaken the full CI acceptance gate.
15
+ - Document regeneration and review procedure in `CONTRIBUTING.md`; update fixture counts only through the coverage assertion, not a hand-maintained magic number.
16
+
17
+ ## Dependencies
18
+
19
+ - Cards 01–04, including card 03c's full-file format dispatch, and a frozen shared schema/ordering policy.
20
+ - Suitable redistributable MNG/JNG samples and recorded license/provenance; the current checked-in corpus contains 180 PNG fixtures and no MNG/JNG fixtures.
21
+
22
+ ## Done when
23
+
24
+ - Every checked-in PNG fixture and every newly licensed MNG/JNG fixture has exactly one deterministic structured JSON golden, including valid fixtures with no findings.
25
+ - The MNG/JNG fixture metadata records a redistribution-compatible license, source, format, and expected validity, and the set exercises both format-specific diagnostic citations.
26
+ - `--check` detects missing, extra, stale, reordered, schema-incompatible, or host-dependent golden output without writing files.
27
+ - The one-time transition regeneration is isolated for human review, and later CI proves no unreviewed drift.
28
+ - Contributor documentation explains targeted checks, full-corpus verification, and the controlled regeneration command.
29
+
30
+ ```bash
31
+ bundle exec rspec spec/integration/png_validation_spec.rb \
32
+ spec/integration/mng_validation_spec.rb \
33
+ spec/integration/jng_validation_spec.rb
34
+ script/regenerate_diagnostic_goldens --check
35
+ bundle exec rspec spec/structured_diagnostics/corpus_spec.rb
36
+ bundle exec rubocop
37
+ ```
38
+
39
+ ## Status
40
+
41
+ State: done
42
+ PR: https://github.com/claricle/png_conform/pull/12
@@ -0,0 +1,22 @@
1
+ # Progress and handoff
2
+
3
+ ## Current state
4
+
5
+ - Cards 01–05 are `done`. The structured diagnostics format from claricle/png_conform#9 is adopted end to end.
6
+ - Reports are built on the shared LensCore contract, with the requirement catalog, format-aware orchestration (PNG/MNG/JNG) and canonical report output in place.
7
+ - The corpus has one golden per checked-in fixture under `spec/fixtures/diagnostics/{pngsuite,idot,mng,jng}/`. Each golden is `ValidationResult#to_lens_report(filename: ...).to_h`, with every emitted diagnostic kept and no timestamp.
8
+ - `script/regenerate_diagnostic_goldens` has `--write` and `--check` modes. `bundle exec rake diagnostics:check` runs `--check` and is part of the default `rake` task (spec, rubocop, check), so CI catches drift.
9
+ - `spec/structured_diagnostics/corpus_spec.rb` asserts one-to-one fixture/golden coverage and exact output equality.
10
+ - `CONTRIBUTING.md` documents targeted checks, full-corpus verification and the controlled `--write` regeneration and review steps.
11
+
12
+ ## Next executable action
13
+
14
+ None for this effort. Regenerate goldens with `--write` only for an intended rule, report or schema change, review the full golden diff, then confirm with `bundle exec rake diagnostics:check`.
15
+
16
+ ## Blockers
17
+
18
+ None.
19
+
20
+ ## Remaining gaps and risks
21
+
22
+ - Dormant sources (`Services::ZlibValidator`, `Validators::StreamingIdatValidator`, `lib/png_conform/pipelines/`) remain excluded from the catalog unless a public contract is documented for them.
@@ -165,7 +165,7 @@ class BenchmarkRunner
165
165
  pct = (current.to_f / total * 100).round(1)
166
166
  bar_width = 40
167
167
  filled = (bar_width * current / total).to_i
168
- bar = "=" * filled + " " * (bar_width - filled)
168
+ bar = ("=" * filled) + (" " * (bar_width - filled))
169
169
 
170
170
  print "\r [#{bar}] #{pct}% (#{current}/#{total}) #{label} "
171
171
  $stdout.flush
@@ -46,8 +46,8 @@ class MetricsCollector
46
46
  tool_runs = runs_for_tool(tool)
47
47
  return nil if tool_runs.empty?
48
48
 
49
- execution_times = tool_runs.map { |r| r[:execution_time] }.compact
50
- memory_values = tool_runs.map { |r| r[:peak_memory] }.compact
49
+ execution_times = tool_runs.filter_map { |r| r[:execution_time] }
50
+ memory_values = tool_runs.filter_map { |r| r[:peak_memory] }
51
51
 
52
52
  {
53
53
  tool: tool,
@@ -118,7 +118,7 @@ class MetricsCollector
118
118
  total_runs: @runs.size,
119
119
  tools: tools,
120
120
  files_tested: @runs.map { |r| r[:file] }.uniq.size,
121
- tool_statistics: tools.map { |tool| calculate_statistics(tool) }.compact,
121
+ tool_statistics: tools.filter_map { |tool| calculate_statistics(tool) },
122
122
  }
123
123
  end
124
124
 
@@ -42,9 +42,9 @@ class ReportGenerator
42
42
  return "No benchmark data available.\n" if tools.empty?
43
43
 
44
44
  output = []
45
- output << "=" * 80
45
+ output << ("=" * 80)
46
46
  output << "PNG Validation Tool Benchmark Comparison"
47
- output << "=" * 80
47
+ output << ("=" * 80)
48
48
  output << ""
49
49
  output << "Configuration:"
50
50
  output << " Files tested: #{summary[:files_tested]} PNG files"
@@ -62,9 +62,9 @@ class ReportGenerator
62
62
  # Performance comparison (if we have 2 tools)
63
63
  if tools.size == 2
64
64
  comparison = metrics_collector.compare_tools(tools[0], tools[1])
65
- output << "-" * 80
65
+ output << ("-" * 80)
66
66
  output << "PERFORMANCE SUMMARY"
67
- output << "-" * 80
67
+ output << ("-" * 80)
68
68
  output << ""
69
69
  output << format_comparison_table(comparison)
70
70
  output << ""
@@ -73,9 +73,9 @@ class ReportGenerator
73
73
  end
74
74
 
75
75
  # Detailed statistics per tool
76
- output << "-" * 80
76
+ output << ("-" * 80)
77
77
  output << "DETAILED STATISTICS"
78
- output << "-" * 80
78
+ output << ("-" * 80)
79
79
  output << ""
80
80
 
81
81
  tools.each do |tool|
@@ -176,9 +176,8 @@ class ReportGenerator
176
176
  # Format comparison table for text output.
177
177
  def format_comparison_table(comparison)
178
178
  lines = []
179
- lines << sprintf("%-15s %12s %12s %12s %8s",
180
- "Tool", "Avg Time", "Files/sec", "Peak Memory", "Winner")
181
- lines << "-" * 80
179
+ lines << "Tool Avg Time Files/sec Peak Memory Winner"
180
+ lines << ("-" * 80)
182
181
 
183
182
  [comparison[:tool1], comparison[:tool2]].each do |tool|
184
183
  stats = comparison[:stats][tool]