add tests
parent
893e152dab
commit
763ce1c485
|
@ -33,17 +33,30 @@ set(ZIG_SOURCES
|
||||||
"${CMAKE_SOURCE_DIR}/src/os.cpp"
|
"${CMAKE_SOURCE_DIR}/src/os.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(TEST_SOURCES
|
||||||
|
"${CMAKE_SOURCE_DIR}/src/buffer.cpp"
|
||||||
|
"${CMAKE_SOURCE_DIR}/src/util.cpp"
|
||||||
|
"${CMAKE_SOURCE_DIR}/src/os.cpp"
|
||||||
|
"${CMAKE_SOURCE_DIR}/test/standalone.cpp"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
set(CONFIGURE_OUT_FILE "${CMAKE_BINARY_DIR}/config.h")
|
set(CONFIGURE_OUT_FILE "${CMAKE_BINARY_DIR}/config.h")
|
||||||
configure_file (
|
configure_file (
|
||||||
"${CMAKE_SOURCE_DIR}/src/config.h.in"
|
"${CMAKE_SOURCE_DIR}/src/config.h.in"
|
||||||
${CONFIGURE_OUT_FILE}
|
${CONFIGURE_OUT_FILE}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
${CMAKE_SOURCE_DIR}
|
||||||
|
${CMAKE_BINARY_DIR}
|
||||||
|
"${CMAKE_SOURCE_DIR}/src"
|
||||||
|
)
|
||||||
|
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Wno-unused-variable -Wno-unused-but-set-variable")
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Wno-unused-variable -Wno-unused-but-set-variable")
|
||||||
|
|
||||||
set(EXE_CFLAGS "-std=c++11 -fno-exceptions -fno-rtti -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -Werror -Wall -Werror=strict-prototypes -Werror=old-style-definition -Werror=missing-prototypes")
|
set(EXE_CFLAGS "-std=c++11 -fno-exceptions -fno-rtti -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -Werror -Wall -Werror=strict-prototypes -Werror=old-style-definition -Werror=missing-prototypes")
|
||||||
|
|
||||||
|
|
||||||
add_executable(zig ${ZIG_SOURCES})
|
add_executable(zig ${ZIG_SOURCES})
|
||||||
set_target_properties(zig PROPERTIES
|
set_target_properties(zig PROPERTIES
|
||||||
COMPILE_FLAGS ${EXE_CFLAGS})
|
COMPILE_FLAGS ${EXE_CFLAGS})
|
||||||
|
@ -52,3 +65,8 @@ target_link_libraries(zig LINK_PUBLIC
|
||||||
)
|
)
|
||||||
install(TARGETS zig DESTINATION bin)
|
install(TARGETS zig DESTINATION bin)
|
||||||
|
|
||||||
|
add_executable(run_tests ${TEST_SOURCES})
|
||||||
|
target_link_libraries(run_tests)
|
||||||
|
set_target_properties(run_tests PROPERTIES
|
||||||
|
COMPILE_FLAGS ${EXE_CFLAGS}
|
||||||
|
)
|
||||||
|
|
13
README.md
13
README.md
|
@ -31,7 +31,6 @@ readable, safe, optimal, and concise code to solve any computing problem.
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
* Unit tests.
|
|
||||||
* C style comments.
|
* C style comments.
|
||||||
* Simple .so library
|
* Simple .so library
|
||||||
* Multiple files
|
* Multiple files
|
||||||
|
@ -66,7 +65,7 @@ Root : many(TopLevelDecl) token(EOF)
|
||||||
|
|
||||||
TopLevelDecl : FnDef | ExternBlock
|
TopLevelDecl : FnDef | ExternBlock
|
||||||
|
|
||||||
ExternBlock : many(Directive) token(Extern) token(LBrace) many(FnProtoDecl) token(RBrace)
|
ExternBlock : many(Directive) token(Extern) token(LBrace) many(FnDecl) token(RBrace)
|
||||||
|
|
||||||
FnProto : token(Fn) token(Symbol) ParamDeclList option(token(Arrow) Type)
|
FnProto : token(Fn) token(Symbol) ParamDeclList option(token(Arrow) Type)
|
||||||
|
|
||||||
|
@ -96,3 +95,13 @@ FnCall : token(Symbol) token(LParen) list(Expression, token(Comma)) token(RParen
|
||||||
|
|
||||||
Directive : token(NumberSign) token(Symbol) token(LParen) token(String) token(RParen)
|
Directive : token(NumberSign) token(Symbol) token(LParen) token(String) token(RParen)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Building
|
||||||
|
|
||||||
|
```
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
./run_tests
|
||||||
|
```
|
||||||
|
|
84
src/os.cpp
84
src/os.cpp
|
@ -10,6 +10,11 @@
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
void os_spawn_process(const char *exe, ZigList<const char *> &args, bool detached) {
|
void os_spawn_process(const char *exe, ZigList<const char *> &args, bool detached) {
|
||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
|
@ -32,6 +37,24 @@ void os_spawn_process(const char *exe, ZigList<const char *> &args, bool detache
|
||||||
zig_panic("execvp failed: %s", strerror(errno));
|
zig_panic("execvp failed: %s", strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void read_all_fd(int fd, Buf *out_buf) {
|
||||||
|
static const ssize_t buf_size = 8192;
|
||||||
|
buf_resize(out_buf, buf_size);
|
||||||
|
ssize_t actual_buf_len = 0;
|
||||||
|
for (;;) {
|
||||||
|
ssize_t amt_read = read(fd, buf_ptr(out_buf), buf_len(out_buf));
|
||||||
|
if (amt_read < 0)
|
||||||
|
zig_panic("fd read error");
|
||||||
|
actual_buf_len += amt_read;
|
||||||
|
if (amt_read == 0) {
|
||||||
|
buf_resize(out_buf, actual_buf_len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf_resize(out_buf, actual_buf_len + buf_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename) {
|
void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename) {
|
||||||
int last_index = buf_len(full_path) - 1;
|
int last_index = buf_len(full_path) - 1;
|
||||||
if (last_index >= 0 && buf_ptr(full_path)[last_index] == '/') {
|
if (last_index >= 0 && buf_ptr(full_path)[last_index] == '/') {
|
||||||
|
@ -49,3 +72,64 @@ void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename) {
|
||||||
buf_init_from_buf(out_basename, full_path);
|
buf_init_from_buf(out_basename, full_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void os_exec_process(const char *exe, ZigList<const char *> &args,
|
||||||
|
int *return_code, Buf *out_stderr, Buf *out_stdout)
|
||||||
|
{
|
||||||
|
int stdin_pipe[2];
|
||||||
|
int stdout_pipe[2];
|
||||||
|
int stderr_pipe[2];
|
||||||
|
|
||||||
|
int err;
|
||||||
|
if ((err = pipe(stdin_pipe)))
|
||||||
|
zig_panic("pipe failed");
|
||||||
|
if ((err = pipe(stdout_pipe)))
|
||||||
|
zig_panic("pipe failed");
|
||||||
|
if ((err = pipe(stderr_pipe)))
|
||||||
|
zig_panic("pipe failed");
|
||||||
|
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid == -1)
|
||||||
|
zig_panic("fork failed");
|
||||||
|
if (pid == 0) {
|
||||||
|
// child
|
||||||
|
if (dup2(stdin_pipe[0], STDIN_FILENO) == -1)
|
||||||
|
zig_panic("dup2 failed");
|
||||||
|
|
||||||
|
if (dup2(stdout_pipe[1], STDOUT_FILENO) == -1)
|
||||||
|
zig_panic("dup2 failed");
|
||||||
|
|
||||||
|
if (dup2(stderr_pipe[1], STDERR_FILENO) == -1)
|
||||||
|
zig_panic("dup2 failed");
|
||||||
|
|
||||||
|
const char **argv = allocate<const char *>(args.length + 2);
|
||||||
|
argv[0] = exe;
|
||||||
|
argv[args.length + 1] = nullptr;
|
||||||
|
for (int i = 0; i < args.length; i += 1) {
|
||||||
|
argv[i + 1] = args.at(i);
|
||||||
|
}
|
||||||
|
execvp(exe, const_cast<char * const *>(argv));
|
||||||
|
zig_panic("execvp failed: %s", strerror(errno));
|
||||||
|
} else {
|
||||||
|
// parent
|
||||||
|
close(stdin_pipe[0]);
|
||||||
|
close(stdout_pipe[1]);
|
||||||
|
close(stderr_pipe[1]);
|
||||||
|
|
||||||
|
waitpid(pid, return_code, 0);
|
||||||
|
|
||||||
|
read_all_fd(stdout_pipe[0], out_stdout);
|
||||||
|
read_all_fd(stderr_pipe[0], out_stderr);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void os_write_file(Buf *full_path, Buf *contents) {
|
||||||
|
int fd;
|
||||||
|
if ((fd = open(buf_ptr(full_path), O_CREAT|O_CLOEXEC|O_WRONLY|O_TRUNC, S_IRWXU)) == -1)
|
||||||
|
zig_panic("open failed");
|
||||||
|
ssize_t amt_written = write(fd, buf_ptr(contents), buf_len(contents));
|
||||||
|
if (amt_written != buf_len(contents))
|
||||||
|
zig_panic("write failed: %s", strerror(errno));
|
||||||
|
if (close(fd) == -1)
|
||||||
|
zig_panic("close failed");
|
||||||
|
}
|
||||||
|
|
|
@ -12,8 +12,12 @@
|
||||||
#include "buffer.hpp"
|
#include "buffer.hpp"
|
||||||
|
|
||||||
void os_spawn_process(const char *exe, ZigList<const char *> &args, bool detached);
|
void os_spawn_process(const char *exe, ZigList<const char *> &args, bool detached);
|
||||||
|
void os_exec_process(const char *exe, ZigList<const char *> &args,
|
||||||
|
int *return_code, Buf *out_stderr, Buf *out_stdout);
|
||||||
|
|
||||||
void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename);
|
void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename);
|
||||||
|
|
||||||
|
void os_write_file(Buf *full_path, Buf *contents);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
int add(int a, int b);
|
|
|
@ -1,3 +0,0 @@
|
||||||
export fn add(a: i32, b: i32) -> i32 {
|
|
||||||
return a + b;
|
|
||||||
}
|
|
|
@ -0,0 +1,144 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015 Andrew Kelley
|
||||||
|
*
|
||||||
|
* This file is part of zig, which is MIT licensed.
|
||||||
|
* See http://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "list.hpp"
|
||||||
|
#include "buffer.hpp"
|
||||||
|
#include "os.hpp"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
struct TestSourceFile {
|
||||||
|
const char *relative_path;
|
||||||
|
const char *text;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TestCase {
|
||||||
|
const char *case_name;
|
||||||
|
const char *output;
|
||||||
|
const char *source;
|
||||||
|
ZigList<const char *> compile_errors;
|
||||||
|
ZigList<const char *> compiler_args;
|
||||||
|
ZigList<const char *> program_args;
|
||||||
|
};
|
||||||
|
|
||||||
|
ZigList<TestCase*> test_cases = {0};
|
||||||
|
const char *tmp_source_path = ".tmp_source.zig";
|
||||||
|
const char *tmp_exe_path = "./.tmp_exe";
|
||||||
|
|
||||||
|
static void add_simple_case(const char *case_name, const char *source, const char *output) {
|
||||||
|
TestCase *test_case = allocate<TestCase>(1);
|
||||||
|
test_case->case_name = case_name;
|
||||||
|
test_case->output = output;
|
||||||
|
test_case->source = source;
|
||||||
|
|
||||||
|
test_case->compiler_args.append("build");
|
||||||
|
test_case->compiler_args.append(tmp_source_path);
|
||||||
|
test_case->compiler_args.append("--output");
|
||||||
|
test_case->compiler_args.append(tmp_exe_path);
|
||||||
|
test_case->compiler_args.append("--release");
|
||||||
|
test_case->compiler_args.append("--strip");
|
||||||
|
|
||||||
|
test_cases.append(test_case);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_all_test_cases(void) {
|
||||||
|
add_simple_case("hello world with libc", R"SOURCE(
|
||||||
|
#link("c")
|
||||||
|
extern {
|
||||||
|
fn puts(s: *mut u8) -> i32;
|
||||||
|
fn exit(code: i32) -> unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _start() -> unreachable {
|
||||||
|
puts("Hello, world!");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
)SOURCE", "Hello, world!\n");
|
||||||
|
|
||||||
|
add_simple_case("function call", R"SOURCE(
|
||||||
|
#link("c")
|
||||||
|
extern {
|
||||||
|
fn puts(s: *mut u8) -> i32;
|
||||||
|
fn exit(code: i32) -> unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _start() -> unreachable {
|
||||||
|
this_is_a_function();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn this_is_a_function() -> unreachable {
|
||||||
|
puts("OK");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
)SOURCE", "OK\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void run_test(TestCase *test_case) {
|
||||||
|
os_write_file(buf_create_from_str(tmp_source_path), buf_create_from_str(test_case->source));
|
||||||
|
|
||||||
|
Buf zig_stderr = BUF_INIT;
|
||||||
|
Buf zig_stdout = BUF_INIT;
|
||||||
|
int return_code;
|
||||||
|
os_exec_process("./zig", test_case->compiler_args, &return_code, &zig_stderr, &zig_stdout);
|
||||||
|
|
||||||
|
if (return_code != 0) {
|
||||||
|
printf("\nCompile failed with return code %d:\n", return_code);
|
||||||
|
printf("zig");
|
||||||
|
for (int i = 0; i < test_case->compiler_args.length; i += 1) {
|
||||||
|
printf(" %s", test_case->compiler_args.at(i));
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
printf("%s\n", buf_ptr(&zig_stderr));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Buf program_stderr = BUF_INIT;
|
||||||
|
Buf program_stdout = BUF_INIT;
|
||||||
|
os_exec_process(tmp_exe_path, test_case->program_args, &return_code, &program_stderr, &program_stdout);
|
||||||
|
|
||||||
|
if (return_code != 0) {
|
||||||
|
printf("\nProgram exited with return code %d:\n", return_code);
|
||||||
|
printf("zig");
|
||||||
|
for (int i = 0; i < test_case->compiler_args.length; i += 1) {
|
||||||
|
printf(" %s", test_case->compiler_args.at(i));
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
printf("%s\n", buf_ptr(&program_stderr));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!buf_eql_str(&program_stdout, test_case->output)) {
|
||||||
|
printf("\n");
|
||||||
|
printf("==== Test failed. Expected output: ====\n");
|
||||||
|
printf("%s\n", test_case->output);
|
||||||
|
printf("========= Actual output: ==============\n");
|
||||||
|
printf("%s\n", buf_ptr(&program_stdout));
|
||||||
|
printf("=======================================\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void run_all_tests(void) {
|
||||||
|
for (int i = 0; i < test_cases.length; i += 1) {
|
||||||
|
TestCase *test_case = test_cases.at(i);
|
||||||
|
printf("Test %d/%d %s...", i + 1, test_cases.length, test_case->case_name);
|
||||||
|
run_test(test_case);
|
||||||
|
printf("OK\n");
|
||||||
|
}
|
||||||
|
printf("%d tests passed.\n", test_cases.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cleanup(void) {
|
||||||
|
remove(tmp_source_path);
|
||||||
|
remove(tmp_exe_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
add_all_test_cases();
|
||||||
|
run_all_tests();
|
||||||
|
cleanup();
|
||||||
|
}
|
Loading…
Reference in New Issue