use std::{fs::File, io::Write, process::Command};
use ccarp::{translate_project, translate_single_file, CompilerFlags};
use rand::Rng;
fn run(code: &str, flags: CompilerFlags, release: bool) -> (Option<i32>,Option<i32>) {
let mut rng=rand::rng();
let id=rng.random::<u128>();
let c_name=format!("main{id}.c");
let c_out=format!("a{id}.out");
let mut file = File::create(&c_name).unwrap_or_else(|_| panic!("Failed to create file '{c_name}'!"));
file.write_all(code.as_bytes()).unwrap_or_else(|_| panic!("Could not write into file '{c_name}'!"));
let c_output=Command::new("gcc").arg(&c_name).arg("-o").arg(&c_out).output().and_then(|_| Command::new(format!("./{c_out}")).output()).expect("Failed to compile C program!").status.code();
let rust_name=format!("auto{id}");
translate_single_file(&c_name, &rust_name, flags).unwrap();
let rust_output =
if !release {
Command::new("cargo").arg("run").current_dir(&rust_name).output().expect("Failed to run rust program!").status.code()
} else {
Command::new("cargo").arg("run").arg("--release").current_dir(&rust_name).output().expect("Failed to run rust program!").status.code()
};
Command::new("rm").arg(&c_name).output().expect("Could not delete c file!");
Command::new("rm").arg(c_out).output().expect("Could not delete .out file!");
Command::new("rm").args(["-r"]).arg(rust_name).output().expect("Could not delete rust directory!");
(c_output,rust_output)
}
fn run_toplevel(code: &str, flags: CompilerFlags,release: bool) -> (Option<i32>,Option<i32>) {
run(&format!("int main() {{\n{code}\nreturn res;\n}}"),flags,release)
}
macro_rules! run {
($code:expr) => {
run_toplevel($code,CompilerFlags::Raw|CompilerFlags::NoHelper,false)
};
($code:expr,$flags:expr) => {
run_toplevel($code,$flags,false)
};
($code:expr,$flags:expr,$release:expr) => {
run_toplevel($code,$flags,$release)
};
(%$code:expr) => {
run($code,CompilerFlags::Raw|CompilerFlags::NoHelper,false)
};
(%$code:expr,$flags:expr) => {
run($code,$flags,false)
};
(%$code:expr,$flags:expr,$release:expr) => {
run($code,$flags,$release)
};
}
#[test]
fn two_plus_two() {
let (c,r)=run!("int res=2+2;");
assert_eq!(c,r);
}
#[test]
fn order_of_operations() {
let (c,r)=run!("int res=2/1+1;");
assert_eq!(c,r);
}
#[test]
fn fib() {
let (c,r)=run!(%"
int fib(int n) {
int a=0,b=1,c;
for (int i=0;i<n;i++) {
c=a+b;
a=b;
b=c;
}
return a;
}
int main() {
return fib(11);
}
");
assert_eq!(c,r);
}
#[test]
fn fib2() {
let (c,r)=run!(%"
int fib(int n) {
if (n==0) return 0;
if (n<=2) return 1;
else return fib(n-1)+fib(n-2);
}
int main() {
return fib(11);
}
");
assert_eq!(c,r);
}
#[test]
fn limits() {
let (c,r)=run!("int res=0;");
assert_eq!(c,r);
let (c,r)=run!("unsigned char res=255;");
assert_eq!(c,r);
}
#[test]
fn calculation() {
let (c,r)=run!("
int res=0;
for (int i=0;i<10000;i++) {
res+=i;
}
");
assert_eq!(c,r);
}
#[test]
fn max() {
let (c,r)=run!(%"
int max(int a, long b) {
return a > b ? a : b;
}
int main() {
return max(5,10);
}
");
assert_eq!(c,r);
}
#[test]
fn field_access() {
let (c,r)=run!("
union A {
int a,b;
};
union A a={1};
int res=a.a;
");
assert_eq!(c,r);
let (c,r)=run!("
struct A {
int a,b;
};
struct A a={1,2};
int res=a.a;
");
assert_eq!(c,r);
}
#[test]
fn switch() {
for s in ["7","87","123","2847","29383"] {
let (c,r)=run!(&format!("
char *const num=\"{s}\";
int res = 0;
for (int i = 0; i < {}; i++) {{
switch (num[i]) {{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{{
res *= 10;
res += num[i] - '0';
}}
break;
default:
return 0;
break;
}}
}}
",s.len()));
assert_eq!(c,r);
}
}
#[test]
fn calculation2() {
let (c,r)=run!("
int rndCurrent = 0;
int rnd[10]={0,5,7,4,9,1,8,2,6,3};
int number = 13;
for (int i = 0; i < number; i++) {
number += rnd[rndCurrent];
number = number & rnd[rndCurrent];
if (rndCurrent == 9) {
rndCurrent = 0;
}
else {
rndCurrent += 1;
}
}
for (int i = 0; i < 10; i++)
{
rnd[i] = number & i + rndCurrent;
number = number << 2;
if (rndCurrent == 9) {
rndCurrent = 0;
}
else {
rndCurrent += 1;
}
}
int isPrime = number % 2 == 0;
if (isPrime){
rndCurrent += (number | 2) << 3;
rndCurrent = rndCurrent % 10;
return rnd[rndCurrent];
}
for (int i = 1; i < number >> 1; i += 2)
{
rndCurrent += number | i;
if (number % i == 0)
{
isPrime = 0;
break;
}
}
rndCurrent = rndCurrent % 10;
int res=rnd[rndCurrent];
");
assert_eq!(c,r);
}
#[test]
fn is_prime() {
for num in [2,11,19,81,100,997] {
let (c,r)=run!(&format!("
int n = {num};
int cnt = 0;
// If number is less than/equal to 1,
// it is not prime
if (n <= 1)
return 0;
else {{
// Check for divisors from 1 to n
for (int i = 1; i <= n; i++) {{
// Check how many number is divisible
// by n
if (n % i == 0)
cnt++;
}}
// If n is divisible by more than 2 numbers
// then it is not prime
if (cnt > 2)
return 0;
// else it is prime
else
return 1;
}}
int res=101;
"));
assert_eq!(c,r);
}
}
#[test]
fn sizeof() {
let (c,r)=run!("
int res=0;
res += sizeof(char) >= 1;
res += sizeof(short) >= 2;
res += sizeof(int) >= 2;
res += sizeof(long) >= 4;
res += sizeof(long long) >= 8;
");
assert_eq!(c,r);
}
#[test]
fn area_and_perimeter() {
let (c,r)=run!(%"
int area(int a, int b)
{
int A;
A = a * b;
return A;
}
int perimeter(int a, int b)
{
int P;
P = 2 * (a + b);
return P;
}
int main()
{
int l = 11, b = 9;
return area(l, b) + perimeter(l, b);
}
");
assert_eq!(c,r);
}
#[test]
fn leap_year() {
let (c,r)=run!(%"
_Bool checkYear(int year)
{
// If a year is multiple of 400,
// then it is a leap year
if (year % 400 == 0)
return 1;
// Else If a year is multiple of 100,
// then it is not a leap year
else if (year % 100 == 0)
return 0;
// Else If a year is multiple of 4,
// then it is a leap year
else if (year % 4 == 0)
return 1;
// if no above condition is satisfied, then it is not
// a leap year
return 0;
}
// Driver code
int main()
{
int year = 2000;
if (checkYear(year)) {
return 1;
}
else {
return 0;
}
return 101;
}
");
assert_eq!(c,r);
}
#[test]
fn reverse_num() {
let (c,r)=run!(%"
int reverseDigits(int num)
{
int rev_num = 0;
while (num > 0) {
rev_num = rev_num * 10 + num % 10;
num = num / 10;
}
return rev_num;
}
// Driver code
int main()
{
int num = 4562;
return reverseDigits(num);
}
");
assert_eq!(c,r);
}
#[test]
fn palindrome() {
let (c,r)=run!(%"
int reverseNum(int N) {
// Function to store the reversed number
int rev = 0;
while (N > 0) {
// Extract the last digit
int dig = N % 10;
// Append the digit to the reversed number
rev = rev * 10 + dig;
// Remove the last digit
N /= 10;
}
return rev;
}
int isPalindrome(int N) {
// Negative numbers are not palindromes
if (N < 0)
return 0;
return N == reverseNum(N);
}
int main() {
int N = 121;
if (isPalindrome(N)) {
return 1;
}
else {
return 0;
}
return 101;
}
");
assert_eq!(c,r);
}
#[test]
fn factorial() {
let (c,r)=run!(%"
unsigned int factorial(unsigned int N) {
int fact = 1, i;
// Loop from 1 to N to get the factorial
for (i = 1; i <= N; i++) {
fact *= i;
}
return fact;
}
int main() {
int N = 5;
return factorial(N);
}
");
assert_eq!(c,r);
}
#[test]
fn largest_array_element() {
let (c,r)=run!(%"
int findMax(int arr[4], int n) {
// Assume the first element is the largest
int max = arr[0];
for (int i = 1; i < n; i++) {
// Update max if arr[i] is greater
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
int main() {
int arr[4] = {5, 2, 7, 6};
return findMax(arr, 4);
}
");
assert_eq!(c,r);
}
#[test]
fn generic_rotate() {
let (c,r)=run!(%"
// Function to rotate array left by one position
void leftRotateByOne(int arr[], int n)
{
int temp = arr[0];
for (int i = 0; i < n - 1; i++) {
arr[i] = arr[i + 1];
}
arr[n - 1] = temp;
}
// Function to rotate array left by d positions
void leftRotate(int arr[], int d, int n)
{
for (int i = 0; i < d; i++) {
leftRotateByOne(arr, n);
}
}
int main()
{
int arr[]={ 1, 2, 3, 4, 5, 6, 7 };
int n = sizeof(arr) / sizeof(arr[0]);
int d = 2;
// Rotate array left by d positions
leftRotate(arr, d, n);
return arr[0];
}
");
assert_eq!(c,r);
}
#[test]
fn deceptive_syntax() {
let (c,r)=run!("int a=23; int res=(a)+2;");
assert_eq!(c,r);
let (c,r)=run!("typedef int inty; int res=(inty)+2;");
assert_eq!(c,r);
}
#[test]
fn ifelse() {
let (c,r)=run!("
int a=12;
long long int b=a;
char c=b;
int res;
if (a==b) {
res=(int)c;
}
else {
res=101;
}
");
assert_eq!(c,r);
}
#[test]
fn dowhile() {
let (c,r)=run!("
int res=0;
do {
res+=3;
int a=3;
a-=5;
int b=43;
res-=2;
}
while (res<23);
");
assert_eq!(c,r);
}
#[test]
fn helper() {
let (c,r)=run!("int a=0; int res=a++;",CompilerFlags::Raw);
assert_eq!(c,r);
}
#[test]
fn escape() {
let (c,r)=run!(r"int res='\n';");
assert_eq!(c,r);
let (c,r)=run!(r"int res='\t';");
assert_eq!(c,r);
let (c,r)=run!(r"int res='\'';");
assert_eq!(c,r);
let (c,r)=run!(r"int res='\?';");
assert_eq!(c,r);
let (c,r)=run!(r"int res='\\';");
assert_eq!(c,r);
let (c,r)=run!(r"int res='\a';");
assert_eq!(c,r);
let (c,r)=run!(r"int res='\b';");
assert_eq!(c,r);
let (c,r)=run!(r"int res='\f';");
assert_eq!(c,r);
let (c,r)=run!(r"int res='\r';");
assert_eq!(c,r);
let (c,r)=run!(r"int res='\v';");
assert_eq!(c,r);
}
#[test]
fn expr() {
let (c,r)=run!(r"int a=23; int res=(1==1,2,(int)(long)-~a+++3*1<<2>>1<127==1&12^13|14&&0||1?3:4);",CompilerFlags::Raw);
assert_eq!(c,r);
}
#[test]
fn struct_typedef() {
let (c,r)=run!(r"typedef struct { int a,b; } ABStruct; ABStruct ab={1,2}; int res=ab.a;");
assert_eq!(c,r);
}
#[test]
fn designator() {
let (c,r)=run!(r"int a[5]={[2]=1,2,3,[0]=4,5}; int res=a[4];");
assert_eq!(c,r);
let (c,r)=run!(r"struct { int a,b; } ab={.b=2,.a=1}; int res=ab.a;");
assert_eq!(c,r);
}
#[test]
fn enum_test() {
let (c,r)=run!(r"enum ABC { A=2+3, B, C }; int res=A;");
assert_eq!(c,r);
}
#[test]
fn fn_def() {
let (c,r)=run!(%"
int fun1(int a, int b) {
return a+b;
}
int fun2(a,b) int a,b; {
return a-b;
}
int fun3(a,b) int a; int b; {
return a*b;
}
int main() {
return fun1(fun2(fun3(1,2),3),4);
}
");
assert_eq!(c,r);
}
#[test]
fn apple() {
let (c,r)=run!(%"
enum AppleColor {
Red,
Green,
Yellow,
Blue
};
typedef union place_ {
unsigned short Region;
unsigned short CountryCode;
} Place;
typedef struct apple_ {
enum AppleColor color;
unsigned int amount;
Place origin;
unsigned short isDomestic;
} Apple;
int main()
{
Apple apples[10]={{Blue,0,{0},0},{Blue,0,{0},0},{Blue,0,{0},0},{Blue,0,{0},0},{Blue,0,{0},0},{Blue,0,{0},0},{Blue,0,{0},0},{Blue,0,{0},0},{Blue,0,{0},0},{Blue,0,{0},0}};
Apple newApple = { .color=Blue, .amount=5, .origin.Region=4, .isDomestic=1 };
Apple newApple2 = { .color=Blue, .amount=5, .origin={4}, .isDomestic=1 };
enum AppleColor colors[4] = { Red, Green, Yellow, Blue };
for (unsigned int i = 0; i < 10; i++)
{
apples[i].color = colors[i % 4];
apples[i].amount = i * 2;
apples[i].origin.CountryCode = i;
apples[i].isDomestic = 0;
}
signed int i1 = 9;
int number = 0;
while (i1 > -1)
{
if ((int)apples[i1].color == (int)newApple.color)
{
number++;
}
i1--;
}
return number;
}
",CompilerFlags::Default);
assert_eq!(c,r)
}
#[test]
fn fuzz() {
let c_output=Command::new("gcc").arg("func.c").arg("driver.c").current_dir("tests/fuzz/").output().and_then(|_| Command::new("./a.out").current_dir("tests/fuzz/").output()).expect("Failed to compile C program!").status.code();
let rust_name="auto";
translate_project("tests/fuzz/", rust_name, CompilerFlags::Pedantic).unwrap();
let rust_output=Command::new("cargo").arg("run").arg("--release").current_dir(format!("tests/fuzz/{rust_name}")).output().expect("Failed to run rust program!").status.code();
Command::new("rm").arg("./a.out").current_dir("tests/fuzz/").output().expect("Could not delete .out file!");
Command::new("rm").args(["-r"]).arg(format!("tests/fuzz/{rust_name}")).output().expect("Could not delete rust directory!");
assert_eq!(c_output,rust_output)
}
#[test]
fn point() {
for flags in [CompilerFlags::Default, CompilerFlags::MakeItWork] {
let (c,r)=run!("
int nums[5]={1,2,3,4,5};
int *const nums2=nums;
int res=nums2[0];
",flags);
assert_eq!(c,r);
let (c,r)=run!("
int nums[5]={1,2,3,4,5};
int *nums2=nums;
int res=nums2[0];
",flags);
assert_eq!(c,r)
}
}