summaryrefslogtreecommitdiff
path: root/rsvg-convert/tests/rsvg_convert.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rsvg-convert/tests/rsvg_convert.rs')
-rw-r--r--rsvg-convert/tests/rsvg_convert.rs1078
1 files changed, 1078 insertions, 0 deletions
diff --git a/rsvg-convert/tests/rsvg_convert.rs b/rsvg-convert/tests/rsvg_convert.rs
new file mode 100644
index 00000000..f58edf6c
--- /dev/null
+++ b/rsvg-convert/tests/rsvg_convert.rs
@@ -0,0 +1,1078 @@
+//use crate::predicates::ends_with_pkg_version;
+mod internal_predicates;
+use internal_predicates::file;
+
+use assert_cmd::assert::IntoOutputPredicate;
+use assert_cmd::Command;
+#[cfg(system_deps_have_cairo_pdf)]
+use chrono::{TimeZone, Utc};
+use predicates::boolean::*;
+use predicates::prelude::*;
+use predicates::str::*;
+use rsvg::{Length, LengthUnit};
+use std::path::Path;
+use tempfile::Builder;
+use url::Url;
+
+// What should be tested here?
+// The goal is to test the code in rsvg-convert, not the entire library.
+//
+// - command-line options that affect size (width, height, zoom, resolution) ✔
+// - pixel dimensions of the output (should be sufficient to do that for PNG) ✔
+// - limit on output size (32767 pixels) ✔
+// - output formats (PNG, PDF, PS, EPS, SVG) ✔
+// - multi-page output (for PDF) ✔
+// - output file option ✔
+// - SOURCE_DATA_EPOCH environment variable for PDF output ✔
+// - background color option ✔
+// - optional CSS stylesheet ✔
+// - error handling for missing SVG dimensions ✔
+// - error handling for export lookup ID ✔
+// - error handling for invalid input ✔
+
+struct RsvgConvert {}
+
+impl RsvgConvert {
+ fn new() -> Command {
+ Command::cargo_bin("rsvg-convert").unwrap()
+ }
+
+ fn new_with_input<P>(file: P) -> Command
+ where
+ P: AsRef<Path>,
+ {
+ let mut command = RsvgConvert::new();
+ match command.pipe_stdin(&file) {
+ Ok(_) => command,
+ Err(e) => panic!("Error opening file '{}': {}", file.as_ref().display(), e),
+ }
+ }
+
+ fn accepts_arg(option: &str) {
+ RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
+ .arg(option)
+ .assert()
+ .success();
+ }
+
+ fn option_yields_output<I, P>(option: &str, output_pred: I)
+ where
+ I: IntoOutputPredicate<P>,
+ P: Predicate<[u8]>,
+ {
+ RsvgConvert::new()
+ .arg(option)
+ .assert()
+ .success()
+ .stdout(output_pred);
+ }
+}
+
+#[test]
+fn converts_svg_from_stdin_to_png() {
+ RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
+ .assert()
+ .success()
+ .stdout(file::is_png());
+}
+
+#[test]
+fn argument_is_input_filename() {
+ let input = Path::new("tests/fixtures/bug521-with-viewbox.svg");
+ RsvgConvert::new()
+ .arg(input)
+ .assert()
+ .success()
+ .stdout(file::is_png());
+}
+
+#[test]
+fn argument_is_url() {
+ let path = Path::new("tests/fixtures/bug521-with-viewbox.svg")
+ .canonicalize()
+ .unwrap();
+ let url = Url::from_file_path(path).unwrap();
+ let stringified = url.as_str();
+ assert!(stringified.starts_with("file://"));
+
+ RsvgConvert::new()
+ .arg(stringified)
+ .assert()
+ .success()
+ .stdout(file::is_png());
+}
+
+#[test]
+fn output_format_png() {
+ RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
+ .arg("--format=png")
+ .assert()
+ .success()
+ .stdout(file::is_png());
+}
+
+#[cfg(system_deps_have_cairo_ps)]
+#[test]
+fn output_format_ps() {
+ RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
+ .arg("--format=ps")
+ .assert()
+ .success()
+ .stdout(file::is_ps());
+}
+
+#[cfg(system_deps_have_cairo_ps)]
+#[test]
+fn output_format_eps() {
+ RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
+ .arg("--format=eps")
+ .assert()
+ .success()
+ .stdout(file::is_eps());
+}
+
+#[cfg(system_deps_have_cairo_pdf)]
+#[test]
+fn output_format_pdf() {
+ RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
+ .arg("--format=pdf")
+ .assert()
+ .success()
+ .stdout(file::is_pdf());
+}
+
+#[cfg(system_deps_have_cairo_svg)]
+#[test]
+fn output_format_svg_short_option() {
+ RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
+ .arg("-f")
+ .arg("svg")
+ .assert()
+ .success()
+ .stdout(file::is_svg());
+}
+
+#[cfg(system_deps_have_cairo_svg)]
+#[test]
+fn user_specified_width_and_height() {
+ RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
+ .arg("--format")
+ .arg("svg")
+ .arg("--width")
+ .arg("42cm")
+ .arg("--height")
+ .arg("43cm")
+ .assert()
+ .success()
+ .stdout(file::is_svg().with_size(
+ Length::new(42.0, LengthUnit::Cm),
+ Length::new(43.0, LengthUnit::Cm),
+ ));
+}
+
+#[cfg(system_deps_have_cairo_svg)]
+#[test]
+fn user_specified_width_and_height_px_output() {
+ RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
+ .arg("--format")
+ .arg("svg")
+ .arg("--width")
+ .arg("1920")
+ .arg("--height")
+ .arg("508mm")
+ .assert()
+ .success()
+ .stdout(file::is_svg().with_size(
+ Length::new(1920.0, LengthUnit::Px),
+ Length::new(1920.0, LengthUnit::Px),
+ ));
+}
+
+#[cfg(system_deps_have_cairo_svg)]
+#[test]
+fn user_specified_width_and_height_a4() {
+ RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
+ .arg("--format")
+ .arg("svg")
+ .arg("--page-width")
+ .arg("210mm")
+ .arg("--page-height")
+ .arg("297mm")
+ .arg("--left")
+ .arg("1cm")
+ .arg("--top")
+ .arg("1cm")
+ .arg("--width")
+ .arg("190mm")
+ .arg("--height")
+ .arg("277mm")
+ .assert()
+ .success()
+ .stdout(file::is_svg().with_size(
+ Length::new(210.0, LengthUnit::Mm),
+ Length::new(297.0, LengthUnit::Mm),
+ ));
+}
+
+#[test]
+fn output_file_option() {
+ let output = {
+ let tempfile = Builder::new().suffix(".png").tempfile().unwrap();
+ tempfile.path().to_path_buf()
+ };
+ assert!(predicates::path::is_file().not().eval(&output));
+
+ RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
+ .arg(format!("--output={}", output.display()))
+ .assert()
+ .success()
+ .stdout(is_empty());
+
+ assert!(predicates::path::is_file().eval(&output));
+ std::fs::remove_file(&output).unwrap();
+}
+
+#[test]
+fn output_file_short_option() {
+ let output = {
+ let tempfile = Builder::new().suffix(".png").tempfile().unwrap();
+ tempfile.path().to_path_buf()
+ };
+ assert!(predicates::path::is_file().not().eval(&output));
+
+ RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
+ .arg("-o")
+ .arg(format!("{}", output.display()))
+ .assert()
+ .success()
+ .stdout(is_empty());
+
+ assert!(predicates::path::is_file().eval(&output));
+ std::fs::remove_file(&output).unwrap();
+}
+
+#[test]
+fn overwrites_existing_output_file() {
+ let output = {
+ let tempfile = Builder::new().suffix(".png").tempfile().unwrap();
+ tempfile.path().to_path_buf()
+ };
+ assert!(predicates::path::is_file().not().eval(&output));
+
+ for _ in 0..2 {
+ RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
+ .arg(format!("--output={}", output.display()))
+ .assert()
+ .success()
+ .stdout(is_empty());
+
+ assert!(predicates::path::is_file().eval(&output));
+ }
+
+ std::fs::remove_file(&output).unwrap();
+}
+
+#[test]
+fn empty_input_yields_error() {
+ let starts_with = starts_with("Error reading SVG");
+ let ends_with = ends_with("Input file is too short").trim();
+ RsvgConvert::new()
+ .assert()
+ .failure()
+ .stderr(starts_with.and(ends_with));
+}
+
+#[test]
+fn empty_svg_yields_error() {
+ RsvgConvert::new_with_input("tests/fixtures/empty.svg")
+ .assert()
+ .failure()
+ .stderr("The SVG stdin has no dimensions\n");
+}
+
+#[test]
+fn multiple_input_files_not_allowed_for_png_output() {
+ let one = Path::new("tests/fixtures/bug521-with-viewbox.svg");
+ let two = Path::new("tests/fixtures/sub-rect-no-unit.svg");
+ RsvgConvert::new()
+ .arg(one)
+ .arg(two)
+ .assert()
+ .failure()
+ .stderr(contains(
+ "Multiple SVG files are only allowed for PDF and (E)PS output",
+ ));
+}
+
+#[cfg(system_deps_have_cairo_ps)]
+#[test]
+fn multiple_input_files_accepted_for_eps_output() {
+ let one = Path::new("tests/fixtures/bug521-with-viewbox.svg");
+ let two = Path::new("tests/fixtures/sub-rect-no-unit.svg");
+ RsvgConvert::new()
+ .arg("--format=eps")
+ .arg(one)
+ .arg(two)
+ .assert()
+ .success()
+ .stdout(file::is_eps());
+}
+
+#[cfg(system_deps_have_cairo_ps)]
+#[test]
+fn multiple_input_files_accepted_for_ps_output() {
+ let one = Path::new("tests/fixtures/bug521-with-viewbox.svg");
+ let two = Path::new("tests/fixtures/sub-rect-no-unit.svg");
+ RsvgConvert::new()
+ .arg("--format=ps")
+ .arg(one)
+ .arg(two)
+ .assert()
+ .success()
+ .stdout(file::is_ps());
+}
+
+#[cfg(system_deps_have_cairo_pdf)]
+#[test]
+fn multiple_input_files_create_multi_page_pdf_output() {
+ let one = Path::new("tests/fixtures/bug521-with-viewbox.svg");
+ let two = Path::new("tests/fixtures/sub-rect-no-unit.svg");
+ let three = Path::new("tests/fixtures/example.svg");
+ RsvgConvert::new()
+ .arg("--format=pdf")
+ .arg(one)
+ .arg(two)
+ .arg(three)
+ .assert()
+ .success()
+ .stdout(
+ file::is_pdf()
+ .with_page_count(3)
+ .and(file::is_pdf().with_page_size(0, 150.0, 75.0))
+ .and(file::is_pdf().with_page_size(1, 123.0, 123.0))
+ .and(file::is_pdf().with_page_size(2, 75.0, 300.0)),
+ );
+}
+
+#[cfg(system_deps_have_cairo_pdf)]
+#[test]
+fn multiple_input_files_create_multi_page_pdf_output_fixed_size() {
+ let one = Path::new("tests/fixtures/bug521-with-viewbox.svg");
+ let two = Path::new("tests/fixtures/sub-rect-no-unit.svg");
+ let three = Path::new("tests/fixtures/example.svg");
+ RsvgConvert::new()
+ .arg("--format=pdf")
+ .arg("--page-width=8.5in")
+ .arg("--page-height=11in")
+ .arg("--width=7.5in")
+ .arg("--height=10in")
+ .arg("--left=0.5in")
+ .arg("--top=0.5in")
+ .arg("--keep-aspect-ratio")
+ .arg(one)
+ .arg(two)
+ .arg(three)
+ .assert()
+ .success()
+ .stdout(
+ file::is_pdf()
+ .with_page_count(3)
+ // https://www.wolframalpha.com/input/?i=convert+11+inches+to+desktop+publishing+points
+ .and(file::is_pdf().with_page_size(0, 612.0, 792.0))
+ .and(file::is_pdf().with_page_size(1, 612.0, 792.0))
+ .and(file::is_pdf().with_page_size(2, 612.0, 792.0)),
+ );
+}
+
+#[cfg(system_deps_have_cairo_pdf)]
+#[test]
+fn pdf_has_link() {
+ let input = Path::new("tests/fixtures/a-link.svg");
+ RsvgConvert::new()
+ .arg("--format=pdf")
+ .arg(input)
+ .assert()
+ .success()
+ .stdout(file::is_pdf().with_link("https://example.com"));
+}
+
+#[cfg(system_deps_have_cairo_pdf)]
+#[test]
+fn pdf_has_link_inside_text() {
+ let input = Path::new("tests/fixtures/text-a-link.svg");
+ RsvgConvert::new()
+ .arg("--format=pdf")
+ .arg(input)
+ .assert()
+ .success()
+ .stdout(
+ file::is_pdf()
+ .with_link("https://example.com")
+ .and(file::is_pdf().with_link("https://another.example.com")),
+ );
+}
+
+#[cfg(system_deps_have_cairo_pdf)]
+#[test]
+fn pdf_has_text() {
+ let input = Path::new("tests/fixtures/hello-world.svg");
+ RsvgConvert::new()
+ .arg("--format=pdf")
+ .arg(input)
+ .assert()
+ .success()
+ .stdout(
+ file::is_pdf()
+ .with_text("Hello world!")
+ .and(file::is_pdf().with_text("Hello again!")),
+ );
+}
+
+#[cfg(system_deps_have_cairo_pdf)]
+#[test]
+fn env_source_data_epoch_controls_pdf_creation_date() {
+ let input = Path::new("tests/fixtures/bug521-with-viewbox.svg");
+ let date = 1581411039; // seconds since epoch
+ RsvgConvert::new()
+ .env("SOURCE_DATE_EPOCH", format!("{}", date))
+ .arg("--format=pdf")
+ .arg(input)
+ .assert()
+ .success()
+ .stdout(file::is_pdf().with_creation_date(Utc.timestamp_opt(date, 0).unwrap()));
+}
+
+#[cfg(system_deps_have_cairo_pdf)]
+#[test]
+fn env_source_data_epoch_no_digits() {
+ // intentionally not testing for the full error string here
+ let input = Path::new("tests/fixtures/bug521-with-viewbox.svg");
+ RsvgConvert::new()
+ .env("SOURCE_DATE_EPOCH", "foobar")
+ .arg("--format=pdf")
+ .arg(input)
+ .assert()
+ .failure()
+ .stderr(starts_with("Environment variable $SOURCE_DATE_EPOCH"));
+}
+
+#[cfg(system_deps_have_cairo_pdf)]
+#[test]
+fn env_source_data_epoch_trailing_garbage() {
+ // intentionally not testing for the full error string here
+ let input = Path::new("tests/fixtures/bug521-with-viewbox.svg");
+ RsvgConvert::new()
+ .arg("--format=pdf")
+ .env("SOURCE_DATE_EPOCH", "1234556+")
+ .arg(input)
+ .assert()
+ .failure()
+ .stderr(starts_with("Environment variable $SOURCE_DATE_EPOCH"));
+}
+
+#[cfg(system_deps_have_cairo_pdf)]
+#[test]
+fn env_source_data_epoch_empty() {
+ // intentionally not testing for the full error string here
+ let input = Path::new("tests/fixtures/bug521-with-viewbox.svg");
+ RsvgConvert::new()
+ .arg("--format=pdf")
+ .env("SOURCE_DATE_EPOCH", "")
+ .arg(input)
+ .assert()
+ .failure()
+ .stderr(starts_with("Environment variable $SOURCE_DATE_EPOCH"));
+}
+
+#[test]
+fn width_option() {
+ RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
+ .arg("--width=300")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_size(300, 150));
+}
+
+#[test]
+fn height_option() {
+ RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
+ .arg("--height=200")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_size(400, 200));
+}
+
+#[test]
+fn width_and_height_options() {
+ RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
+ .arg("--width=300")
+ .arg("--height=200")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_size(300, 200));
+}
+
+#[test]
+fn unsupported_unit_in_width_and_height() {
+ RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
+ .arg("--height=200ex")
+ .assert()
+ .failure()
+ .stderr(contains("supported units"));
+}
+
+#[test]
+fn invalid_length() {
+ RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
+ .arg("--page-width=foo")
+ .assert()
+ .failure()
+ .stderr(contains("can not be parsed as a length"));
+}
+
+#[test]
+fn zoom_factor() {
+ RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
+ .arg("--zoom=0.8")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_size(160, 80));
+}
+
+#[test]
+fn zoom_factor_and_larger_size() {
+ RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
+ .arg("--width=400")
+ .arg("--height=200")
+ .arg("--zoom=1.5")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_size(300, 150));
+}
+
+#[test]
+fn zoom_factor_and_smaller_size() {
+ RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
+ .arg("--width=400")
+ .arg("--height=200")
+ .arg("--zoom=3.5")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_size(400, 200));
+}
+
+#[test]
+fn x_zoom_option() {
+ RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
+ .arg("--x-zoom=2")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_size(400, 100));
+}
+
+#[test]
+fn x_short_option() {
+ RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
+ .arg("-x")
+ .arg("2.0")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_size(400, 100));
+}
+
+#[test]
+fn y_zoom_option() {
+ RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
+ .arg("--y-zoom=2.0")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_size(200, 200));
+}
+
+#[test]
+fn y_short_option() {
+ RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
+ .arg("-y")
+ .arg("2")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_size(200, 200));
+}
+
+#[test]
+fn huge_zoom_factor_yields_error() {
+ RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
+ .arg("--zoom=1000")
+ .assert()
+ .failure()
+ .stderr(starts_with(
+ "The resulting image would be larger than 32767 pixels",
+ ));
+}
+
+#[test]
+fn negative_zoom_factor_yields_error() {
+ RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
+ .arg("--zoom=-2")
+ .assert()
+ .failure()
+ .stderr(contains("Invalid zoom"));
+}
+
+#[test]
+fn invalid_zoom_factor_yields_error() {
+ RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
+ .arg("--zoom=foo")
+ .assert()
+ .failure()
+ .stderr(contains("invalid value"));
+}
+
+#[test]
+fn default_resolution_is_96dpi() {
+ RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_size(96, 384));
+}
+
+#[test]
+fn x_resolution() {
+ RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
+ .arg("--dpi-x=300")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_size(300, 384));
+}
+
+#[test]
+fn x_resolution_short_option() {
+ RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
+ .arg("-d")
+ .arg("45")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_size(45, 384));
+}
+
+#[test]
+fn y_resolution() {
+ RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
+ .arg("--dpi-y=300")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_size(96, 1200));
+}
+
+#[test]
+fn y_resolution_short_option() {
+ RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
+ .arg("-p")
+ .arg("45")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_size(96, 180));
+}
+
+#[test]
+fn x_and_y_resolution() {
+ RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
+ .arg("--dpi-x=300")
+ .arg("--dpi-y=150")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_size(300, 600));
+}
+
+#[test]
+fn zero_resolution_is_invalid() {
+ RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
+ .arg("--dpi-x=0")
+ .arg("--dpi-y=0")
+ .assert()
+ .failure()
+ .stderr(contains("Invalid resolution"));
+}
+
+#[test]
+fn negative_resolution_is_invalid() {
+ RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
+ .arg("--dpi-x=-100")
+ .arg("--dpi-y=-100")
+ .assert()
+ .failure()
+ .stderr(contains("Invalid resolution"));
+}
+
+#[test]
+fn zero_offset_png() {
+ RsvgConvert::new_with_input("tests/fixtures/dimensions-in.svg")
+ .arg("--page-width=640")
+ .arg("--page-height=480")
+ .arg("--width=200")
+ .arg("--height=100")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_contents("tests/fixtures/zero-offset-png.png"));
+}
+
+#[test]
+fn offset_png() {
+ RsvgConvert::new_with_input("tests/fixtures/dimensions-in.svg")
+ .arg("--page-width=640")
+ .arg("--page-height=480")
+ .arg("--width=200")
+ .arg("--height=100")
+ .arg("--left=100")
+ .arg("--top=50")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_contents("tests/fixtures/offset-png.png"));
+}
+
+#[cfg(system_deps_have_cairo_pdf)]
+#[test]
+fn unscaled_pdf_size() {
+ RsvgConvert::new_with_input("tests/fixtures/dimensions-in.svg")
+ .arg("--format=pdf")
+ .assert()
+ .success()
+ .stdout(file::is_pdf().with_page_size(0, 72.0, 72.0));
+}
+
+#[cfg(system_deps_have_cairo_pdf)]
+#[test]
+fn pdf_size_width_height() {
+ RsvgConvert::new_with_input("tests/fixtures/dimensions-in.svg")
+ .arg("--format=pdf")
+ .arg("--width=2in")
+ .arg("--height=3in")
+ .assert()
+ .success()
+ .stdout(file::is_pdf().with_page_size(0, 144.0, 216.0));
+}
+
+#[cfg(system_deps_have_cairo_pdf)]
+#[test]
+fn pdf_size_width_height_proportional() {
+ RsvgConvert::new_with_input("tests/fixtures/dimensions-in.svg")
+ .arg("--format=pdf")
+ .arg("--width=2in")
+ .arg("--height=3in")
+ .arg("--keep-aspect-ratio")
+ .assert()
+ .success()
+ .stdout(file::is_pdf().with_page_size(0, 144.0, 144.0));
+}
+
+#[cfg(system_deps_have_cairo_pdf)]
+#[test]
+fn pdf_page_size() {
+ RsvgConvert::new_with_input("tests/fixtures/dimensions-in.svg")
+ .arg("--format=pdf")
+ .arg("--page-width=210mm")
+ .arg("--page-height=297mm")
+ .assert()
+ .success()
+ .stdout(file::is_pdf().with_page_size(0, 210.0 / 25.4 * 72.0, 297.0 / 25.4 * 72.0));
+}
+
+#[cfg(system_deps_have_cairo_pdf)]
+#[test]
+fn multiple_input_files_create_multi_page_pdf_size_override() {
+ let one = Path::new("tests/fixtures/bug521-with-viewbox.svg");
+ let two = Path::new("tests/fixtures/sub-rect-no-unit.svg");
+ let three = Path::new("tests/fixtures/example.svg");
+ RsvgConvert::new()
+ .arg("--format=pdf")
+ .arg("--width=300pt")
+ .arg("--height=200pt")
+ .arg(one)
+ .arg(two)
+ .arg(three)
+ .assert()
+ .success()
+ .stdout(
+ file::is_pdf()
+ .with_page_count(3)
+ .and(file::is_pdf().with_page_size(0, 300.0, 200.0))
+ .and(file::is_pdf().with_page_size(1, 300.0, 200.0))
+ .and(file::is_pdf().with_page_size(2, 300.0, 200.0)),
+ );
+}
+
+#[cfg(system_deps_have_cairo_pdf)]
+#[test]
+fn missing_page_size_yields_error() {
+ RsvgConvert::new_with_input("tests/fixtures/dimensions-in.svg")
+ .arg("--format=pdf")
+ .arg("--page-width=210mm")
+ .assert()
+ .failure()
+ .stderr(contains("both").and(contains("options")));
+
+ RsvgConvert::new_with_input("tests/fixtures/dimensions-in.svg")
+ .arg("--format=pdf")
+ .arg("--page-height=297mm")
+ .assert()
+ .failure()
+ .stderr(contains("both").and(contains("options")));
+}
+
+#[test]
+fn does_not_clip_partial_coverage_pixels() {
+ RsvgConvert::new_with_input("tests/fixtures/bug677-partial-pixel.svg")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_size(2, 2));
+}
+
+#[test]
+fn background_color_option_with_valid_color() {
+ RsvgConvert::accepts_arg("--background-color=LimeGreen");
+}
+
+#[test]
+fn background_color_option_none() {
+ RsvgConvert::accepts_arg("--background-color=None");
+}
+
+#[test]
+fn background_color_short_option() {
+ RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
+ .arg("-b")
+ .arg("#aabbcc")
+ .assert()
+ .success();
+}
+
+#[test]
+fn background_color_option_invalid_color_yields_error() {
+ RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
+ .arg("--background-color=foobar")
+ .assert()
+ .failure()
+ .stderr(contains("Invalid").and(contains("color")));
+}
+
+#[test]
+fn background_color_is_rendered() {
+ RsvgConvert::new_with_input("tests/fixtures/gimp-wilber.svg")
+ .arg("--background-color=purple")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_contents("tests/fixtures/gimp-wilber-ref.png"));
+}
+
+#[test]
+fn stylesheet_option() {
+ RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
+ .arg("--stylesheet=tests/fixtures/empty.svg")
+ .assert()
+ .success();
+}
+
+#[test]
+fn stylesheet_short_option() {
+ RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
+ .arg("-s")
+ .arg("tests/fixtures/empty.svg")
+ .assert()
+ .success();
+}
+
+#[test]
+fn stylesheet_option_error() {
+ RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
+ .arg("--stylesheet=foobar")
+ .assert()
+ .failure()
+ .stderr(starts_with("Error reading stylesheet"));
+}
+
+#[test]
+fn export_id_option() {
+ RsvgConvert::new_with_input("tests/fixtures/geometry-element.svg")
+ .arg("--export-id=foo")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_size(40, 50));
+}
+
+#[test]
+fn export_id_with_zero_stroke_width() {
+ // https://gitlab.gnome.org/GNOME/librsvg/-/issues/601
+ //
+ // This tests a bug that manifested itself easily with the --export-id option, but it
+ // is not a bug with the option itself. An object with stroke_width=0 was causing
+ // an extra point at the origin to be put in the bounding box, so the final image
+ // spanned the origin to the actual visible bounds of the rendered object.
+ //
+ // We can probably test this more cleanly once we have a render tree.
+ RsvgConvert::new_with_input("tests/fixtures/bug601-zero-stroke-width.svg")
+ .arg("--export-id=foo")
+ .assert()
+ .success()
+ .stdout(
+ file::is_png()
+ .with_contents("tests/fixtures/bug601-zero-stroke-width-render-only-foo.png"),
+ );
+}
+
+#[test]
+fn export_id_short_option() {
+ RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
+ .arg("-i")
+ .arg("two")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_size(100, 200));
+}
+
+#[test]
+fn export_id_with_hash_prefix() {
+ RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
+ .arg("-i")
+ .arg("#two")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_size(100, 200));
+}
+
+#[test]
+fn export_id_option_error() {
+ RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
+ .arg("--export-id=foobar")
+ .assert()
+ .failure()
+ .stderr(starts_with("File stdin does not have an object with id \""));
+}
+
+#[test]
+fn unlimited_option() {
+ RsvgConvert::accepts_arg("--unlimited");
+}
+
+#[test]
+fn unlimited_short_option() {
+ RsvgConvert::accepts_arg("-u");
+}
+
+#[test]
+fn keep_aspect_ratio_option() {
+ let input = Path::new("tests/fixtures/dpi.svg");
+ RsvgConvert::new_with_input(input)
+ .arg("--width=500")
+ .arg("--height=1000")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_size(500, 1000));
+ RsvgConvert::new_with_input(input)
+ .arg("--width=500")
+ .arg("--height=1000")
+ .arg("--keep-aspect-ratio")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_size(250, 1000));
+}
+
+#[test]
+fn keep_aspect_ratio_short_option() {
+ let input = Path::new("tests/fixtures/dpi.svg");
+ RsvgConvert::new_with_input(input)
+ .arg("--width=1000")
+ .arg("--height=500")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_size(1000, 500));
+ RsvgConvert::new_with_input(input)
+ .arg("--width=1000")
+ .arg("--height=500")
+ .arg("-a")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_size(125, 500));
+}
+
+#[test]
+fn overflowing_size_is_detected() {
+ RsvgConvert::new_with_input("tests/fixtures/bug591-vbox-overflow.svg")
+ .assert()
+ .failure()
+ .stderr(starts_with(
+ "The resulting image would be larger than 32767 pixels",
+ ));
+}
+
+#[test]
+fn accept_language_given() {
+ RsvgConvert::new_with_input("tests/fixtures/accept-language.svg")
+ .arg("--accept-language=es-MX")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_contents("tests/fixtures/accept-language-es.png"));
+
+ RsvgConvert::new_with_input("tests/fixtures/accept-language.svg")
+ .arg("--accept-language=de")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_contents("tests/fixtures/accept-language-de.png"));
+}
+
+#[test]
+fn accept_language_fallback() {
+ RsvgConvert::new_with_input("tests/fixtures/accept-language.svg")
+ .arg("--accept-language=fr")
+ .assert()
+ .success()
+ .stdout(file::is_png().with_contents("tests/fixtures/accept-language-fallback.png"));
+}
+
+#[test]
+fn accept_language_invalid_tag() {
+ // underscores are not valid in BCP47 language tags
+ RsvgConvert::new_with_input("tests/fixtures/accept-language.svg")
+ .arg("--accept-language=foo_bar")
+ .assert()
+ .failure()
+ .stderr(contains("invalid language tag"));
+}
+
+#[test]
+fn keep_image_data_option() {
+ RsvgConvert::accepts_arg("--keep-image-data");
+}
+
+#[test]
+fn no_keep_image_data_option() {
+ RsvgConvert::accepts_arg("--no-keep-image-data");
+}
+
+fn is_version_output() -> AndPredicate<StartsWithPredicate, TrimPredicate<EndsWithPredicate>, str> {
+ starts_with("rsvg-convert version ")
+ .and(predicates::str::ends_with(env!("CARGO_PKG_VERSION")).trim())
+}
+
+#[test]
+fn version_option() {
+ RsvgConvert::option_yields_output("--version", is_version_output())
+}
+
+#[test]
+fn version_short_option() {
+ RsvgConvert::option_yields_output("-v", is_version_output())
+}
+
+fn is_usage_output() -> OrPredicate<ContainsPredicate, ContainsPredicate, str> {
+ contains("Usage:").or(contains("USAGE:"))
+}
+
+#[test]
+fn help_option() {
+ RsvgConvert::option_yields_output("--help", is_usage_output())
+}
+
+#[test]
+fn help_short_option() {
+ RsvgConvert::option_yields_output("-?", is_usage_output())
+}