// tests/trip.rs - implemention the TRIP test for Tectonic
// Copyright 2016-2018 the Tectonic Project
// Licensed under the MIT License.

//! Our incarnation of the classic TRIP test. Unfortunately, the test is
//! defined in terms of the precise terminal output and error handling behavior
//! of the engine, so you can't do anything to improve the (incredibly poor) UX
//! of the TeX engine without having to fudge what "the TRIP test" is. That is
//! what we have done.
//!
//! Cargo tries to run tests in multiple simultaneous threads, which of course
//! totally fails for Tectonic since the engine has tons of global state. The
//! multithreading can be disabled by setting the RUST_TEST_THREADS environment
//! variable to "1", but that's an annoying solution. So, we use a global mutex
//! to achieve the same effect. Classy.

use tectonic::io::testing::SingleInputFileIo;
use tectonic::io::{FilesystemPrimaryInputIo, IoProvider, IoStack, MemoryIo};
use tectonic::TexEngine;
use tectonic_bridge_core::{CoreBridgeLauncher, MinimalDriver};
use tectonic_engine_xetex::TexOutcome;
use tectonic_status_base::NoopStatusBackend;

#[path = "util/mod.rs"]
mod util;
use crate::util::{test_path, Expected, ExpectedFile};

#[test]
fn trip_test() {
    util::set_test_root();

    let mut p = test_path(&["trip", "trip"]);

    // An IoProvider for the input file.
    p.set_extension("tex");
    let mut tex = FilesystemPrimaryInputIo::new(&p);

    // And the TFM file.
    p.set_extension("tfm");
    let mut tfm = SingleInputFileIo::new(&p);

    // Read in the expected outputs.
    let expected_log = ExpectedFile::read_with_extension(&mut p, "log");
    let expected_xdv = ExpectedFile::read_with_extension(&mut p, "xdv");
    let expected_fot = ExpectedFile::read_with_extension(&mut p, "fot");
    p.set_file_name("tripos");
    let expected_os = ExpectedFile::read_with_extension(&mut p, "tex");

    // MemoryIo layer that will accept the outputs. Save `files` since the
    // engine consumes `mem`.
    let mut mem = MemoryIo::new(true);

    // First engine pass -- make the format file.
    let res1 = {
        let io = IoStack::new(vec![&mut mem as &mut dyn IoProvider, &mut tex, &mut tfm]);
        let mut hooks = MinimalDriver::new(io);
        let mut status = NoopStatusBackend::default();
        let mut launcher = CoreBridgeLauncher::new(&mut hooks, &mut status);

        TexEngine::default()
            .halt_on_error_mode(false)
            .initex_mode(true)
            .process(&mut launcher, "INITEX", "trip")
    };

    // Second pass -- process it
    let res2 = {
        let io = IoStack::new(vec![&mut mem as &mut dyn IoProvider, &mut tex, &mut tfm]);
        let mut hooks = MinimalDriver::new(io);
        let mut status = NoopStatusBackend::default();
        let mut launcher = CoreBridgeLauncher::new(&mut hooks, &mut status);

        TexEngine::default()
            .halt_on_error_mode(false)
            .initex_mode(false)
            .process(&mut launcher, "trip.fmt", "trip")
    };

    // Check that outputs match expectations.
    let files = &*mem.files.borrow();
    Expected::new()
        .res(Ok(TexOutcome::Errors), res1)
        .res(Ok(TexOutcome::Errors), res2)
        .file(expected_log.collection(files))
        .file(expected_xdv.collection(files))
        .file(expected_os.collection(files))
        .file(expected_fot.data(&files.get("").unwrap().data))
        .finish();
}

#[test]
fn etrip_test() {
    util::set_test_root();

    let mut p = test_path(&["trip", "etrip"]);

    // An IoProvider the input file.
    p.set_extension("tex");
    let mut tex = FilesystemPrimaryInputIo::new(&p);

    // And the TFM file.
    p.set_extension("tfm");
    let mut tfm = SingleInputFileIo::new(&p);

    // Read in the expected outputs.
    let expected_log = ExpectedFile::read_with_extension(&mut p, "log");
    let expected_xdv = ExpectedFile::read_with_extension(&mut p, "xdv");
    let expected_fot = ExpectedFile::read_with_extension(&mut p, "fot");
    let expected_out = ExpectedFile::read_with_extension(&mut p, "out");

    // MemoryIo layer that will accept the outputs. Save `files` since the
    // engine consumes `mem`.
    let mut mem = MemoryIo::new(true);
    let files = mem.files.clone();

    // First engine pass -- make the format file.
    let res1 = {
        let io = IoStack::new(vec![&mut mem as &mut dyn IoProvider, &mut tex, &mut tfm]);
        let mut hooks = MinimalDriver::new(io);
        let mut status = NoopStatusBackend::default();
        let mut launcher = CoreBridgeLauncher::new(&mut hooks, &mut status);

        TexEngine::default()
            .halt_on_error_mode(false)
            .initex_mode(true)
            .process(&mut launcher, "INITEX", "etrip")
    };

    // Second pass -- process it
    let res2 = {
        let io = IoStack::new(vec![&mut mem, &mut tex, &mut tfm]);
        let mut hooks = MinimalDriver::new(io);
        let mut status = NoopStatusBackend::default();
        let mut launcher = CoreBridgeLauncher::new(&mut hooks, &mut status);

        TexEngine::default()
            .halt_on_error_mode(false)
            .initex_mode(false)
            .process(&mut launcher, "etrip.fmt", "etrip")
    };

    // Check that outputs match expectations.
    let files = &*files.borrow();
    Expected::new()
        .res(Ok(TexOutcome::Errors), res1)
        .res(Ok(TexOutcome::Errors), res2)
        .file(expected_log.collection(files))
        .file(expected_xdv.collection(files))
        .file(expected_out.collection(files))
        .file(expected_fot.data(&files.get("").unwrap().data))
        .finish();
}
