commit
29b349398f
|
@ -2,7 +2,7 @@ name: "Build check gemstone in SDK"
|
||||||
run-name: SDK build check to ${{ inputs.deploy_target }} by @${{ github.actor }}
|
run-name: SDK build check to ${{ inputs.deploy_target }} by @${{ github.actor }}
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
env:
|
env:
|
||||||
SDK: 0.2.1-alpine-3.19.1
|
SDK: 0.2.2-alpine-3.19.1
|
||||||
jobs:
|
jobs:
|
||||||
build-check-sdk:
|
build-check-sdk:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@ -11,4 +11,4 @@ jobs:
|
||||||
- name: Setup SDK
|
- name: Setup SDK
|
||||||
run: docker pull servostar/gemstone:sdk-"$SDK" && docker build --tag gemstone:devkit-"$SDK" .
|
run: docker pull servostar/gemstone:sdk-"$SDK" && docker build --tag gemstone:devkit-"$SDK" .
|
||||||
- name: Compile
|
- name: Compile
|
||||||
run: docker run gemstone:devkit-"$SDK" make check
|
run: docker run gemstone:devkit-"$SDK" sh run-check-test.sh
|
||||||
|
|
|
@ -13,4 +13,5 @@ Makefile
|
||||||
lexer.ll.c
|
lexer.ll.c
|
||||||
parser.tab.c
|
parser.tab.c
|
||||||
parser.tab.h
|
parser.tab.h
|
||||||
build
|
build
|
||||||
|
/Testing/
|
||||||
|
|
|
@ -21,6 +21,15 @@ project(gemstone
|
||||||
DESCRIPTION "programming language compiler"
|
DESCRIPTION "programming language compiler"
|
||||||
LANGUAGES C)
|
LANGUAGES C)
|
||||||
|
|
||||||
|
set(GEMSTONE_TEST_DIR ${PROJECT_SOURCE_DIR}/tests)
|
||||||
|
set(GEMSTONE_BINARY_DIR ${PROJECT_SOURCE_DIR}/bin)
|
||||||
|
|
||||||
|
include(CTest)
|
||||||
|
|
||||||
|
if(BUILD_TESTING)
|
||||||
|
add_subdirectory(tests)
|
||||||
|
endif()
|
||||||
|
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
# ------------------------------------------------ #
|
# ------------------------------------------------ #
|
||||||
|
@ -79,7 +88,7 @@ add_executable(release
|
||||||
set_target_properties(release
|
set_target_properties(release
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
OUTPUT_NAME "gsc"
|
OUTPUT_NAME "gsc"
|
||||||
RUNTIME_OUTPUT_DIRECTORY "bin/release")
|
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/release)
|
||||||
|
|
||||||
# FIXME: cannot compile with /O2 because of /RTC1 flag
|
# FIXME: cannot compile with /O2 because of /RTC1 flag
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
|
@ -111,7 +120,7 @@ add_executable(debug
|
||||||
set_target_properties(debug
|
set_target_properties(debug
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
OUTPUT_NAME "gsc"
|
OUTPUT_NAME "gsc"
|
||||||
RUNTIME_OUTPUT_DIRECTORY "bin/debug")
|
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/debug)
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
set(DEBUG_FLAGS /DEBUG)
|
set(DEBUG_FLAGS /DEBUG)
|
||||||
|
@ -140,7 +149,7 @@ add_executable(check
|
||||||
set_target_properties(check
|
set_target_properties(check
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
OUTPUT_NAME "gsc"
|
OUTPUT_NAME "gsc"
|
||||||
RUNTIME_OUTPUT_DIRECTORY "bin/check")
|
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/check)
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
set(CHECK_FLAGS /DEBUG /WX)
|
set(CHECK_FLAGS /DEBUG /WX)
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
FROM servostar/gemstone:sdk-0.2.1-alpine-3.19.1
|
FROM servostar/gemstone:sdk-0.2.2-alpine-3.19.1
|
||||||
LABEL authors="servostar"
|
LABEL authors="servostar"
|
||||||
LABEL version="0.2.1"
|
LABEL version="0.2.2"
|
||||||
LABEL description="docker image for setting up the build pipeline on SDK"
|
LABEL description="docker image for setting up the build pipeline on SDK"
|
||||||
LABEL website="https://github.com/Servostar/gemstone"
|
LABEL website="https://github.com/Servostar/gemstone"
|
||||||
|
|
||||||
RUN git clone https://github.com/Servostar/gemstone.git /home/lorang
|
COPY --chown=lorang src /home/lorang/src
|
||||||
|
COPY --chown=lorang tests /home/lorang/tests
|
||||||
|
COPY --chown=lorang CMakeLists.txt /home/lorang/
|
||||||
|
COPY --chown=lorang run-check-test.sh /home/lorang/
|
||||||
|
|
||||||
RUN cmake .
|
RUN cmake .
|
||||||
|
|
22
README.md
22
README.md
|
@ -21,6 +21,28 @@ Requires:
|
||||||
- bison
|
- bison
|
||||||
- flex
|
- flex
|
||||||
|
|
||||||
|
## Writing Tests
|
||||||
|
|
||||||
|
Since the project is build and configured through CMake it makes sense to rely for tests
|
||||||
|
on CTest. All tests are located in the subfolder `tests`. In this directory is a CMakeLists.txt which specifies which tests
|
||||||
|
are to be run. Actual tests are located in folders within tests and contain a final CMakeLists.txt which specifies what to run
|
||||||
|
for a single test.
|
||||||
|
|
||||||
|
```
|
||||||
|
tests
|
||||||
|
└─ test_group1
|
||||||
|
└─ CMakeLists.txt # specify tests in this group
|
||||||
|
└─ ... # test files of group 1
|
||||||
|
|
||||||
|
└─ test_group2
|
||||||
|
└─ CMakeLists.txt # specify tests in this group
|
||||||
|
└─ ... # test files of group 2
|
||||||
|
|
||||||
|
└─ CMakeLists.txt # specify test groups to run
|
||||||
|
|
||||||
|
CMakeLists.txt # build configuration
|
||||||
|
```
|
||||||
|
|
||||||
## Development with VSCode/Codium
|
## Development with VSCode/Codium
|
||||||
|
|
||||||
Recommended extensions for getting a decent experience are the following:
|
Recommended extensions for getting a decent experience are the following:
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
# Author: Sven Vogel
|
||||||
|
# Created: 02.05.2024
|
||||||
|
# Description: Builds the project and runs tests
|
||||||
|
# Returns 0 on success and 1 when something went wrong
|
||||||
|
|
||||||
|
echo "+--------------------------------------+"
|
||||||
|
echo "| BUILDING all TARGETS |"
|
||||||
|
echo "+--------------------------------------+"
|
||||||
|
|
||||||
|
make -B
|
||||||
|
if [ ! $? -eq 0 ]; then
|
||||||
|
echo "===> failed to build targets"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "+--------------------------------------+"
|
||||||
|
echo "| RUNNING CODE CHECK |"
|
||||||
|
echo "+--------------------------------------+"
|
||||||
|
|
||||||
|
make check
|
||||||
|
if [ ! $? -eq 0 ]; then
|
||||||
|
echo "===> failed code check..."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "+--------------------------------------+"
|
||||||
|
echo "| RUNNING TESTS |"
|
||||||
|
echo "+--------------------------------------+"
|
||||||
|
|
||||||
|
ctest -VV --output-on-failure --schedule-random -j 4
|
||||||
|
if [ ! $? -eq 0 ]; then
|
||||||
|
echo "===> failed tests..."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "+--------------------------------------+"
|
||||||
|
echo "| COMPLETED CHECK + TESTS SUCCESSFULLY |"
|
||||||
|
echo "+--------------------------------------+"
|
|
@ -1,11 +1,11 @@
|
||||||
FROM alpine:3.19.1
|
FROM alpine:3.19.1
|
||||||
LABEL authors="servostar"
|
LABEL authors="servostar"
|
||||||
LABEL version="0.2.1"
|
LABEL version="0.2.2"
|
||||||
LABEL description="base image for building the gemstone programming language compiler"
|
LABEL description="base image for building the gemstone programming language compiler"
|
||||||
LABEL website="https://github.com/Servostar/gemstone"
|
LABEL website="https://github.com/Servostar/gemstone"
|
||||||
|
|
||||||
# install dependencies
|
# install dependencies
|
||||||
RUN apk add build-base gcc make cmake bison flex git
|
RUN apk add build-base gcc make cmake bison flex git python3
|
||||||
|
|
||||||
# create user for build
|
# create user for build
|
||||||
RUN adduser --disabled-password lorang
|
RUN adduser --disabled-password lorang
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
include(CTest)
|
||||||
|
|
||||||
|
set(PROJECT_BINARY_DIR bin)
|
||||||
|
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/tests)
|
||||||
|
set(CTEST_BINARY_DIRECTORY ${PROJECT_BINARY_DIR}/tests)
|
||||||
|
|
||||||
|
# Provide test to run here or include another CMakeLists.txt
|
||||||
|
|
||||||
|
add_subdirectory(logging)
|
||||||
|
add_subdirectory(input_file)
|
|
@ -0,0 +1,9 @@
|
||||||
|
include(CTest)
|
||||||
|
|
||||||
|
# ------------------------------------------------------- #
|
||||||
|
# CTEST 1
|
||||||
|
# test if the program accepts a file as input
|
||||||
|
|
||||||
|
add_test(NAME input_file_check
|
||||||
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/bin/check
|
||||||
|
COMMAND python ${GEMSTONE_TEST_DIR}/input_file/test_input_file.py ${GEMSTONE_TEST_DIR}/input_file/test.gem)
|
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
import "std.io"
|
||||||
|
|
||||||
|
fun main {
|
||||||
|
print("Hello, World!!!")
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
import os.path
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
from logging import info
|
||||||
|
|
||||||
|
|
||||||
|
def check_accept():
|
||||||
|
info("testing handling of input file...")
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
test_file_name = sys.argv[1]
|
||||||
|
|
||||||
|
p = subprocess.run(["./gsc", test_file_name], capture_output=True, text=True)
|
||||||
|
|
||||||
|
assert p.returncode == 0
|
||||||
|
|
||||||
|
|
||||||
|
def check_abort():
|
||||||
|
info("testing handling of missing input file...")
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
p = subprocess.run("./gsc", capture_output=True, text=True)
|
||||||
|
|
||||||
|
assert p.returncode == 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
info("check if binary exists...")
|
||||||
|
assert os.path.exists("./gsc")
|
||||||
|
|
||||||
|
check_accept()
|
||||||
|
check_abort()
|
|
@ -0,0 +1,63 @@
|
||||||
|
include(CTest)
|
||||||
|
|
||||||
|
include_directories(${PROJECT_SOURCE_DIR}/src)
|
||||||
|
|
||||||
|
# ------------------------------------------------------- #
|
||||||
|
# CTEST 1
|
||||||
|
# test the default output of the logger
|
||||||
|
|
||||||
|
add_executable(logging_output
|
||||||
|
${PROJECT_SOURCE_DIR}/src/sys/log.c
|
||||||
|
output.c)
|
||||||
|
set_target_properties(logging_output
|
||||||
|
PROPERTIES
|
||||||
|
OUTPUT_NAME "output"
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/logging)
|
||||||
|
add_test(NAME logging_output
|
||||||
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||||
|
COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_output)
|
||||||
|
|
||||||
|
# ------------------------------------------------------- #
|
||||||
|
# CTEST 2
|
||||||
|
# test the panic functionality of the logger
|
||||||
|
|
||||||
|
add_executable(logging_panic
|
||||||
|
${PROJECT_SOURCE_DIR}/src/sys/log.c
|
||||||
|
panic.c)
|
||||||
|
set_target_properties(logging_panic
|
||||||
|
PROPERTIES
|
||||||
|
OUTPUT_NAME "panic"
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/logging)
|
||||||
|
add_test(NAME logging_panic
|
||||||
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||||
|
COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_panic)
|
||||||
|
|
||||||
|
# ------------------------------------------------------- #
|
||||||
|
# CTEST 3
|
||||||
|
# test the ability to write to multiple output streams
|
||||||
|
|
||||||
|
add_executable(logging_streams
|
||||||
|
${PROJECT_SOURCE_DIR}/src/sys/log.c
|
||||||
|
streams.c)
|
||||||
|
set_target_properties(logging_streams
|
||||||
|
PROPERTIES
|
||||||
|
OUTPUT_NAME "stream"
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/logging)
|
||||||
|
add_test(NAME logging_streams
|
||||||
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||||
|
COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_stream)
|
||||||
|
|
||||||
|
# ------------------------------------------------------- #
|
||||||
|
# CTEST 4
|
||||||
|
# test compile time log level switch
|
||||||
|
|
||||||
|
add_executable(logging_level
|
||||||
|
${PROJECT_SOURCE_DIR}/src/sys/log.c
|
||||||
|
level.c)
|
||||||
|
set_target_properties(logging_level
|
||||||
|
PROPERTIES
|
||||||
|
OUTPUT_NAME "level"
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/logging)
|
||||||
|
add_test(NAME logging_level
|
||||||
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||||
|
COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_level)
|
|
@ -0,0 +1,18 @@
|
||||||
|
//
|
||||||
|
// Created by servostar on 5/2/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "sys/log.h"
|
||||||
|
|
||||||
|
#define LOG_LEVEL LOG_LEVEL_WARNING
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
log_init();
|
||||||
|
|
||||||
|
DEBUG("logging some debug...");
|
||||||
|
INFO("logging some info...");
|
||||||
|
WARN("logging some warning...");
|
||||||
|
ERROR("logging some error...");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
//
|
||||||
|
// Created by servostar on 5/1/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "sys/log.h"
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
log_init();
|
||||||
|
|
||||||
|
DEBUG("logging some debug...");
|
||||||
|
INFO("logging some info...");
|
||||||
|
WARN("logging some warning...");
|
||||||
|
ERROR("logging some error...");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
//
|
||||||
|
// Created by servostar on 5/2/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "sys/log.h"
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
log_init();
|
||||||
|
|
||||||
|
// this should appear in stderr
|
||||||
|
INFO("before exit");
|
||||||
|
|
||||||
|
PANIC("oooops something happened");
|
||||||
|
|
||||||
|
// this should NOT appear in stderr
|
||||||
|
// ^^^
|
||||||
|
ERROR("after exit");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
//
|
||||||
|
// Created by servostar on 5/2/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "sys/log.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
static FILE* file;
|
||||||
|
|
||||||
|
void close_file(void) {
|
||||||
|
if (file != NULL) {
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
log_init();
|
||||||
|
|
||||||
|
// this should appear in stderr
|
||||||
|
INFO("should only be in stderr");
|
||||||
|
|
||||||
|
file = fopen("tmp/test.log", "w");
|
||||||
|
if (file == NULL) {
|
||||||
|
PANIC("could not open file");
|
||||||
|
}
|
||||||
|
atexit(close_file);
|
||||||
|
|
||||||
|
log_register_stream(file);
|
||||||
|
|
||||||
|
INFO("should be in both");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
from logging import info, error
|
||||||
|
import os
|
||||||
|
|
||||||
|
BIN_DIR = "bin/tests/logging/"
|
||||||
|
|
||||||
|
|
||||||
|
def run_check_output():
|
||||||
|
info("started check output...")
|
||||||
|
|
||||||
|
p = subprocess.run(BIN_DIR + "output", capture_output=True, text=True)
|
||||||
|
|
||||||
|
info("checking exit code...")
|
||||||
|
|
||||||
|
# check exit code
|
||||||
|
assert p.returncode == 0
|
||||||
|
|
||||||
|
output = p.stderr
|
||||||
|
|
||||||
|
# check if logs appear in default log output (stderr)
|
||||||
|
info("checking stderr...")
|
||||||
|
|
||||||
|
assert "logging some debug..." in output
|
||||||
|
assert "logging some info..." in output
|
||||||
|
assert "logging some warning..." in output
|
||||||
|
assert "logging some error..." in output
|
||||||
|
|
||||||
|
|
||||||
|
def run_check_level():
|
||||||
|
info("started check level...")
|
||||||
|
|
||||||
|
p = subprocess.run(BIN_DIR + "level", capture_output=True, text=True)
|
||||||
|
|
||||||
|
info("checking exit code...")
|
||||||
|
|
||||||
|
# check exit code
|
||||||
|
assert p.returncode == 0
|
||||||
|
|
||||||
|
output = p.stderr
|
||||||
|
|
||||||
|
# check if logs appear in default log output (stderr)
|
||||||
|
info("checking stderr...")
|
||||||
|
|
||||||
|
assert "logging some debug..." not in output
|
||||||
|
assert "logging some info..." not in output
|
||||||
|
assert "logging some warning..." in output
|
||||||
|
assert "logging some error..." in output
|
||||||
|
|
||||||
|
|
||||||
|
def run_check_panic():
|
||||||
|
info("started check panic...")
|
||||||
|
|
||||||
|
p = subprocess.run(BIN_DIR + "panic", capture_output=True, text=True)
|
||||||
|
|
||||||
|
info("checking exit code...")
|
||||||
|
|
||||||
|
# check exit code
|
||||||
|
assert p.returncode == 1
|
||||||
|
|
||||||
|
output = p.stderr
|
||||||
|
|
||||||
|
# check if logs appear (not) in default log output (stderr)
|
||||||
|
info("checking stderr...")
|
||||||
|
|
||||||
|
assert "before exit" in output
|
||||||
|
assert "oooops something happened" in output
|
||||||
|
assert "after exit" not in output
|
||||||
|
|
||||||
|
|
||||||
|
def run_check_stream():
|
||||||
|
info("started check panic...")
|
||||||
|
|
||||||
|
info("creating temporary folder...")
|
||||||
|
|
||||||
|
if not os.path.exists("tmp"):
|
||||||
|
os.mkdir("tmp")
|
||||||
|
|
||||||
|
info("cleaning temporary folder...")
|
||||||
|
|
||||||
|
if os.path.exists("tmp/test.log"):
|
||||||
|
os.remove("tmp/test.log")
|
||||||
|
|
||||||
|
info("launching test binary...")
|
||||||
|
|
||||||
|
p = subprocess.run(BIN_DIR + "stream", capture_output=True, text=True)
|
||||||
|
|
||||||
|
info("checking exit code...")
|
||||||
|
|
||||||
|
# check exit code
|
||||||
|
assert p.returncode == 0
|
||||||
|
|
||||||
|
with open("tmp/test.log", "r") as file:
|
||||||
|
assert "should be in both" in "".join(file.readlines())
|
||||||
|
|
||||||
|
output = p.stderr
|
||||||
|
|
||||||
|
# check if logs appear (not) in default log output (stderr)
|
||||||
|
info("checking stderr...")
|
||||||
|
|
||||||
|
assert "should only be in stderr" in output
|
||||||
|
assert "should be in both" in output
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
target = sys.argv[1]
|
||||||
|
|
||||||
|
info(f"starting logging test suite with target: {target}")
|
||||||
|
|
||||||
|
match target:
|
||||||
|
case "check_output":
|
||||||
|
run_check_output()
|
||||||
|
case "check_panic":
|
||||||
|
run_check_panic()
|
||||||
|
case "check_stream":
|
||||||
|
run_check_stream()
|
||||||
|
case "check_level":
|
||||||
|
run_check_level()
|
||||||
|
case _:
|
||||||
|
error(f"unknown target: {target}")
|
||||||
|
exit(1)
|
Loading…
Reference in New Issue