1
1
//use crate::predicates::ends_with_pkg_version;
2
mod internal_predicates;
3
use internal_predicates::file;
4

            
5
use assert_cmd::assert::IntoOutputPredicate;
6
use assert_cmd::Command;
7
#[cfg(system_deps_have_cairo_pdf)]
8
use chrono::{TimeZone, Utc};
9
use predicates::boolean::*;
10
use predicates::prelude::*;
11
use predicates::str::*;
12
use rsvg::{Length, LengthUnit};
13
use std::path::Path;
14
use tempfile::Builder;
15
use url::Url;
16

            
17
// What should be tested here?
18
// The goal is to test the code in rsvg-convert, not the entire library.
19
//
20
//  - command-line options that affect size (width, height, zoom, resolution) ✔
21
//  - pixel dimensions of the output (should be sufficient to do that for PNG) ✔
22
//  - limit on output size (32767 pixels) ✔
23
//  - output formats (PNG, PDF, PS, EPS, SVG) ✔
24
//  - multi-page output (for PDF) ✔
25
//  - output file option ✔
26
//  - SOURCE_DATA_EPOCH environment variable for PDF output ✔
27
//  - background color option ✔
28
//  - optional CSS stylesheet ✔
29
//  - error handling for missing SVG dimensions ✔
30
//  - error handling for export lookup ID ✔
31
//  - error handling for invalid input ✔
32

            
33
struct RsvgConvert {}
34

            
35
impl RsvgConvert {
36
109
    fn new() -> Command {
37
109
        Command::cargo_bin("rsvg-convert").unwrap()
38
109
    }
39

            
40
88
    fn new_with_input<P>(file: P) -> Command
41
    where
42
        P: AsRef<Path>,
43
    {
44
88
        let mut command = RsvgConvert::new();
45
88
        match command.pipe_stdin(&file) {
46
88
            Ok(_) => command,
47
            Err(e) => panic!("Error opening file '{}': {}", file.as_ref().display(), e),
48
        }
49
88
    }
50

            
51
6
    fn accepts_arg(option: &str) {
52
6
        RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
53
            .arg(option)
54
            .assert()
55
6
            .success();
56
6
    }
57

            
58
4
    fn option_yields_output<I, P>(option: &str, output_pred: I)
59
    where
60
        I: IntoOutputPredicate<P>,
61
        P: Predicate<[u8]>,
62
    {
63
4
        RsvgConvert::new()
64
            .arg(option)
65
            .assert()
66
            .success()
67
4
            .stdout(output_pred);
68
4
    }
69
}
70

            
71
#[test]
72
2
fn converts_svg_from_stdin_to_png() {
73
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
74
        .assert()
75
1
        .success()
76
2
        .stdout(file::is_png());
77
2
}
78

            
79
#[test]
80
2
fn converts_svg_from_stdin_to_png_using_stdin_argument() {
81
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
82
        .arg("-")
83
        .assert()
84
1
        .success()
85
2
        .stdout(file::is_png());
86
2
}
87

            
88
#[test]
89
2
fn argument_is_input_filename() {
90
1
    let input = Path::new("tests/fixtures/bug521-with-viewbox.svg");
91
2
    RsvgConvert::new()
92
        .arg(input)
93
        .assert()
94
1
        .success()
95
2
        .stdout(file::is_png());
96
2
}
97

            
98
#[test]
99
2
fn argument_is_url() {
100
1
    let path = Path::new("tests/fixtures/bug521-with-viewbox.svg")
101
        .canonicalize()
102
        .unwrap();
103
1
    let url = Url::from_file_path(path).unwrap();
104
1
    let stringified = url.as_str();
105
1
    assert!(stringified.starts_with("file://"));
106

            
107
2
    RsvgConvert::new()
108
        .arg(stringified)
109
        .assert()
110
1
        .success()
111
2
        .stdout(file::is_png());
112
2
}
113

            
114
#[test]
115
2
fn output_format_png() {
116
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
117
        .arg("--format=png")
118
        .assert()
119
1
        .success()
120
2
        .stdout(file::is_png());
121
2
}
122

            
123
#[cfg(system_deps_have_cairo_ps)]
124
#[test]
125
2
fn output_format_ps() {
126
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
127
        .arg("--format=ps")
128
        .assert()
129
1
        .success()
130
2
        .stdout(file::is_ps());
131
2
}
132

            
133
#[cfg(system_deps_have_cairo_ps)]
134
#[test]
135
2
fn output_format_eps() {
136
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
137
        .arg("--format=eps")
138
        .assert()
139
1
        .success()
140
2
        .stdout(file::is_eps());
141
2
}
142

            
143
#[cfg(system_deps_have_cairo_pdf)]
144
#[test]
145
2
fn output_format_pdf() {
146
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
147
        .arg("--format=pdf")
148
        .assert()
149
1
        .success()
150
2
        .stdout(file::is_pdf());
151
2
}
152

            
153
#[cfg(system_deps_have_cairo_pdf)]
154
#[test]
155
2
fn output_format_pdf_1_7() {
156
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
157
        .arg("--format=pdf1.7")
158
        .assert()
159
1
        .success()
160
2
        .stdout(file::is_pdf().with_version("1.7"));
161
2
}
162

            
163
#[cfg(system_deps_have_cairo_pdf)]
164
#[test]
165
2
fn output_format_pdf_1_6() {
166
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
167
        .arg("--format=pdf1.6")
168
        .assert()
169
1
        .success()
170
2
        .stdout(file::is_pdf().with_version("1.6"));
171
2
}
172

            
173
#[cfg(system_deps_have_cairo_pdf)]
174
#[test]
175
2
fn output_format_pdf_1_5() {
176
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
177
        .arg("--format=pdf1.5")
178
        .assert()
179
1
        .success()
180
2
        .stdout(file::is_pdf().with_version("1.5"));
181
2
}
182

            
183
#[cfg(system_deps_have_cairo_pdf)]
184
#[test]
185
2
fn output_format_pdf_1_4() {
186
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
187
        .arg("--format=pdf1.4")
188
        .assert()
189
1
        .success()
190
2
        .stdout(file::is_pdf().with_version("1.4"));
191
2
}
192

            
193
#[cfg(system_deps_have_cairo_svg)]
194
#[test]
195
2
fn output_format_svg_short_option() {
196
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
197
        .arg("-f")
198
        .arg("svg")
199
        .assert()
200
1
        .success()
201
2
        .stdout(file::is_svg());
202
2
}
203

            
204
#[cfg(system_deps_have_cairo_svg)]
205
#[test]
206
2
fn user_specified_width_and_height() {
207
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
208
        .arg("--format")
209
        .arg("svg")
210
        .arg("--width")
211
        .arg("42cm")
212
        .arg("--height")
213
        .arg("43cm")
214
        .assert()
215
1
        .success()
216
2
        .stdout(file::is_svg().with_size(
217
1
            Length::new(42.0, LengthUnit::Cm),
218
1
            Length::new(43.0, LengthUnit::Cm),
219
1
        ));
220
2
}
221

            
222
#[cfg(system_deps_have_cairo_svg)]
223
#[test]
224
2
fn user_specified_width_and_height_px_output() {
225
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
226
        .arg("--format")
227
        .arg("svg")
228
        .arg("--width")
229
        .arg("1920")
230
        .arg("--height")
231
        .arg("508mm")
232
        .assert()
233
1
        .success()
234
2
        .stdout(file::is_svg().with_size(
235
1
            Length::new(1920.0, LengthUnit::Px),
236
1
            Length::new(1920.0, LengthUnit::Px),
237
1
        ));
238
2
}
239

            
240
#[cfg(system_deps_have_cairo_svg)]
241
#[test]
242
2
fn user_specified_width_and_height_a4() {
243
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
244
        .arg("--format")
245
        .arg("svg")
246
        .arg("--page-width")
247
        .arg("210mm")
248
        .arg("--page-height")
249
        .arg("297mm")
250
        .arg("--left")
251
        .arg("1cm")
252
        .arg("--top")
253
        .arg("1cm")
254
        .arg("--width")
255
        .arg("190mm")
256
        .arg("--height")
257
        .arg("277mm")
258
        .assert()
259
1
        .success()
260
2
        .stdout(file::is_svg().with_size(
261
1
            Length::new(210.0, LengthUnit::Mm),
262
1
            Length::new(297.0, LengthUnit::Mm),
263
1
        ));
264
2
}
265

            
266
#[test]
267
2
fn output_file_option() {
268
1
    let output = {
269
1
        let tempfile = Builder::new().suffix(".png").tempfile().unwrap();
270
1
        tempfile.path().to_path_buf()
271
1
    };
272
1
    assert!(predicates::path::is_file().not().eval(&output));
273

            
274
3
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
275
1
        .arg(format!("--output={}", output.display()))
276
        .assert()
277
1
        .success()
278
2
        .stdout(is_empty());
279

            
280
1
    assert!(predicates::path::is_file().eval(&output));
281
1
    std::fs::remove_file(&output).unwrap();
282
2
}
283

            
284
#[test]
285
2
fn output_file_short_option() {
286
1
    let output = {
287
1
        let tempfile = Builder::new().suffix(".png").tempfile().unwrap();
288
1
        tempfile.path().to_path_buf()
289
1
    };
290
1
    assert!(predicates::path::is_file().not().eval(&output));
291

            
292
3
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
293
        .arg("-o")
294
1
        .arg(format!("{}", output.display()))
295
        .assert()
296
1
        .success()
297
2
        .stdout(is_empty());
298

            
299
1
    assert!(predicates::path::is_file().eval(&output));
300
1
    std::fs::remove_file(&output).unwrap();
301
2
}
302

            
303
#[test]
304
2
fn overwrites_existing_output_file() {
305
1
    let output = {
306
1
        let tempfile = Builder::new().suffix(".png").tempfile().unwrap();
307
1
        tempfile.path().to_path_buf()
308
1
    };
309
1
    assert!(predicates::path::is_file().not().eval(&output));
310

            
311
3
    for _ in 0..2 {
312
6
        RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
313
2
            .arg(format!("--output={}", output.display()))
314
            .assert()
315
2
            .success()
316
4
            .stdout(is_empty());
317

            
318
2
        assert!(predicates::path::is_file().eval(&output));
319
    }
320

            
321
1
    std::fs::remove_file(&output).unwrap();
322
2
}
323

            
324
#[test]
325
2
fn empty_input_yields_error() {
326
1
    let starts_with = starts_with("Error reading SVG");
327
1
    let ends_with = ends_with("Input file is too short").trim();
328
2
    RsvgConvert::new()
329
        .assert()
330
1
        .failure()
331
2
        .stderr(starts_with.and(ends_with));
332
2
}
333

            
334
#[test]
335
2
fn empty_svg_yields_error() {
336
1
    RsvgConvert::new_with_input("tests/fixtures/empty.svg")
337
        .assert()
338
        .failure()
339
1
        .stderr("The SVG stdin has no dimensions\n");
340
2
}
341

            
342
#[test]
343
2
fn multiple_input_files_not_allowed_for_png_output() {
344
1
    let one = Path::new("tests/fixtures/bug521-with-viewbox.svg");
345
1
    let two = Path::new("tests/fixtures/sub-rect-no-unit.svg");
346
2
    RsvgConvert::new()
347
        .arg(one)
348
        .arg(two)
349
        .assert()
350
1
        .failure()
351
1
        .stderr(contains(
352
            "Multiple SVG files are only allowed for PDF and (E)PS output",
353
1
        ));
354
2
}
355

            
356
#[cfg(system_deps_have_cairo_ps)]
357
#[test]
358
2
fn multiple_input_files_accepted_for_eps_output() {
359
1
    let one = Path::new("tests/fixtures/bug521-with-viewbox.svg");
360
1
    let two = Path::new("tests/fixtures/sub-rect-no-unit.svg");
361
2
    RsvgConvert::new()
362
        .arg("--format=eps")
363
        .arg(one)
364
        .arg(two)
365
        .assert()
366
1
        .success()
367
2
        .stdout(file::is_eps());
368
2
}
369

            
370
#[cfg(system_deps_have_cairo_ps)]
371
#[test]
372
2
fn multiple_input_files_accepted_for_ps_output() {
373
1
    let one = Path::new("tests/fixtures/bug521-with-viewbox.svg");
374
1
    let two = Path::new("tests/fixtures/sub-rect-no-unit.svg");
375
2
    RsvgConvert::new()
376
        .arg("--format=ps")
377
        .arg(one)
378
        .arg(two)
379
        .assert()
380
1
        .success()
381
2
        .stdout(file::is_ps());
382
2
}
383

            
384
#[cfg(system_deps_have_cairo_pdf)]
385
#[test]
386
2
fn multiple_input_files_create_multi_page_pdf_output() {
387
1
    let one = Path::new("tests/fixtures/bug521-with-viewbox.svg");
388
1
    let two = Path::new("tests/fixtures/sub-rect-no-unit.svg");
389
1
    let three = Path::new("tests/fixtures/example.svg");
390
2
    RsvgConvert::new()
391
        .arg("--format=pdf")
392
        .arg(one)
393
        .arg(two)
394
        .arg(three)
395
        .assert()
396
1
        .success()
397
        .stdout(
398
4
            file::is_pdf()
399
1
                .with_page_count(3)
400
2
                .and(file::is_pdf().with_page_size(0, 150.0, 75.0))
401
2
                .and(file::is_pdf().with_page_size(1, 123.0, 123.0))
402
2
                .and(file::is_pdf().with_page_size(2, 75.0, 300.0)),
403
1
        );
404
2
}
405

            
406
#[cfg(system_deps_have_cairo_pdf)]
407
#[test]
408
2
fn multiple_input_files_create_multi_page_pdf_output_fixed_size() {
409
1
    let one = Path::new("tests/fixtures/bug521-with-viewbox.svg");
410
1
    let two = Path::new("tests/fixtures/sub-rect-no-unit.svg");
411
1
    let three = Path::new("tests/fixtures/example.svg");
412
2
    RsvgConvert::new()
413
        .arg("--format=pdf")
414
        .arg("--page-width=8.5in")
415
        .arg("--page-height=11in")
416
        .arg("--width=7.5in")
417
        .arg("--height=10in")
418
        .arg("--left=0.5in")
419
        .arg("--top=0.5in")
420
        .arg("--keep-aspect-ratio")
421
        .arg(one)
422
        .arg(two)
423
        .arg(three)
424
        .assert()
425
1
        .success()
426
        .stdout(
427
4
            file::is_pdf()
428
1
                .with_page_count(3)
429
                // https://www.wolframalpha.com/input/?i=convert+11+inches+to+desktop+publishing+points
430
2
                .and(file::is_pdf().with_page_size(0, 612.0, 792.0))
431
2
                .and(file::is_pdf().with_page_size(1, 612.0, 792.0))
432
2
                .and(file::is_pdf().with_page_size(2, 612.0, 792.0)),
433
1
        );
434
2
}
435

            
436
#[cfg(system_deps_have_cairo_pdf)]
437
#[test]
438
2
fn pdf_has_link() {
439
1
    let input = Path::new("tests/fixtures/a-link.svg");
440
2
    RsvgConvert::new()
441
        .arg("--format=pdf")
442
        .arg(input)
443
        .assert()
444
1
        .success()
445
2
        .stdout(file::is_pdf().with_link("https://example.com"));
446
2
}
447

            
448
#[cfg(system_deps_have_cairo_pdf)]
449
#[test]
450
2
fn pdf_has_link_inside_text() {
451
1
    let input = Path::new("tests/fixtures/text-a-link.svg");
452
2
    RsvgConvert::new()
453
        .arg("--format=pdf")
454
        .arg(input)
455
        .assert()
456
1
        .success()
457
        .stdout(
458
2
            file::is_pdf()
459
1
                .with_link("https://example.com")
460
2
                .and(file::is_pdf().with_link("https://another.example.com")),
461
1
        );
462
2
}
463

            
464
#[cfg(system_deps_have_cairo_pdf)]
465
#[test]
466
2
fn pdf_has_text() {
467
1
    let input = Path::new("tests/fixtures/hello-world.svg");
468
2
    RsvgConvert::new()
469
        .arg("--format=pdf")
470
        .arg(input)
471
        .assert()
472
1
        .success()
473
        .stdout(
474
2
            file::is_pdf()
475
1
                .with_text("Hello world!")
476
2
                .and(file::is_pdf().with_text("Hello again!")),
477
1
        );
478
2
}
479

            
480
#[cfg(system_deps_have_cairo_pdf)]
481
#[test]
482
2
fn env_source_data_epoch_controls_pdf_creation_date() {
483
1
    let input = Path::new("tests/fixtures/bug521-with-viewbox.svg");
484
2
    let date = 1581411039; // seconds since epoch
485
3
    RsvgConvert::new()
486
1
        .env("SOURCE_DATE_EPOCH", format!("{}", date))
487
        .arg("--format=pdf")
488
        .arg(input)
489
        .assert()
490
1
        .success()
491
2
        .stdout(file::is_pdf().with_creation_date(Utc.timestamp_opt(date, 0).unwrap()));
492
2
}
493

            
494
#[cfg(system_deps_have_cairo_pdf)]
495
#[test]
496
2
fn env_source_data_epoch_no_digits() {
497
    // intentionally not testing for the full error string here
498
1
    let input = Path::new("tests/fixtures/bug521-with-viewbox.svg");
499
2
    RsvgConvert::new()
500
        .env("SOURCE_DATE_EPOCH", "foobar")
501
        .arg("--format=pdf")
502
        .arg(input)
503
        .assert()
504
1
        .failure()
505
2
        .stderr(starts_with("Environment variable $SOURCE_DATE_EPOCH"));
506
2
}
507

            
508
#[cfg(system_deps_have_cairo_pdf)]
509
#[test]
510
2
fn env_source_data_epoch_trailing_garbage() {
511
    // intentionally not testing for the full error string here
512
1
    let input = Path::new("tests/fixtures/bug521-with-viewbox.svg");
513
2
    RsvgConvert::new()
514
        .arg("--format=pdf")
515
        .env("SOURCE_DATE_EPOCH", "1234556+")
516
        .arg(input)
517
        .assert()
518
1
        .failure()
519
2
        .stderr(starts_with("Environment variable $SOURCE_DATE_EPOCH"));
520
2
}
521

            
522
#[cfg(system_deps_have_cairo_pdf)]
523
#[test]
524
2
fn env_source_data_epoch_empty() {
525
    // intentionally not testing for the full error string here
526
1
    let input = Path::new("tests/fixtures/bug521-with-viewbox.svg");
527
2
    RsvgConvert::new()
528
        .arg("--format=pdf")
529
        .env("SOURCE_DATE_EPOCH", "")
530
        .arg(input)
531
        .assert()
532
1
        .failure()
533
2
        .stderr(starts_with("Environment variable $SOURCE_DATE_EPOCH"));
534
2
}
535

            
536
#[test]
537
2
fn width_option() {
538
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
539
        .arg("--width=300")
540
        .assert()
541
1
        .success()
542
1
        .stdout(file::is_png().with_size(300, 150));
543
2
}
544

            
545
#[test]
546
2
fn height_option() {
547
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
548
        .arg("--height=200")
549
        .assert()
550
1
        .success()
551
1
        .stdout(file::is_png().with_size(400, 200));
552
2
}
553

            
554
#[test]
555
2
fn width_and_height_options() {
556
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
557
        .arg("--width=300")
558
        .arg("--height=200")
559
        .assert()
560
1
        .success()
561
1
        .stdout(file::is_png().with_size(300, 200));
562
2
}
563

            
564
#[test]
565
2
fn unsupported_unit_in_width_and_height() {
566
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
567
        .arg("--height=200ex")
568
        .assert()
569
1
        .failure()
570
2
        .stderr(contains("supported units"));
571
2
}
572

            
573
#[test]
574
2
fn invalid_length() {
575
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
576
        .arg("--page-width=foo")
577
        .assert()
578
1
        .failure()
579
2
        .stderr(contains("can not be parsed as a length"));
580
2
}
581

            
582
#[test]
583
2
fn zoom_factor() {
584
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
585
        .arg("--zoom=0.8")
586
        .assert()
587
1
        .success()
588
1
        .stdout(file::is_png().with_size(160, 80));
589
2
}
590

            
591
#[test]
592
2
fn zoom_factor_and_larger_size() {
593
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
594
        .arg("--width=400")
595
        .arg("--height=200")
596
        .arg("--zoom=1.5")
597
        .assert()
598
1
        .success()
599
1
        .stdout(file::is_png().with_size(300, 150));
600
2
}
601

            
602
#[test]
603
2
fn zoom_factor_and_smaller_size() {
604
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
605
        .arg("--width=400")
606
        .arg("--height=200")
607
        .arg("--zoom=3.5")
608
        .assert()
609
1
        .success()
610
1
        .stdout(file::is_png().with_size(400, 200));
611
2
}
612

            
613
#[test]
614
2
fn x_zoom_option() {
615
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
616
        .arg("--x-zoom=2")
617
        .assert()
618
1
        .success()
619
1
        .stdout(file::is_png().with_size(400, 100));
620
2
}
621

            
622
#[test]
623
2
fn x_short_option() {
624
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
625
        .arg("-x")
626
        .arg("2.0")
627
        .assert()
628
1
        .success()
629
1
        .stdout(file::is_png().with_size(400, 100));
630
2
}
631

            
632
#[test]
633
2
fn y_zoom_option() {
634
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
635
        .arg("--y-zoom=2.0")
636
        .assert()
637
1
        .success()
638
1
        .stdout(file::is_png().with_size(200, 200));
639
2
}
640

            
641
#[test]
642
2
fn y_short_option() {
643
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
644
        .arg("-y")
645
        .arg("2")
646
        .assert()
647
1
        .success()
648
1
        .stdout(file::is_png().with_size(200, 200));
649
2
}
650

            
651
#[test]
652
2
fn huge_zoom_factor_yields_error() {
653
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
654
        .arg("--zoom=1000")
655
        .assert()
656
1
        .failure()
657
1
        .stderr(starts_with(
658
            "The resulting image would be larger than 32767 pixels",
659
1
        ));
660
2
}
661

            
662
#[test]
663
2
fn negative_zoom_factor_yields_error() {
664
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
665
        .arg("--zoom=-2")
666
        .assert()
667
1
        .failure()
668
2
        .stderr(contains("Invalid zoom"));
669
2
}
670

            
671
#[test]
672
2
fn invalid_zoom_factor_yields_error() {
673
2
    RsvgConvert::new_with_input("tests/fixtures/bug521-with-viewbox.svg")
674
        .arg("--zoom=foo")
675
        .assert()
676
1
        .failure()
677
2
        .stderr(contains("invalid value"));
678
2
}
679

            
680
#[test]
681
2
fn default_resolution_is_96dpi() {
682
2
    RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
683
        .assert()
684
1
        .success()
685
1
        .stdout(file::is_png().with_size(96, 384));
686
2
}
687

            
688
#[test]
689
2
fn x_resolution() {
690
2
    RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
691
        .arg("--dpi-x=300")
692
        .assert()
693
1
        .success()
694
1
        .stdout(file::is_png().with_size(300, 384));
695
2
}
696

            
697
#[test]
698
2
fn x_resolution_short_option() {
699
2
    RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
700
        .arg("-d")
701
        .arg("45")
702
        .assert()
703
1
        .success()
704
1
        .stdout(file::is_png().with_size(45, 384));
705
2
}
706

            
707
#[test]
708
2
fn y_resolution() {
709
2
    RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
710
        .arg("--dpi-y=300")
711
        .assert()
712
1
        .success()
713
1
        .stdout(file::is_png().with_size(96, 1200));
714
2
}
715

            
716
#[test]
717
2
fn y_resolution_short_option() {
718
2
    RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
719
        .arg("-p")
720
        .arg("45")
721
        .assert()
722
1
        .success()
723
1
        .stdout(file::is_png().with_size(96, 180));
724
2
}
725

            
726
#[test]
727
2
fn x_and_y_resolution() {
728
2
    RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
729
        .arg("--dpi-x=300")
730
        .arg("--dpi-y=150")
731
        .assert()
732
1
        .success()
733
1
        .stdout(file::is_png().with_size(300, 600));
734
2
}
735

            
736
#[test]
737
2
fn zero_resolution_is_invalid() {
738
2
    RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
739
        .arg("--dpi-x=0")
740
        .arg("--dpi-y=0")
741
        .assert()
742
1
        .failure()
743
2
        .stderr(contains("Invalid resolution"));
744
2
}
745

            
746
#[test]
747
2
fn negative_resolution_is_invalid() {
748
2
    RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
749
        .arg("--dpi-x=-100")
750
        .arg("--dpi-y=-100")
751
        .assert()
752
1
        .failure()
753
2
        .stderr(contains("Invalid resolution"));
754
2
}
755

            
756
#[test]
757
2
fn zero_offset_png() {
758
2
    RsvgConvert::new_with_input("tests/fixtures/dimensions-in.svg")
759
        .arg("--page-width=640")
760
        .arg("--page-height=480")
761
        .arg("--width=200")
762
        .arg("--height=100")
763
        .assert()
764
1
        .success()
765
2
        .stdout(file::is_png().with_contents("tests/fixtures/zero-offset-png.png"));
766
2
}
767

            
768
#[test]
769
2
fn offset_png() {
770
2
    RsvgConvert::new_with_input("tests/fixtures/dimensions-in.svg")
771
        .arg("--page-width=640")
772
        .arg("--page-height=480")
773
        .arg("--width=200")
774
        .arg("--height=100")
775
        .arg("--left=100")
776
        .arg("--top=50")
777
        .assert()
778
1
        .success()
779
2
        .stdout(file::is_png().with_contents("tests/fixtures/offset-png.png"));
780
2
}
781

            
782
#[cfg(system_deps_have_cairo_pdf)]
783
#[test]
784
2
fn unscaled_pdf_size() {
785
2
    RsvgConvert::new_with_input("tests/fixtures/dimensions-in.svg")
786
        .arg("--format=pdf")
787
        .assert()
788
1
        .success()
789
2
        .stdout(file::is_pdf().with_page_size(0, 72.0, 72.0));
790
2
}
791

            
792
#[cfg(system_deps_have_cairo_pdf)]
793
#[test]
794
2
fn pdf_size_width_height() {
795
2
    RsvgConvert::new_with_input("tests/fixtures/dimensions-in.svg")
796
        .arg("--format=pdf")
797
        .arg("--width=2in")
798
        .arg("--height=3in")
799
        .assert()
800
1
        .success()
801
2
        .stdout(file::is_pdf().with_page_size(0, 144.0, 216.0));
802
2
}
803

            
804
#[cfg(system_deps_have_cairo_pdf)]
805
#[test]
806
2
fn pdf_size_width_height_proportional() {
807
2
    RsvgConvert::new_with_input("tests/fixtures/dimensions-in.svg")
808
        .arg("--format=pdf")
809
        .arg("--width=2in")
810
        .arg("--height=3in")
811
        .arg("--keep-aspect-ratio")
812
        .assert()
813
1
        .success()
814
2
        .stdout(file::is_pdf().with_page_size(0, 144.0, 144.0));
815
2
}
816

            
817
#[cfg(system_deps_have_cairo_pdf)]
818
#[test]
819
2
fn pdf_page_size() {
820
2
    RsvgConvert::new_with_input("tests/fixtures/dimensions-in.svg")
821
        .arg("--format=pdf")
822
        .arg("--page-width=210mm")
823
        .arg("--page-height=297mm")
824
        .assert()
825
1
        .success()
826
2
        .stdout(file::is_pdf().with_page_size(0, 210.0 / 25.4 * 72.0, 297.0 / 25.4 * 72.0));
827
2
}
828

            
829
#[cfg(system_deps_have_cairo_pdf)]
830
#[test]
831
2
fn multiple_input_files_create_multi_page_pdf_size_override() {
832
1
    let one = Path::new("tests/fixtures/bug521-with-viewbox.svg");
833
1
    let two = Path::new("tests/fixtures/sub-rect-no-unit.svg");
834
1
    let three = Path::new("tests/fixtures/example.svg");
835
2
    RsvgConvert::new()
836
        .arg("--format=pdf")
837
        .arg("--width=300pt")
838
        .arg("--height=200pt")
839
        .arg(one)
840
        .arg(two)
841
        .arg(three)
842
        .assert()
843
1
        .success()
844
        .stdout(
845
4
            file::is_pdf()
846
1
                .with_page_count(3)
847
2
                .and(file::is_pdf().with_page_size(0, 300.0, 200.0))
848
2
                .and(file::is_pdf().with_page_size(1, 300.0, 200.0))
849
2
                .and(file::is_pdf().with_page_size(2, 300.0, 200.0)),
850
1
        );
851
2
}
852

            
853
#[cfg(system_deps_have_cairo_pdf)]
854
#[test]
855
2
fn missing_page_size_yields_error() {
856
2
    RsvgConvert::new_with_input("tests/fixtures/dimensions-in.svg")
857
        .arg("--format=pdf")
858
        .arg("--page-width=210mm")
859
        .assert()
860
1
        .failure()
861
1
        .stderr(contains("both").and(contains("options")));
862

            
863
2
    RsvgConvert::new_with_input("tests/fixtures/dimensions-in.svg")
864
        .arg("--format=pdf")
865
        .arg("--page-height=297mm")
866
        .assert()
867
1
        .failure()
868
1
        .stderr(contains("both").and(contains("options")));
869
2
}
870

            
871
#[test]
872
2
fn does_not_clip_partial_coverage_pixels() {
873
2
    RsvgConvert::new_with_input("tests/fixtures/bug677-partial-pixel.svg")
874
        .assert()
875
1
        .success()
876
1
        .stdout(file::is_png().with_size(2, 2));
877
2
}
878

            
879
#[test]
880
2
fn background_color_option_with_valid_color() {
881
1
    RsvgConvert::accepts_arg("--background-color=LimeGreen");
882
2
}
883

            
884
#[test]
885
2
fn background_color_option_none() {
886
1
    RsvgConvert::accepts_arg("--background-color=None");
887
2
}
888

            
889
#[test]
890
2
fn background_color_short_option() {
891
1
    RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
892
        .arg("-b")
893
        .arg("#aabbcc")
894
        .assert()
895
1
        .success();
896
2
}
897

            
898
#[test]
899
2
fn background_color_option_invalid_color_yields_error() {
900
2
    RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
901
        .arg("--background-color=foobar")
902
        .assert()
903
1
        .failure()
904
1
        .stderr(contains("Invalid").and(contains("color")));
905
2
}
906

            
907
#[test]
908
2
fn background_color_is_rendered() {
909
2
    RsvgConvert::new_with_input("tests/fixtures/gimp-wilber.svg")
910
        .arg("--background-color=purple")
911
        .assert()
912
1
        .success()
913
2
        .stdout(file::is_png().with_contents("tests/fixtures/gimp-wilber-ref.png"));
914
2
}
915

            
916
#[test]
917
2
fn background_color_rgb() {
918
2
    RsvgConvert::new_with_input("tests/fixtures/empty-10x10.svg")
919
        .arg("--width=10")
920
        .arg("--height=10")
921
        .arg("--background-color=rgb(0, 255, 0)")
922
        .assert()
923
1
        .success()
924
2
        .stdout(file::is_png().with_contents("tests/fixtures/lime-ref.png"));
925
2
}
926

            
927
#[test]
928
2
fn background_color_rgba() {
929
2
    RsvgConvert::new_with_input("tests/fixtures/empty-10x10.svg")
930
        .arg("--width=10")
931
        .arg("--height=10")
932
        .arg("--background-color=rgba(0, 255, 0, 0.5)")
933
        .assert()
934
1
        .success()
935
2
        .stdout(file::is_png().with_contents("tests/fixtures/lime-transparent-ref.png"));
936
2
}
937

            
938
#[test]
939
2
fn background_color_hsl() {
940
2
    RsvgConvert::new_with_input("tests/fixtures/empty-10x10.svg")
941
        .arg("--width=10")
942
        .arg("--height=10")
943
        .arg("--background-color=hsl(120, 100%, 50%)")
944
        .assert()
945
1
        .success()
946
2
        .stdout(file::is_png().with_contents("tests/fixtures/lime-ref.png"));
947
2
}
948

            
949
#[test]
950
2
fn background_color_hsla() {
951
2
    RsvgConvert::new_with_input("tests/fixtures/empty-10x10.svg")
952
        .arg("--width=10")
953
        .arg("--height=10")
954
        .arg("--background-color=hsla(120, 100%, 50%, 0.5)")
955
        .assert()
956
1
        .success()
957
2
        .stdout(file::is_png().with_contents("tests/fixtures/lime-transparent-ref.png"));
958
2
}
959

            
960
#[test]
961
2
fn background_color_hwb() {
962
2
    RsvgConvert::new_with_input("tests/fixtures/empty-10x10.svg")
963
        .arg("--width=10")
964
        .arg("--height=10")
965
        .arg("--background-color=hwb(120 0% 0%)")
966
        .assert()
967
1
        .success()
968
2
        .stdout(file::is_png().with_contents("tests/fixtures/lime-ref.png"));
969
2
}
970

            
971
#[test]
972
2
fn background_color_hwba() {
973
2
    RsvgConvert::new_with_input("tests/fixtures/empty-10x10.svg")
974
        .arg("--width=10")
975
        .arg("--height=10")
976
        .arg("--background-color=hwb(120 0% 0% / 0.5)")
977
        .assert()
978
1
        .success()
979
2
        .stdout(file::is_png().with_contents("tests/fixtures/lime-transparent-ref.png"));
980
2
}
981

            
982
5
fn test_unsupported_background_color(color: &str) {
983
5
    let color_arg = format!("--background-color={color}");
984
10
    RsvgConvert::new_with_input("tests/fixtures/empty-10x10.svg")
985
        .arg("--width=10")
986
        .arg("--height=10")
987
        .arg(&color_arg)
988
        .assert()
989
5
        .failure()
990
5
        .stderr(contains("Invalid value").and(contains("unsupported color syntax")));
991
5
}
992

            
993
#[test]
994
2
fn unsupported_background_color() {
995
1
    let colors = [
996
        "lab(62.2345% -34.9638 47.7721)",
997
        "lch(62.2345% 59.2 126.2)",
998
        "oklab(66.016% -0.1084 0.1114)",
999
        "oklch(0.66016 0.15546 134.231)",
        "color(display-p3 -0.6112 1.0079 -0.2192)",
    ];
6
    for c in &colors {
5
        test_unsupported_background_color(c);
    }
2
}
#[test]
2
fn stylesheet_option() {
1
    RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
        .arg("--stylesheet=tests/fixtures/empty.css")
        .assert()
1
        .success();
2
}
#[test]
2
fn stylesheet_short_option() {
1
    RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
        .arg("-s")
        .arg("tests/fixtures/empty.css")
        .assert()
1
        .success();
2
}
#[test]
2
fn stylesheet_option_error() {
2
    RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
        .arg("--stylesheet=foobar")
        .assert()
1
        .failure()
2
        .stderr(starts_with("Error reading stylesheet"));
2
}
#[test]
2
fn export_id_option() {
2
    RsvgConvert::new_with_input("tests/fixtures/geometry-element.svg")
        .arg("--export-id=foo")
        .assert()
1
        .success()
1
        .stdout(file::is_png().with_size(40, 50));
2
}
#[test]
2
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.
2
    RsvgConvert::new_with_input("tests/fixtures/bug601-zero-stroke-width.svg")
        .arg("--export-id=foo")
        .assert()
1
        .success()
        .stdout(
1
            file::is_png()
                .with_contents("tests/fixtures/bug601-zero-stroke-width-render-only-foo.png"),
1
        );
2
}
#[test]
2
fn export_id_short_option() {
2
    RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
        .arg("-i")
        .arg("two")
        .assert()
1
        .success()
1
        .stdout(file::is_png().with_size(100, 200));
2
}
#[test]
2
fn export_id_with_hash_prefix() {
2
    RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
        .arg("-i")
        .arg("#two")
        .assert()
1
        .success()
1
        .stdout(file::is_png().with_size(100, 200));
2
}
#[test]
2
fn export_id_option_error() {
2
    RsvgConvert::new_with_input("tests/fixtures/dpi.svg")
        .arg("--export-id=foobar")
        .assert()
1
        .failure()
2
        .stderr(starts_with("File stdin does not have an object with id \""));
2
}
#[test]
2
fn unlimited_option() {
1
    RsvgConvert::accepts_arg("--unlimited");
2
}
#[test]
2
fn unlimited_short_option() {
1
    RsvgConvert::accepts_arg("-u");
2
}
#[test]
2
fn keep_aspect_ratio_option() {
1
    let input = Path::new("tests/fixtures/dpi.svg");
2
    RsvgConvert::new_with_input(input)
        .arg("--width=500")
        .arg("--height=1000")
        .assert()
1
        .success()
1
        .stdout(file::is_png().with_size(500, 1000));
2
    RsvgConvert::new_with_input(input)
        .arg("--width=500")
        .arg("--height=1000")
        .arg("--keep-aspect-ratio")
        .assert()
1
        .success()
1
        .stdout(file::is_png().with_size(250, 1000));
2
}
#[test]
2
fn keep_aspect_ratio_short_option() {
1
    let input = Path::new("tests/fixtures/dpi.svg");
2
    RsvgConvert::new_with_input(input)
        .arg("--width=1000")
        .arg("--height=500")
        .assert()
1
        .success()
1
        .stdout(file::is_png().with_size(1000, 500));
2
    RsvgConvert::new_with_input(input)
        .arg("--width=1000")
        .arg("--height=500")
        .arg("-a")
        .assert()
1
        .success()
1
        .stdout(file::is_png().with_size(125, 500));
2
}
#[test]
2
fn overflowing_size_is_detected() {
2
    RsvgConvert::new_with_input("tests/fixtures/bug591-vbox-overflow.svg")
        .assert()
1
        .failure()
1
        .stderr(starts_with(
            "The resulting image would be larger than 32767 pixels",
1
        ));
2
}
#[test]
2
fn accept_language_given() {
2
    RsvgConvert::new_with_input("tests/fixtures/accept-language.svg")
        .arg("--accept-language=es-MX")
        .assert()
1
        .success()
2
        .stdout(file::is_png().with_contents("tests/fixtures/accept-language-es.png"));
2
    RsvgConvert::new_with_input("tests/fixtures/accept-language.svg")
        .arg("--accept-language=de")
        .assert()
1
        .success()
2
        .stdout(file::is_png().with_contents("tests/fixtures/accept-language-de.png"));
2
}
#[test]
2
fn accept_language_fallback() {
2
    RsvgConvert::new_with_input("tests/fixtures/accept-language.svg")
        .arg("--accept-language=fr")
        .assert()
1
        .success()
2
        .stdout(file::is_png().with_contents("tests/fixtures/accept-language-fallback.png"));
2
}
#[test]
2
fn accept_language_invalid_tag() {
    // underscores are not valid in BCP47 language tags
2
    RsvgConvert::new_with_input("tests/fixtures/accept-language.svg")
        .arg("--accept-language=foo_bar")
        .assert()
1
        .failure()
2
        .stderr(contains("invalid language tag"));
2
}
#[test]
2
fn keep_image_data_option() {
1
    RsvgConvert::accepts_arg("--keep-image-data");
2
}
#[test]
2
fn no_keep_image_data_option() {
1
    RsvgConvert::accepts_arg("--no-keep-image-data");
2
}
2
fn is_version_output() -> AndPredicate<StartsWithPredicate, TrimPredicate<EndsWithPredicate>, str> {
4
    starts_with("rsvg-convert version ")
4
        .and(predicates::str::ends_with(env!("CARGO_PKG_VERSION")).trim())
2
}
#[test]
2
fn version_option() {
1
    RsvgConvert::option_yields_output("--version", is_version_output())
2
}
#[test]
2
fn version_short_option() {
1
    RsvgConvert::option_yields_output("-v", is_version_output())
2
}
2
fn is_usage_output() -> OrPredicate<ContainsPredicate, ContainsPredicate, str> {
2
    contains("Usage:").or(contains("USAGE:"))
2
}
#[test]
2
fn help_option() {
1
    RsvgConvert::option_yields_output("--help", is_usage_output())
2
}
#[test]
2
fn help_short_option() {
1
    RsvgConvert::option_yields_output("-?", is_usage_output())
2
}
#[test]
2
fn multiple_stdin_arguments_not_allowed() {
2
    RsvgConvert::new_with_input("tests/fixtures/accept-language.svg")
        .arg("-")
        .arg("-")
        .assert()
1
        .failure()
2
        .stderr(contains("Only one input file can be read from stdin"));
2
}