ezcp 0.4.0

A simple tool to automatically generate test cases for competitive programming problems.
Documentation
#[cfg(test)]
#[allow(clippy::unwrap_used)]
pub mod cpp_runner_tests {
    use std::time::Instant;
    use tempfile::TempDir;
    use crate::Error::CompilerError;
    use crate::runner::cpp_runner::CppRunner;
    use crate::runner::exec_runner::RunResult;
    use crate::tests::test_shared::initialize_logger;

    const HELLO_WORLD_PROGRAM: &str = r#"
    #include <iostream>
    using namespace std;
    int main() {
        cout << "Hello, World!" << endl;
        return 0;
    }
    "#;

    #[test]
    fn test_runner_new() {
        initialize_logger();

        let tempdir = TempDir::new().unwrap();

        let _runner = CppRunner::new(&tempdir.path()).unwrap();

        drop(tempdir);
    }

    #[test]
    fn test_runner_add_program() {
        initialize_logger();

        let tempdir = TempDir::new().unwrap();
        let mut runner = CppRunner::new(tempdir.path()).unwrap();

        let _handle = runner.add_program(HELLO_WORLD_PROGRAM).unwrap();

        drop(tempdir);
    }

    #[test]
    fn test_runner_add_faulty_program() {
        initialize_logger();

        let tempdir = TempDir::new().unwrap();
        let mut runner = CppRunner::new(tempdir.path()).unwrap();

        let faulty_program_source = r#"
        #include <iostream>
        using namespace std;
        int main() {
            compile error here
            cout << "Hello, World!" << endl;
            return 1;
        }
        "#;

        assert!(matches!(runner.add_program(faulty_program_source), Err(CompilerError { .. })));

        drop(tempdir);
    }

    #[test]
    fn test_runner_run_program() {
        initialize_logger();

        let tempdir = TempDir::new().unwrap();
        let mut runner = CppRunner::new(tempdir.path()).unwrap();

        let program_handle = runner.add_program(HELLO_WORLD_PROGRAM).unwrap();
        let task_handle = runner.add_task(program_handle, "".to_owned(), 1.0);

        runner.run_tasks(None).unwrap();

        let result = runner.get_result(task_handle);

        assert!(matches!(result, RunResult::Ok( .. )));

        if let RunResult::Ok(_, output) = result {
            assert_eq!(output.trim(), "Hello, World!");
        }

        drop(tempdir);
    }

    #[test]
    fn test_runner_run_programs() {
        initialize_logger();

        let tempdir = TempDir::new().unwrap();
        let mut runner = CppRunner::new(tempdir.path()).unwrap();

        let mut program_handles = Vec::new();
        for _ in 0..5 {
            let code = r#"
            #include <iostream>
            using namespace std;
            int main() {
                int n;
                cin>>n;
                cout << $program_id$ << " " << n << endl;
                return 0;
            }
            "#.replace("$program_id$", &format!("{}", program_handles.len()));
            let program_handle = runner.add_program(&code).unwrap();
            program_handles.push(program_handle);
        }

        let mut task_handles = Vec::new();
        for i in 0..100 {
            let input = format!("{}\n", i);
            let task_handle = runner.add_task(program_handles[i % program_handles.len()], input, 1.0);
            task_handles.push(task_handle);
        }

        runner.run_tasks(None).unwrap();

        for (i, task_handle) in task_handles.iter().enumerate() {
            let result = runner.get_result(*task_handle);
            assert!(matches!(result, RunResult::Ok( .. )));

            if let RunResult::Ok(_, output) = result {
                assert_eq!(output.trim(), format!("{} {}", i % program_handles.len(), i));
            }
        }

        drop(tempdir);
    }

    #[test]
    fn test_same_program_100_times() {
        initialize_logger();

        let tempdir = TempDir::new().unwrap();
        let mut runner = CppRunner::new(tempdir.path()).unwrap();

        let time = Instant::now();

        for _i in 0..100 {
            let _program_handle = runner.add_program(HELLO_WORLD_PROGRAM).unwrap();
        }

        // make sure it doesn't take too long
        assert!(time.elapsed().as_secs() < 10, "Adding the same program 100 times took too long");

        drop(tempdir);
    }

    #[test]
    fn test_runner_program_tle() {
        initialize_logger();

        let tempdir = TempDir::new().unwrap();
        let mut runner = CppRunner::new(tempdir.path()).unwrap();

        let program_source = r#"
        int main() {
            while (true) {
                // Infinite loop to simulate TLE
            }
            return 0;
        }
        "#;

        let program_handle = runner.add_program(program_source).unwrap();
        let task_handle = runner.add_task(program_handle, "1\n".to_owned(), 1.0);

        runner.run_tasks(None).unwrap();

        // Check that the result is indeed a TLE
        let result = runner.get_result(task_handle);
        assert!(matches!(result, RunResult::TimedOut));

        drop(tempdir);
    }

    #[test]
    #[cfg(not(windows))]
    fn test_runner_program_crash() {
        initialize_logger();

        let tempdir = TempDir::new().unwrap();
        let mut runner = CppRunner::new(tempdir.path()).unwrap();

        let program_source = r#"
        int main() {
            int*n=nullptr;
            while(true){
                *n=1;
                n++;
            }
            return 0;
        }
        "#;

        let program_handle = runner.add_program(program_source).unwrap();
        let task_handle = runner.add_task(program_handle, "1\n".to_owned(), 1.0);

        runner.run_tasks(None).unwrap();

        // Check that the result is indeed a crash
        let result = runner.get_result(task_handle);
        assert!(matches!(result, RunResult::Crashed));

        drop(tempdir);
    }

    #[test]
    #[cfg(not(windows))]
    fn test_runner_pickup_cache() {
        initialize_logger();

        let tempdir = TempDir::new().unwrap();
        let mut start = Instant::now();

        for it in 0..30 {
            if it == 1 {
                // After the first iteration, we should have a cache
                start = Instant::now();
            }
            let mut runner = CppRunner::new(tempdir.path()).unwrap();

            let program_handle = runner.add_program(HELLO_WORLD_PROGRAM).unwrap();
            let task_handle = runner.add_task(program_handle, "".to_owned(), 1.0);

            runner.run_tasks(None).unwrap();

            let result = runner.get_result(task_handle);

            assert!(matches!(result, RunResult::Ok( .. )));

            if let RunResult::Ok(_, output) = result {
                assert_eq!(output.trim(), "Hello, World!");
            }
        }

        let elapsed = start.elapsed();
        assert!(elapsed.as_secs() < 10, "Cache pickup took too long: {:?}", elapsed);

        drop(tempdir);
    }
}