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
    #[allow(clippy::new_ret_no_self)]
37
109
    fn new() -> Command {
38
109
        Command::cargo_bin("rsvg-convert").unwrap()
39
109
    }
40

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
994
#[test]
995
2
fn unsupported_background_color() {
996
1
    let colors = [
997
        "lab(62.2345% -34.9638 47.7721)",
998
        "lch(62.2345% 59.2 126.2)",
999
        "oklab(66.016% -0.1084 0.1114)",
        "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
}