mirror of
https://github.com/kmc7468/cs420.git
synced 2025-12-15 06:58:50 +00:00
Initial commit
This commit is contained in:
2
tests/.gitignore
vendored
Normal file
2
tests/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/platform.info
|
||||
/csmith-*
|
||||
21
tests/ast_printer_test.rs
Normal file
21
tests/ast_printer_test.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
use std::path::Path;
|
||||
|
||||
use kecc::*;
|
||||
|
||||
#[test]
|
||||
fn ast_printer_test() {
|
||||
let mut parse = Parse::default();
|
||||
let dir_path = Path::new("examples/");
|
||||
let dir = dir_path.read_dir().expect("read_dir call failed");
|
||||
for entry in dir {
|
||||
let test_file = ok_or!(entry, continue);
|
||||
let test_unit = parse.translate(&test_file.path().as_path()).expect(
|
||||
&format!(
|
||||
"parse failed {:?}",
|
||||
test_file.path().into_os_string().to_str().unwrap()
|
||||
)
|
||||
.to_owned(),
|
||||
);
|
||||
write_c_test(&test_unit);
|
||||
}
|
||||
}
|
||||
112
tests/fuzz.py
Normal file
112
tests/fuzz.py
Normal file
@@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""Fuzzes KECC.
|
||||
|
||||
For customization, one may restrict/loosen the replacement rule by adding/deleting the pair into
|
||||
below `REPLACE_DICT`.
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import itertools
|
||||
import argparse
|
||||
import sys
|
||||
import re
|
||||
|
||||
REPLACE_DICT = {
|
||||
"#include \"csmith.h\"": "",
|
||||
"volatile ": "",
|
||||
"uint16_t": "unsigned int",
|
||||
"uint32_t": "unsigned int",
|
||||
"int16_t": "int",
|
||||
"int32_t": "int",
|
||||
"uint": "unsigned int",
|
||||
}
|
||||
CSMITH_DIR = "csmith-2.3.0"
|
||||
|
||||
def install_csmith(tests_dir, bin_file):
|
||||
global CSMITH_DIR
|
||||
csmith_root_dir = os.path.join(tests_dir, CSMITH_DIR)
|
||||
if not os.path.exists(bin_file):
|
||||
subprocess.Popen(["curl", "https://embed.cs.utah.edu/csmith/" + CSMITH_DIR + ".tar.gz", "-o", CSMITH_DIR + ".tar.gz"], cwd=tests_dir).communicate()
|
||||
subprocess.Popen(["tar", "xzvf", CSMITH_DIR + ".tar.gz"], cwd=tests_dir).communicate()
|
||||
subprocess.Popen("cmake .; make -j", shell=True, cwd=csmith_root_dir).communicate()
|
||||
else:
|
||||
print("Using the existing csmith...")
|
||||
|
||||
def generate(tests_dir, bin_file, runtime, file_name):
|
||||
"""Feeding options to built Csmith to randomly generate test case.
|
||||
|
||||
For generality, I disabled most of the features that are enabled by default.
|
||||
FYI, please take a look at `-h` flag. By adding or deleting one of `--blah-blah...`
|
||||
in `options` list below, csmith will be able to generate corresponding test case.
|
||||
A developer may customize the options to meet one's needs for testing.
|
||||
"""
|
||||
global CSMITH_DIR
|
||||
options = ["--no-builtins", "--no-safe-math", "--no-unions"]
|
||||
args = [bin_file] + options
|
||||
dst_path = os.path.join(runtime, file_name)
|
||||
|
||||
with open(dst_path, 'w') as f_dst:
|
||||
subprocess.Popen(args, cwd=tests_dir, stdout=f_dst).wait()
|
||||
f_dst.flush()
|
||||
|
||||
return dst_path
|
||||
|
||||
def preprocess(src_path, file_name):
|
||||
"""Preprocessing test case to fit in kecc parser specification.
|
||||
|
||||
It resolves an issue that arbitrarily included header file to hinder parsing.
|
||||
"""
|
||||
global REPLACE_DICT, CSMITH_DIR
|
||||
with open(src_path, 'r') as src:
|
||||
src = str(src.read())
|
||||
|
||||
for _from, _to in REPLACE_DICT.items():
|
||||
src = src.replace(_from, _to)
|
||||
|
||||
with open(os.path.join(os.path.dirname(src_path), file_name), 'w') as dst:
|
||||
dst.write(str(src))
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description='Fuzzing KECC.')
|
||||
parser.add_argument('-n', '--num', type=int, help='The number of tests')
|
||||
parser.add_argument('-p', '--print', action='store_true', help='Fuzzing C AST printer')
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.print:
|
||||
cargo_arg = "-p"
|
||||
else:
|
||||
raise "Specify fuzzing argument"
|
||||
|
||||
tests_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
csmith_bin = os.path.abspath(os.path.join(tests_dir, CSMITH_DIR, "src/csmith"))
|
||||
csmith_runtime = os.path.abspath(os.path.join(tests_dir, CSMITH_DIR, "runtime/"))
|
||||
install_csmith(tests_dir, csmith_bin)
|
||||
|
||||
# Run cargo test infinitely
|
||||
TEST_FAIL_TOKEN = "test result: FAILED."
|
||||
raw_test_file = "raw_test.c"
|
||||
test_file = "test.c"
|
||||
try:
|
||||
if args.num is None:
|
||||
print("Fuzzing with infinitely many test cases. Please press [ctrl+C] to break.")
|
||||
iterator = itertools.count(0)
|
||||
else:
|
||||
print("Fuzzing with {} test cases.".format(args.num))
|
||||
iterator = range(args.num)
|
||||
|
||||
for i in iterator:
|
||||
print("Test case #{}".format(i))
|
||||
preprocess(generate(tests_dir, csmith_bin, csmith_runtime, raw_test_file), test_file)
|
||||
args = ["cargo", "run", "--release", "--bin", "fuzz", "--", cargo_arg, os.path.join(csmith_runtime, test_file)]
|
||||
proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=tests_dir)
|
||||
test_result = str(proc.stdout.read())
|
||||
if TEST_FAIL_TOKEN in test_result:
|
||||
sys.exit("[Error] Test failed. Check `{}` that failed to parse.\nTo investigate, run '{}` in terminal."
|
||||
.format(os.path.join(csmith_runtime, test_file), " ".join(args)))
|
||||
|
||||
except KeyboardInterrupt:
|
||||
proc.terminate()
|
||||
print("\n[KeyboardInterrupt] Test ended")
|
||||
68
tests/ir_test.rs
Normal file
68
tests/ir_test.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use kecc::run_ir::*;
|
||||
use kecc::*;
|
||||
use std::path::Path;
|
||||
|
||||
// TODO: cover all examples in the future
|
||||
#[test]
|
||||
fn ir_interpreter_test() {
|
||||
// Test toy example
|
||||
assert_eq!(run_example("examples/foo.c"), Ok(Value::Int(-1)));
|
||||
|
||||
// Test toy example with negate unary operator
|
||||
assert_eq!(run_example("examples/negate.c"), Ok(Value::Int(1)));
|
||||
|
||||
// Test fibonacci function with for-loop
|
||||
assert_eq!(run_example("examples/fib3.c"), Ok(Value::Int(34)));
|
||||
|
||||
// Test fibonacci function with while-loop
|
||||
assert_eq!(run_example("examples/fib4.c"), Ok(Value::Int(34)));
|
||||
|
||||
// Test fibonacci function with do-while-loop
|
||||
assert_eq!(run_example("examples/fib5.c"), Ok(Value::Int(34)));
|
||||
|
||||
// Test fibonacci function with recursive function call
|
||||
assert_eq!(run_example("examples/fibonacci.c"), Ok(Value::Int(34)));
|
||||
|
||||
// Test example with global variable
|
||||
assert_eq!(run_example("examples/foo3.c"), Ok(Value::Int(30)));
|
||||
|
||||
// Test example with comma expressions
|
||||
assert_eq!(run_example("examples/comma.c"), Ok(Value::Int(7)));
|
||||
|
||||
// Test example with complex function call
|
||||
assert_eq!(run_example("examples/foo4.c"), Ok(Value::Int(6)));
|
||||
|
||||
// Test example with pointer
|
||||
assert_eq!(run_example("examples/pointer.c"), Ok(Value::Int(3)));
|
||||
|
||||
// Test example with sizeof
|
||||
assert_eq!(run_example("examples/sizeof.c"), Ok(Value::Int(4)));
|
||||
|
||||
// Test example with alignof
|
||||
assert_eq!(run_example("examples/alignof.c"), Ok(Value::Int(4)));
|
||||
|
||||
// Test example with simple for statement
|
||||
assert_eq!(run_example("examples/simple_for.c"), Ok(Value::Int(55)));
|
||||
|
||||
// Test example with conditional expression
|
||||
assert_eq!(run_example("examples/cond.c"), Ok(Value::Int(5)));
|
||||
|
||||
// Test example with switch statement
|
||||
assert_eq!(run_example("examples/switch.c"), Ok(Value::Int(2)));
|
||||
}
|
||||
|
||||
fn run_example(example_path: &str) -> Result<Value, InterpreterError> {
|
||||
let example_path = Path::new(example_path);
|
||||
let unit = Parse::default()
|
||||
.translate(&example_path)
|
||||
.expect("parse failed");
|
||||
let ir = Irgen::default()
|
||||
.translate(&unit)
|
||||
.expect("failed to generate ir");
|
||||
|
||||
// TODO: consider command line arguments in the future
|
||||
// TODO: randomly generate argument values
|
||||
let args = Vec::new();
|
||||
|
||||
run_ir(&ir, args)
|
||||
}
|
||||
Reference in New Issue
Block a user