Files
deckcraft/apps/deckcraft-cli/tests/damaged.rs
Faabyy 368e07493c PPTX: a slide that can't be read is reported, not dropped in silence (#203)
The importer skipped a slide whose part was missing or unreadable and only
logged it, so `info` showed one slide fewer with exit code 0 and a save wrote
the shorter deck over the original.

`deckcraft_pptx::import_checked` returns the parts it left out (`import` is
unchanged). The engine carries them as `Damage::slides` next to the damaged
media of a `.deckcraft` file: `file.open` and `file.openBytes` return
`lostSlides`, the app shows a message and opens a repaired copy (so Save asks
for a new name), and `deckcraft-cli` warns on stderr.

Co-Authored-By: Claude Code <noreply@anthropic.com>


# Please enter the commit message for your changes. Lines starting
# with '#' will be kept; you may remove them yourself if you want to.
# An empty message aborts the commit.
#
# interactive rebase in progress; onto 1dc6972
# Last command done (1 command done):
#    pick d2e995c PPTX: a slide that can't be read is reported, not dropped in silence (#203)
# No commands remaining.
# You are currently rebasing.
#
# Changes to be committed:
#	modified:   Cargo.lock
#	modified:   ROADMAP.md
#	modified:   apps/deckcraft-cli/Cargo.toml
#	modified:   apps/deckcraft-cli/src/main.rs
#	new file:   apps/deckcraft-cli/tests/damaged.rs
#	modified:   crates/engine/src/cmd/file.rs
#	modified:   crates/engine/src/tests.rs
#	modified:   crates/pptx/src/lib.rs
#	modified:   crates/pptx/src/read/mod.rs
#	modified:   crates/pptx/tests/malformed.rs
#	modified:   docs/file-format-parity.md
#


# Please enter the commit message for your changes. Lines starting
# with '#' will be kept; you may remove them yourself if you want to.
# An empty message aborts the commit.
#
# interactive rebase in progress; onto 22db926
# Last command done (1 command done):
#    pick 31b0d33 PPTX: a slide that can't be read is reported, not dropped in silence (#203)
# No commands remaining.
# You are currently rebasing.
#
# Changes to be committed:
#	modified:   Cargo.lock
#	modified:   ROADMAP.md
#	modified:   apps/deckcraft-cli/Cargo.toml
#	modified:   apps/deckcraft-cli/src/main.rs
#	new file:   apps/deckcraft-cli/tests/damaged.rs
#	modified:   crates/engine/src/cmd/file.rs
#	modified:   crates/engine/src/tests.rs
#	modified:   crates/pptx/src/lib.rs
#	modified:   crates/pptx/src/read/mod.rs
#	modified:   crates/pptx/tests/malformed.rs
#	modified:   docs/file-format-parity.md
#
2026-10-18 09:18:46 +00:00

54 lines
2.1 KiB
Rust

//! Opening a `.pptx` that lost slides says so on stderr instead of saving a shorter deck in silence (#203).
use std::io::{Cursor, Read, Write};
use std::path::PathBuf;
use std::process::{Command, Output};
fn cli(args: &[&str]) -> Output {
Command::new(env!("CARGO_BIN_EXE_deckcraft-cli")).args(args).output().unwrap()
}
fn temp_dir(name: &str) -> PathBuf {
let dir = std::env::temp_dir().join(format!("deckcraft-cli-{name}-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
dir
}
/// `deck` with `ppt/slides/slide2.xml` cut off.
fn without_slide2(deck: &[u8]) -> Vec<u8> {
let mut zin = zip::ZipArchive::new(Cursor::new(deck)).unwrap();
let mut out = zip::ZipWriter::new(Cursor::new(Vec::new()));
let options = zip::write::SimpleFileOptions::default();
for i in 0..zin.len() {
let mut f = zin.by_index(i).unwrap();
let mut data = vec![];
f.read_to_end(&mut data).unwrap();
if f.name() == "ppt/slides/slide2.xml" {
data = b"<p:sld".to_vec();
}
out.start_file(f.name(), options).unwrap();
out.write_all(&data).unwrap();
}
out.finish().unwrap().into_inner()
}
#[test]
fn a_pptx_with_an_unreadable_slide_is_reported_on_stderr() {
let dir = temp_dir("lost-slide");
let good = dir.join("good.pptx");
let made = cli(&["run", "--sample", "--save", good.to_str().unwrap()]);
assert!(made.status.success(), "{}", String::from_utf8_lossy(&made.stderr));
let broken = dir.join("broken.pptx");
std::fs::write(&broken, without_slide2(&std::fs::read(&good).unwrap())).unwrap();
let ok = cli(&["info", good.to_str().unwrap()]);
assert!(ok.status.success() && !String::from_utf8_lossy(&ok.stderr).contains("warning"), "{}", String::from_utf8_lossy(&ok.stderr));
let bad = cli(&["info", broken.to_str().unwrap()]);
assert!(bad.status.success(), "{}", String::from_utf8_lossy(&bad.stderr));
let err = String::from_utf8_lossy(&bad.stderr);
assert!(err.contains("warning") && err.contains("1 slide couldn't be read and left out"), "{err}");
let _ = std::fs::remove_dir_all(&dir);
}