Add support for runtime compiler tests
This commit is contained in:
parent
0a574d5f25
commit
038bf419ff
@ -29,8 +29,8 @@ deploy:
|
|||||||
secure: gDAuRNlF2uVhVHyZtJrX6MwNxnkfkQzohrC/6UcKAgqt+NKs4vZyq5FzTUceiDAkB0se70ZVx08e9ibAiXP/b7D1MPkAEiRxt9J6Vu3x6Bi1kPPuK5RfjFeT3gc1SbrULAP8Nz0NdU0chUhei6/V5NGhTegwp925DJOISq7+Ibw=
|
secure: gDAuRNlF2uVhVHyZtJrX6MwNxnkfkQzohrC/6UcKAgqt+NKs4vZyq5FzTUceiDAkB0se70ZVx08e9ibAiXP/b7D1MPkAEiRxt9J6Vu3x6Bi1kPPuK5RfjFeT3gc1SbrULAP8Nz0NdU0chUhei6/V5NGhTegwp925DJOISq7+Ibw=
|
||||||
file_glob: true
|
file_glob: true
|
||||||
file:
|
file:
|
||||||
- 'pawnc-*-linux.tar.gz'
|
- 'build/pawnc-*-linux.tar.gz'
|
||||||
- 'pawnc-*-macos.zip'
|
- 'build/pawnc-*-macos.zip'
|
||||||
draft: true
|
draft: true
|
||||||
skip_cleanup: true
|
skip_cleanup: true
|
||||||
on:
|
on:
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
project(pawn C)
|
project(pawn C)
|
||||||
cmake_minimum_required(VERSION 2.8)
|
cmake_minimum_required(VERSION 2.8)
|
||||||
|
|
||||||
|
set(OUTPUT_DIR ${CMAKE_BINARY_DIR}/products)
|
||||||
|
add_subdirectory(source/compiler ${OUTPUT_DIR})
|
||||||
|
|
||||||
# For the compiler we only utilize headers from source/amx, i.e. we don't
|
# For the compiler we only utilize headers from source/amx, i.e. we don't
|
||||||
# actually build any of the AMX modules.
|
# actually build any of the AMX modules.
|
||||||
# add_subdirectory(source/amx)
|
if(BUILD_AMX)
|
||||||
|
add_subdirectory(source/amx ${OUTPUT_DIR})
|
||||||
add_subdirectory(source/compiler)
|
endif()
|
||||||
|
@ -5,7 +5,8 @@ configuration:
|
|||||||
- RelWithDebInfo
|
- RelWithDebInfo
|
||||||
|
|
||||||
before_build:
|
before_build:
|
||||||
- cmake . -G "Visual Studio 14 2015" -DCPACK_GENERATOR=ZIP -DCMAKE_GENERATOR_TOOLSET=v100
|
- mkdir build && cd build
|
||||||
|
- cmake ../source -G "Visual Studio 14 2015" -DCPACK_GENERATOR=ZIP -DCMAKE_GENERATOR_TOOLSET=v100
|
||||||
|
|
||||||
build_script:
|
build_script:
|
||||||
- cmake --build . --config %CONFIGURATION%
|
- cmake --build . --config %CONFIGURATION%
|
||||||
@ -15,7 +16,7 @@ test_script:
|
|||||||
- cmake --build . --config %CONFIGURATION% --target pawncc_tests
|
- cmake --build . --config %CONFIGURATION% --target pawncc_tests
|
||||||
|
|
||||||
artifacts:
|
artifacts:
|
||||||
- path: pawnc-*-windows.zip
|
- path: build/pawnc-*-windows.zip
|
||||||
name: Binary package
|
name: Binary package
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
|
@ -139,6 +139,9 @@ target_link_libraries(pawncc pawnc)
|
|||||||
# The Pawn disassembler
|
# The Pawn disassembler
|
||||||
set(PAWNDISASM_SRCS
|
set(PAWNDISASM_SRCS
|
||||||
pawndisasm.c
|
pawndisasm.c
|
||||||
|
sc.h
|
||||||
|
../amx/amx.h
|
||||||
|
../amx/amxdbg.h
|
||||||
../amx/amxdbg.c
|
../amx/amxdbg.c
|
||||||
)
|
)
|
||||||
add_executable(pawndisasm ${PAWNDISASM_SRCS})
|
add_executable(pawndisasm ${PAWNDISASM_SRCS})
|
||||||
@ -161,6 +164,15 @@ if(MSVC)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Generate targets for running compiler tests
|
# Generate targets for running compiler tests
|
||||||
|
set(PAWNRUNS_SRCS
|
||||||
|
pawnruns.c
|
||||||
|
../amx/amx.c
|
||||||
|
../amx/amx.h
|
||||||
|
../amx/amxaux.c
|
||||||
|
../amx/amxaux.h
|
||||||
|
../amx/amxcons.c
|
||||||
|
../amx/amxcore.c)
|
||||||
|
add_executable(pawnruns ${PAWNRUNS_SRCS})
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
|
|
||||||
# Generate a binary package with CPack
|
# Generate a binary package with CPack
|
||||||
|
55
source/compiler/pawnruns.c
Normal file
55
source/compiler/pawnruns.c
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/* A simpler runner based on source/amx/pawnrun/prun1.c.
|
||||||
|
*
|
||||||
|
* Copyright (c) ITB CompuPhase, 2001-2005
|
||||||
|
*
|
||||||
|
* This file may be freely used. No warranties of any kind.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h> /* for exit() */
|
||||||
|
#include <signal.h>
|
||||||
|
#include <string.h> /* for memset() (on some compilers) */
|
||||||
|
#include "../amx/amx.h"
|
||||||
|
#include "../amx/amxaux.h"
|
||||||
|
|
||||||
|
static void ErrorExit(AMX *amx, int errorcode)
|
||||||
|
{
|
||||||
|
printf("Run time error %d: \"%s\"\n", errorcode, aux_StrError(errorcode));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintUsage(char *program)
|
||||||
|
{
|
||||||
|
printf("Usage: %s <filename>\n<filename> is a compiled script.\n", program);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc,char *argv[])
|
||||||
|
{
|
||||||
|
extern AMX_NATIVE_INFO console_Natives[];
|
||||||
|
extern AMX_NATIVE_INFO core_Natives[];
|
||||||
|
|
||||||
|
AMX amx;
|
||||||
|
cell ret = 0;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (argc != 2)
|
||||||
|
PrintUsage(argv[0]);
|
||||||
|
|
||||||
|
err = aux_LoadProgram(&amx, argv[1], NULL);
|
||||||
|
if (err != AMX_ERR_NONE)
|
||||||
|
ErrorExit(&amx, err);
|
||||||
|
|
||||||
|
amx_Register(&amx, console_Natives, -1);
|
||||||
|
err = amx_Register(&amx, core_Natives, -1);
|
||||||
|
if (err != AMX_ERR_NONE)
|
||||||
|
ErrorExit(&amx, err);
|
||||||
|
|
||||||
|
err = amx_Exec(&amx, &ret, AMX_EXEC_MAIN);
|
||||||
|
if (err != AMX_ERR_NONE)
|
||||||
|
ErrorExit(&amx, err);
|
||||||
|
printf("%s returns %ld\n", argv[1], (long)ret);
|
||||||
|
|
||||||
|
aux_FreeProgram(&amx);
|
||||||
|
return 0;
|
||||||
|
}
|
@ -1,9 +1,14 @@
|
|||||||
find_package(PythonInterp 2.7 REQUIRED)
|
find_package(PythonInterp 2.7)
|
||||||
|
|
||||||
|
if(PYTHONINTERP_FOUND)
|
||||||
add_custom_target(pawncc_tests COMMAND
|
add_custom_target(pawncc_tests COMMAND
|
||||||
COMMAND ${PYTHON_EXECUTABLE}
|
COMMAND ${PYTHON_EXECUTABLE}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/run_tests.py
|
${CMAKE_CURRENT_SOURCE_DIR}/run_tests.py
|
||||||
-c $<TARGET_FILE:pawncc>
|
-c $<TARGET_FILE:pawncc>
|
||||||
|
-r $<TARGET_FILE:pawnruns>
|
||||||
-i ../../../include
|
-i ../../../include
|
||||||
DEPENDS pawncc
|
DEPENDS pawncc
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
else()
|
||||||
|
message("Python was not found, you will not be able to run the compiler tests")
|
||||||
|
endif()
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
'test_type': 'output_check',
|
'test_type': 'output_check',
|
||||||
'errors': """
|
'errors': """
|
||||||
md_array_crash_gh_220.pwn(6) : fatal error 111: user error: OK
|
md_array_crash_gh_220.pwn(6) : fatal error 111: user error: OK
|
||||||
|
|
||||||
Compilation aborted.
|
Compilation aborted.
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
@ -10,11 +10,14 @@ import sys
|
|||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument('-c', '--compiler',
|
parser.add_argument('-c', '--compiler',
|
||||||
required=True,
|
required=True,
|
||||||
help='path to the pawncc executable')
|
help='path to compiler executable (pawncc or pawncc.exe)')
|
||||||
parser.add_argument('-i', '--include',
|
parser.add_argument('-i', '--include',
|
||||||
dest='include_dirs',
|
dest='include_dirs',
|
||||||
action='append',
|
action='append',
|
||||||
help='add specified directory to include path')
|
help='add custom include directories for compile tests')
|
||||||
|
parser.add_argument('-r', '--runner',
|
||||||
|
required=True,
|
||||||
|
help='path to runner executable (pawnrun or pawnrun.exe)')
|
||||||
options = parser.parse_args(sys.argv[1:])
|
options = parser.parse_args(sys.argv[1:])
|
||||||
|
|
||||||
def run_compiler(args):
|
def run_compiler(args):
|
||||||
@ -30,11 +33,7 @@ def run_compiler(args):
|
|||||||
stderr=subprocess.PIPE)
|
stderr=subprocess.PIPE)
|
||||||
|
|
||||||
class OutputCheckTest:
|
class OutputCheckTest:
|
||||||
def __init__(self,
|
def __init__(self, name, source_file, errors=None, extra_args=None):
|
||||||
name,
|
|
||||||
source_file,
|
|
||||||
errors=None,
|
|
||||||
extra_args=None):
|
|
||||||
self.name = name
|
self.name = name
|
||||||
self.source_file = source_file
|
self.source_file = source_file
|
||||||
self.errors = errors
|
self.errors = errors
|
||||||
@ -54,19 +53,14 @@ class OutputCheckTest:
|
|||||||
No errors specified and process exited with non-zero status
|
No errors specified and process exited with non-zero status
|
||||||
"""
|
"""
|
||||||
else:
|
else:
|
||||||
errors = stderr.decode('utf-8').splitlines()
|
errors = stderr.decode('utf-8').strip(' \t\r\n').replace('\r', '')
|
||||||
errors = [e.strip() for e in errors if e.strip()]
|
expected_errors = self.errors.strip(' \t\r\n').replace('\r', '')
|
||||||
expected_errors = self.errors.splitlines()
|
|
||||||
expected_errors = [e.strip() for e in expected_errors if e.strip()]
|
|
||||||
if errors != expected_errors:
|
if errors != expected_errors:
|
||||||
result = False
|
result = False
|
||||||
self.fail_reason = (
|
self.fail_reason = (
|
||||||
'Error output didn\'t match\n\nExpected errors:\n\n{}\n\n'
|
'Error output didn\'t match\n\nExpected errors:\n\n{}\n\n'
|
||||||
'Actual errors:\n\n{}'
|
'Actual errors:\n\n{}'
|
||||||
).format(
|
).format(expected_errors, errors)
|
||||||
'\n'.join(expected_errors).strip(' \t\r\n'),
|
|
||||||
'\n'.join(errors).strip(' \t\r\n')
|
|
||||||
)
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
class CrashTest:
|
class CrashTest:
|
||||||
@ -79,10 +73,37 @@ class CrashTest:
|
|||||||
# TODO: Check if the process crashed.
|
# TODO: Check if the process crashed.
|
||||||
return True
|
return True
|
||||||
|
|
||||||
test_types = {
|
class RuntimeTest:
|
||||||
'output_check': OutputCheckTest,
|
def __init__(self, name, amx_file, output, should_fail):
|
||||||
'crash': CrashTest
|
self.name = name
|
||||||
}
|
self.amx_file = amx_file
|
||||||
|
self.output = output
|
||||||
|
self.should_fail = should_fail
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
process = subprocess.Popen([options.runner, self.amx_file],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE)
|
||||||
|
stdout, stderr = process.communicate()
|
||||||
|
output = ''
|
||||||
|
if stdout is not None:
|
||||||
|
output = stdout.decode('utf-8')
|
||||||
|
if stderr is not None:
|
||||||
|
output += stderr.decode('utf-8')
|
||||||
|
output = output.strip(' \t\r\n').replace('\r', '')
|
||||||
|
expected_output = self.output.strip(' \t\r\n').replace('\r', '')
|
||||||
|
if not self.should_fail and process.returncode != 0:
|
||||||
|
self.fail_reason = (
|
||||||
|
'Runner exited with status {}\n\nOutput: {}'
|
||||||
|
).format(process.returncode, output)
|
||||||
|
return False
|
||||||
|
if output != expected_output:
|
||||||
|
self.fail_reason = (
|
||||||
|
'Output didn\'t match\n\nExpected output:\n\n{}\n\n'
|
||||||
|
'Actual output:\n\n{}'
|
||||||
|
).format(expected_output, output)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
tests = []
|
tests = []
|
||||||
num_tests_disabled = 0
|
num_tests_disabled = 0
|
||||||
@ -100,6 +121,11 @@ for meta_file in glob.glob('*.meta'):
|
|||||||
extra_args=metadata.get('extra_args')))
|
extra_args=metadata.get('extra_args')))
|
||||||
elif test_type == 'crash':
|
elif test_type == 'crash':
|
||||||
tests.append(CrashTest(name=name, source_file=name + '.pwn'))
|
tests.append(CrashTest(name=name, source_file=name + '.pwn'))
|
||||||
|
elif test_type == 'runtime':
|
||||||
|
tests.append(RuntimeTest(name=name,
|
||||||
|
amx_file=name + '.amx',
|
||||||
|
output=metadata.get('output'),
|
||||||
|
should_fail=metadata.get('should_fail')))
|
||||||
else:
|
else:
|
||||||
raise KeyError('Unknown test type: ' + test_type)
|
raise KeyError('Unknown test type: ' + test_type)
|
||||||
|
|
||||||
|
7
source/compiler/tests/runtime_test_example.meta
Normal file
7
source/compiler/tests/runtime_test_example.meta
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
'test_type': 'runtime',
|
||||||
|
'output': """
|
||||||
|
Run time error 4: "Array index out of bounds"
|
||||||
|
""",
|
||||||
|
'should_fail': True
|
||||||
|
}
|
10
source/compiler/tests/runtime_test_example.pwn
Normal file
10
source/compiler/tests/runtime_test_example.pwn
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#include <console>
|
||||||
|
|
||||||
|
main() {
|
||||||
|
new a[1] = {1};
|
||||||
|
new i = 1;
|
||||||
|
|
||||||
|
// When compiled with at least debug level 1 (default) the line below should produce:
|
||||||
|
// Run time error 4: "Array index out of bounds"
|
||||||
|
printf("%d", a[i]);
|
||||||
|
}
|
@ -6,6 +6,7 @@ too_many_args_crash_gh_298.pwn(2) : warning 215: expression has no effect
|
|||||||
too_many_args_crash_gh_298.pwn(2) : error 001: expected token: ";", but found ")"
|
too_many_args_crash_gh_298.pwn(2) : error 001: expected token: ";", but found ")"
|
||||||
too_many_args_crash_gh_298.pwn(2) : error 029: invalid expression, assumed zero
|
too_many_args_crash_gh_298.pwn(2) : error 029: invalid expression, assumed zero
|
||||||
too_many_args_crash_gh_298.pwn(2) : fatal error 107: too many error messages on one line
|
too_many_args_crash_gh_298.pwn(2) : fatal error 107: too many error messages on one line
|
||||||
|
|
||||||
Compilation aborted.
|
Compilation aborted.
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user