Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ exclude = ["docs/*", "examples/*", "*.md", "website/*"]
include = ["**/*.rs", "Cargo.toml"]

[dependencies]
rlimit = "0.10.1"
clap = "4.5"
colored = "3.0.0"
difference = "2.0.0"
Expand Down
19 changes: 18 additions & 1 deletion src/runner/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

use std::fs::File;
use std::io::Write;
#[cfg(target_os = "macos")]
use std::os::unix::process::CommandExt;
use std::path::PathBuf;
use std::process::{Command, Stdio};
use std::time::{Duration, Instant};
Expand Down Expand Up @@ -43,6 +45,16 @@
cmd.args(&commands[1..]);
}

#[cfg(target_os = "macos")]
{
// On macOS, process_control's memory_limit is a no-op.
// We use setrlimit via a pre_exec hook to set the memory limit.
cmd.pre_exec(move || {
rlimit::setrlimit(rlimit::Resource::AS, memory_limit, memory_limit)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
});
}

if let Some(file) = &stdin {
// set output file, only exists
let input = File::open(file.to_str().unwrap()).unwrap();
Expand Down Expand Up @@ -78,6 +90,8 @@
#[cfg(target_os = "macos")]
let response = child_output
.controlled_with_output()
// memory_limit is a no-op on macOS
.memory_limit(memory_limit as usize)

Check failure on line 94 in src/runner/cmd.rs

View workflow job for this annotation

GitHub Actions / MacOS Build and Run test

no method named `memory_limit` found for opaque type `impl Control<Result = process_control::Output> + Debug` in the current scope
.time_limit(Duration::from_millis(timeout as u64))
.terminate_for_timeout()
.wait();
Expand Down Expand Up @@ -109,7 +123,10 @@
} else {
#[cfg(unix)]
match output.status.signal() {
Some(6) => res_status = CPStatus::MLE, // SIGABRT: 6
// SIGABRT: 6
Some(6) => res_status = CPStatus::MLE,
// SIGKILL: 9
Some(9) if cfg!(target_os = "macos") => res_status = CPStatus::MLE,
_ => res_status = CPStatus::RTE,
}

Expand Down
68 changes: 68 additions & 0 deletions tests/cli/stress_subcommand/c_mle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Quick Test: CLI for stress testing in competitive programming
* Copyright (C) 2021-present / Luis Miguel Báez
* License: MIT (See the LICENSE file in the repository root directory)
*/

#![allow(unused_imports)]

use std::{error::Error, process::Command};

use assert_cmd::assert::OutputAssertExt;
use predicates::prelude::predicate;

use crate::util::{
test_command_handler::execute_command_stress_with_timeout,
test_constants::{
BINARY, FOLDER_STRESS, GEN_FILE_C, MLE_C, TARGET_FILE_C,
},
test_utilities::create_files_tle,
};

use super::codes::{GEN_C_STRESS, TARGET_C_STRESS};

#[test]
fn cmd_stress_target_mle_c() -> Result<(), Box<dyn Error>> {
let folder = "stress_mle_c";
create_files_tle(
TARGET_FILE_C,
GEN_FILE_C,
MLE_C,
GEN_C_STRESS,
folder,
)?;
let cases: usize = 3;

let mut cmd = Command::new(BINARY);
execute_command_stress_with_timeout(&mut cmd, TARGET_FILE_C, GEN_FILE_C, cases, 5000usize, folder);

Copilot AI Jan 19, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The timeout value 5000usize is duplicated across multiple test functions. Consider extracting this as a constant (e.g., const MLE_TEST_TIMEOUT: usize = 5000;) to improve maintainability and make it easier to adjust the timeout for all MLE tests consistently.

Copilot uses AI. Check for mistakes.

cmd.assert()
.failure()
.stdout(predicate::str::contains("[MLE]").count(cases));

Ok(())
}

#[test]
fn cmd_stress_gen_mle_c() -> Result<(), Box<dyn Error>> {
let folder = "stress_mle_c_gen";
create_files_tle(
TARGET_FILE_C,
GEN_FILE_C,
TARGET_C_STRESS,
MLE_C,
folder,
)?;
let cases: usize = 3;

let mut cmd = Command::new(BINARY);
execute_command_stress_with_timeout(&mut cmd, TARGET_FILE_C, GEN_FILE_C, cases, 5000usize, folder);

Copilot AI Jan 19, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The timeout value 5000usize is duplicated across multiple test functions. Consider extracting this as a constant (e.g., const MLE_TEST_TIMEOUT: usize = 5000;) to improve maintainability and make it easier to adjust the timeout for all MLE tests consistently.

Copilot uses AI. Check for mistakes.

cmd.assert()
.failure()
.stderr(predicate::str::contains(
"Error: QTEST_MEMORY_LIMIT_EXCEEDED\nInfo: caused by generator file give memory limit exceeded / label <gen-file>\n").count(1),
);

Ok(())
}
47 changes: 47 additions & 0 deletions tests/cli/stress_subcommand/codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,50 @@ A.sort()
print(n)
print(*A)
"#;

pub const GEN_C_STRESS: &str = r#"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int random_int(int min, int max) {
return min + rand() % (max - min + 1);
}

int main() {
srand(time(NULL));
int n = random_int(100000, 200000);
printf("%d\n", n);
for (int i = 0; i < n; ++i) {
printf("%d ", random_int(1, 1000000000));
}
printf("\n");
return 0;
}
"#;

pub const TARGET_C_STRESS: &str = r#"
#include <stdio.h>
#include <stdlib.h>

int compare(const void *a, const void *b) {
return (*(int *)a - *(int *)b);
}

int main() {
int n;
scanf("%d", &n);
int *A = (int *)malloc(n * sizeof(int));
for (int i = 0; i < n; ++i) {
scanf("%d", &A[i]);
}
qsort(A, n, sizeof(int), compare);
printf("%d\n", n);
for (int i = 0; i < n; ++i) {
printf("%d ", A[i]);
}
printf("\n");
free(A);
return 0;
}
"#;
26 changes: 20 additions & 6 deletions tests/cli/stress_subcommand/cpp_mle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,26 @@ use crate::util::{
use super::codes::{GEN_CPP_STRESS, TARGET_CPP_STRESS};

#[test]
#[cfg(not(target_os = "macos"))]
fn cmd_stress_target_mle_cpp() -> Result<(), Box<dyn Error>> {
let folder = "stress_mle_cpp";
create_files_tle(
TARGET_FILE_CPP,
GEN_FILE_CPP,
MLE_CPP,
GEN_CPP_STRESS,
FOLDER_STRESS,
folder,
)?;
let cases: usize = 3;

let mut cmd = Command::new(BINARY);
execute_command_stress_with_timeout(&mut cmd, TARGET_FILE_CPP, GEN_FILE_CPP, cases, 5000usize);
execute_command_stress_with_timeout(
&mut cmd,
TARGET_FILE_CPP,
GEN_FILE_CPP,
cases,
5000usize,
folder,
);

cmd.assert()
.failure()
Expand All @@ -42,19 +49,26 @@ fn cmd_stress_target_mle_cpp() -> Result<(), Box<dyn Error>> {
}

#[test]
#[cfg(not(target_os = "macos"))]
fn cmd_stress_gen_mle_cpp() -> Result<(), Box<dyn Error>> {
let folder = "stress_mle_cpp_gen";
create_files_tle(
TARGET_FILE_CPP,
GEN_FILE_CPP,
TARGET_CPP_STRESS,
MLE_CPP,
FOLDER_STRESS,
folder,
)?;
let cases: usize = 3;

let mut cmd = Command::new(BINARY);
execute_command_stress_with_timeout(&mut cmd, TARGET_FILE_CPP, GEN_FILE_CPP, cases, 5000usize);
execute_command_stress_with_timeout(
&mut cmd,
TARGET_FILE_CPP,
GEN_FILE_CPP,
cases,
5000usize,
folder,
);

cmd.assert()
.failure()
Expand Down
1 change: 1 addition & 0 deletions tests/cli/stress_subcommand/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ pub mod cpp_mle;
pub mod cpp_rte;
pub mod python;
pub mod python_rte;
mod c_mle;
14 changes: 11 additions & 3 deletions tests/util/test_command_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,14 @@ use crate::util::test_constants::{FOLDER, FOLDER_CHECK, FOLDER_CMP, FOLDER_STRES
use super::test_constants::FOLDER_OUTPUT;

pub fn execute_command_stress(cmd: &mut Command, target_file: &str, gen_file: &str, cases: usize) {
execute_command_stress_with_timeout(cmd, target_file, gen_file, cases, 1000usize);
execute_command_stress_with_timeout(
cmd,
target_file,
gen_file,
cases,
1000usize,
FOLDER_STRESS,
);
}

pub fn execute_command_stress_with_timeout(
Expand All @@ -20,12 +27,13 @@ pub fn execute_command_stress_with_timeout(
gen_file: &str,
cases: usize,
timeout: usize,
folder: &str,
) {
cmd.arg("stress")
.arg("--target-file")
.arg(format!("{}/{}/{}", FOLDER, FOLDER_STRESS, target_file))
.arg(format!("{}/{}/{}", FOLDER, folder, target_file))
.arg("--gen-file")
.arg(format!("{}/{}/{}", FOLDER, FOLDER_STRESS, gen_file))
.arg(format!("{}/{}/{}", FOLDER, folder, gen_file))
.arg(format!("--timeout={}", timeout))
.arg(format!("--test-cases={}", cases));
}
Expand Down
1 change: 1 addition & 0 deletions tests/util/test_constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ int main() {

pub const MLE_C: &str = r#"
#include <stdio.h>
#include <stdlib.h>
int * a[100000];
int main() {
int n = 1000000000;
Expand Down
Loading