mirror of
https://github.com/storytold/photocraft.git
synced 2026-11-07 10:54:10 +00:00
Integration: track the v12 .atn fixtures and sniff v12 in the CLI (#10743)
The three version 12 fixtures were tracked inside crates/psd/fuzz/corpus, which crates/psd/fuzz/.gitignore ignores and cargo-fuzz rewrites (cmin) or deletes; the psd and engine test binaries include_bytes! them, so cleaning the corpus broke the build. They move to crates/psd/tests/fixtures/atn with a README (sizes, purpose, sha256; the seeds note folded in) and an ATTRIBUTION.md row; the fuzzer takes the folder as an extra corpus dir. photocraft-cli recognised an extensionless Actions file only by version word 16, so a version 12 set without .atn (classic Mac OS Photoshop 5) went down the JSON path and failed with a UTF-8 error. It now accepts 12 and 16, the versions photocraft-psd reads. Input: v12-commands.atn copied to "Legacy Actions" and passed to load_actions_file. Co-authored-by: yoriswind <164435053+yoriswind@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
713aa92ba5
commit
1f524c378b
10 files changed
+56
-19
No files matched your search
@@ -140,6 +140,12 @@ next to the files:
|
||||
|
||||
Files copied into `corpus/` by hand (tiff, exr, raw) must be MIT, BSD or CC0.
|
||||
|
||||
## Synthetic parser test fixtures
|
||||
|
||||
| Path | Title | Author | Source | License |
|
||||
|---|---|---|---|---|
|
||||
| `crates/psd/tests/fixtures/atn/v12-*.atn` | Independently encoded ATN version-12 parser fixtures | @yoriswind | Original synthetic test data generated for PhotoCraft; wire layout documented in `crates/psd/src/atn.rs`; provenance in the adjacent README | MIT OR Apache-2.0 |
|
||||
|
||||
## Embedded-vector editor source
|
||||
|
||||
The external SVG watcher, engine/UI handoff and bounded SVG rendering in this change
|
||||
|
||||
@@ -83,11 +83,11 @@ pub fn load_actions(text: &str, name: Option<&str>) -> Result<(Steps, Vec<Action
|
||||
}
|
||||
|
||||
/// [`load_actions`] for a file on disk: a Photoshop Actions file (`.atn`, by extension or by
|
||||
/// its version word) is converted first; the played action's steps with no PhotoCraft
|
||||
/// its version word, 12 or 16) is converted first; the played action's steps with no PhotoCraft
|
||||
/// equivalent come back as warnings (they fail at playback, as in Photoshop when a step can't run).
|
||||
pub fn load_actions_file(path: &Path, name: Option<&str>) -> Result<(Steps, Vec<Action>, Vec<String>), ActionsError> {
|
||||
let bytes = std::fs::read(path).map_err(|source| ActionsError::Read { path: path.to_path_buf(), source })?;
|
||||
let is_atn = path.extension().is_some_and(|e| e.eq_ignore_ascii_case("atn")) || bytes.starts_with(&[0, 0, 0, 16]);
|
||||
let is_atn = path.extension().is_some_and(|e| e.eq_ignore_ascii_case("atn")) || matches!(bytes.get(..4), Some([0, 0, 0, 12 | 16]));
|
||||
if !is_atn {
|
||||
let text = String::from_utf8(bytes).map_err(|source| ActionsError::Utf8 { path: path.to_path_buf(), source })?;
|
||||
let (steps, set) = load_actions(&text, name)?;
|
||||
|
||||
@@ -1090,6 +1090,20 @@ fn batch_plays_a_photoshop_actions_file() {
|
||||
assert!(!o.status.success());
|
||||
}
|
||||
|
||||
/// A version 12 Actions file (Photoshop 5, often saved on classic Mac OS without an extension) is
|
||||
/// recognised by its version word like version 16, not read as a JSON actions list.
|
||||
#[test]
|
||||
fn extensionless_version_12_actions_file_is_read_as_atn() {
|
||||
let d = tmp("atn-v12");
|
||||
let path = d.join("Legacy Actions");
|
||||
std::fs::write(&path, include_bytes!("../../../../crates/psd/tests/fixtures/atn/v12-commands.atn")).unwrap();
|
||||
let (steps, set, warnings) = photocraft_cli::load_actions_file(&path, None).unwrap();
|
||||
assert_eq!(set.len(), 1);
|
||||
assert_eq!(steps, vec![("actions.play".to_string(), json!({"action": set[0].name}))]);
|
||||
assert!(warnings.is_empty(), "{warnings:?}");
|
||||
std::fs::remove_dir_all(d).unwrap();
|
||||
}
|
||||
|
||||
/// What `photocraft_cli::run` returned and wrote, in-process.
|
||||
struct Invoke {
|
||||
code: i32,
|
||||
|
||||
@@ -5,7 +5,7 @@ use serde_json::json;
|
||||
/// Legacy descriptor syntax must reach the same commands and parameters as version 16.
|
||||
#[test]
|
||||
fn legacy_v12_commands_import_like_v16() {
|
||||
let bytes = include_bytes!("../../../psd/fuzz/corpus/atn/v12-commands.atn");
|
||||
let bytes = include_bytes!("../../../psd/tests/fixtures/atn/v12-commands.atn");
|
||||
let modern = atn::write(&atn::parse(bytes).unwrap()).unwrap();
|
||||
let expected = vec![
|
||||
("filter.blur.gaussianBlur".into(), json!({"radius": 2.5})),
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
The small synthetic sets in `corpus/atn/v12-*.atn` were assembled from the measured grammar
|
||||
in #10603, without using PhotoCraft's parser or writer. No proprietary files or
|
||||
assets are included. They also serve as inputs for `tests/it/atn_v12.rs`.
|
||||
|
||||
- `v12-stop.atn`: Stop with Unicode text and a Boolean.
|
||||
- `v12-values.atn`: named plug-in event, every legacy value/reference form,
|
||||
bare `warp`/`list`/`time` codes, an object array with three objects and two
|
||||
columns, unpaired UTF-16, opaque path/alias/data bytes, and a descriptorless step.
|
||||
- `v12-commands.atn`: Gaussian Blur, Levels, Select All and Make Layer.
|
||||
|
||||
`Idnt` follows the measured reference's inferred index-like layout; no real
|
||||
version 12 identifier reference was available. Saving these sets produces
|
||||
version 16. Keep them outside the version 16 byte-identical corpus oracle.
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
# Synthetic ATN parser fixtures
|
||||
|
||||
These seven tiny files were independently encoded field by field from the version-16 and
|
||||
version-12 layouts documented in `src/atn.rs`, without calling PhotoCraft's parser or writer. They are
|
||||
original test data, licensed MIT OR Apache-2.0 under the repository's licenses. They contain no user
|
||||
artwork, Photoshop program code or proprietary assets. They are not Photoshop-authored
|
||||
interoperability oracles.
|
||||
|
||||
| File | Bytes | Purpose | SHA-256 |
|
||||
|---|---:|---|---|
|
||||
| `empty.atn` | 13 | Empty set and explicit big-endian version | `6fd936f91296a13b0e7456a38b139a100fd17407d1c582d4502ce76413d01539` |
|
||||
| `metadata.atn` | 214 | UTF-16 surrogate pairs/unpaired units, action order, function key, modifiers, panel flags, disabled step, unknown string event, dialog options | `cec2ff5a25e003aa29e790ea6b110f06ba848f0afd7901f3f8ad4740f782ede2` |
|
||||
| `descriptors.atn` | 859 | Every descriptor value variant and every reference form currently represented by the parser | `6c76502822c1f36c8060a71adb227c4540663518785bbab1b70951dac0ef58ad` |
|
||||
| `float-bits.atn` | 428 | NaN payload, negative zero, infinities, and floats inside lists, objects and arrays | `0aa6278a569c777f249b305e889896132c2a87e44a24cb1a55d1d3fc0928fd0c` |
|
||||
| `v12-stop.atn` | 113 | Version 12: Stop with Unicode text and a Boolean | `293462a7d5cf0043fdc5135630524699f545679088c54bad83f4a86e15160271` |
|
||||
| `v12-values.atn` | 562 | Version 12: named plug-in event, every legacy value and reference form, bare `warp`/`list`/`time` codes, an object array with three objects and two columns, unpaired UTF-16, opaque path/alias/data bytes, and a descriptorless step | `23d3f377996435988367365ee5c2a15462c0ed42ce506dc4a15039387ac6a853` |
|
||||
| `v12-commands.atn` | 404 | Version 12: Gaussian Blur, Levels, Select All and Make Layer | `451958b686da9aab45e65ae902c86333b7149d72021c21904e3aae8e39eb77cb` |
|
||||
|
||||
`tests/it/atn_contract.rs` checks the independently encoded version-16 bytes and expected public
|
||||
structures; `tests/it/atn_v12.rs` (and `photocraft-engine`'s `tests/it/atn_v12.rs`) check the
|
||||
version-12 files. The version-12 files follow the version 12 layout in `src/atn.rs`, assembled from
|
||||
the measured grammar in #10603. Their `Idnt` reference follows the inferred index-like layout; no
|
||||
real version 12 identifier reference was available. Saving them produces version 16, so keep them
|
||||
out of the version-16 byte-identical corpus oracle.
|
||||
|
||||
The files also seed the `atn` fuzz target: `cargo fuzz run atn crates/psd/tests/fixtures/atn`
|
||||
(from `crates/psd`: `cargo fuzz run atn tests/fixtures/atn`).
|
||||
|
||||
A fixture change should explain the changed wire fields and update this table; do not regenerate
|
||||
expectations merely to make a failing parser pass.
|
||||
Vendored
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -2,9 +2,9 @@ use photocraft_psd::PsdError;
|
||||
use photocraft_psd::atn;
|
||||
use photocraft_psd::descriptor::{Class, Descriptor, Id, MAX_DEPTH, ObjectArray, ReferenceItem as R, UnicodeString, Value as V};
|
||||
|
||||
const STOP: &[u8] = include_bytes!("../../fuzz/corpus/atn/v12-stop.atn");
|
||||
const VALUES: &[u8] = include_bytes!("../../fuzz/corpus/atn/v12-values.atn");
|
||||
const COMMANDS: &[u8] = include_bytes!("../../fuzz/corpus/atn/v12-commands.atn");
|
||||
const STOP: &[u8] = include_bytes!("../fixtures/atn/v12-stop.atn");
|
||||
const VALUES: &[u8] = include_bytes!("../fixtures/atn/v12-values.atn");
|
||||
const COMMANDS: &[u8] = include_bytes!("../fixtures/atn/v12-commands.atn");
|
||||
|
||||
fn code(id: &[u8; 4]) -> Id {
|
||||
Id::Code(*id)
|
||||
|
||||
Reference in new issue
Block a user