Migrate to CMake.
parent
5ad9ed7f06
commit
5dd6035dcd
|
@ -10,7 +10,7 @@ STAGED=$(
|
||||||
TARGETS=()
|
TARGETS=()
|
||||||
while IFS= read -r FILENAME
|
while IFS= read -r FILENAME
|
||||||
do
|
do
|
||||||
if [[ "$FILENAME" =~ .*\.c$ ]] || [[ "$FILENAME" == .*\.h$ ]]; then
|
if [[ "$FILENAME" =~ .*\.c(pp)?$ ]] || [[ "$FILENAME" =~ .*\.h(pp)?$ ]]; then
|
||||||
TARGETS+=("${FILENAME}")
|
TARGETS+=("${FILENAME}")
|
||||||
fi
|
fi
|
||||||
done <<< "$STAGED"
|
done <<< "$STAGED"
|
||||||
|
|
|
@ -1,17 +1,27 @@
|
||||||
# Directory used by clangd LSP.
|
# Directory used by clangd LSP.
|
||||||
.cache/
|
/.cache/
|
||||||
|
|
||||||
# Directory used by `direnv` to hold `use flake`-generated profiles.
|
# Directory used by `direnv` to hold `use flake`-generated profiles.
|
||||||
.direnv/
|
/.direnv/
|
||||||
|
|
||||||
# The compilation database produced by `bear`.
|
|
||||||
compile_commands.json
|
|
||||||
|
|
||||||
# The directory containing all build outputs.
|
# The directory containing all build outputs.
|
||||||
dist/
|
/build/
|
||||||
|
|
||||||
# The directory generated by `Doxygen`.
|
# The directory generated by `Doxygen`.
|
||||||
docs/
|
/docs/
|
||||||
|
|
||||||
# A symlink produced by default when running `nix build`.
|
# A symlink produced by default when running `nix build`.
|
||||||
result
|
result
|
||||||
|
|
||||||
|
# Files generated by CMake.
|
||||||
|
CMakeLists.txt.user
|
||||||
|
CMakeCache.txt
|
||||||
|
CMakeFiles
|
||||||
|
CMakeScripts
|
||||||
|
Testing
|
||||||
|
Makefile
|
||||||
|
cmake_install.cmake
|
||||||
|
install_manifest.txt
|
||||||
|
compile_commands.json
|
||||||
|
CTestTestfile.cmake
|
||||||
|
_deps
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
cmake_minimum_required(VERSION 3.19)
|
||||||
|
project(bootstrap VERSION 0.1.3 LANGUAGES C)
|
||||||
|
|
||||||
|
add_executable(bootstrap main.c)
|
||||||
|
|
||||||
|
add_subdirectory(src)
|
||||||
|
target_link_libraries(bootstrap PUBLIC m)
|
||||||
|
target_include_directories(bootstrap PUBLIC include)
|
||||||
|
|
||||||
|
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
|
||||||
|
set_target_properties(
|
||||||
|
bootstrap PROPERTIES
|
||||||
|
# The top-level compile_commands.json is a symbolic link to this file.
|
||||||
|
EXPORT_COMPILE_COMMANDS ON
|
||||||
|
# Enable so that tests can link against the primary executable.
|
||||||
|
ENABLE_EXPORTS ON
|
||||||
|
)
|
||||||
|
enable_testing()
|
||||||
|
add_subdirectory(test)
|
||||||
|
endif()
|
81
Makefile
81
Makefile
|
@ -1,81 +0,0 @@
|
||||||
# ============================================================
|
|
||||||
# Configuration
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
# To create a release build, run `make BUILD=release`.
|
|
||||||
|
|
||||||
BUILD := debug
|
|
||||||
PREFIX := ${CURDIR}/dist/${BUILD}
|
|
||||||
OUT := bootstrap
|
|
||||||
|
|
||||||
CCFLAGS.debug := -DDEBUG -g -Og
|
|
||||||
CCFLAGS.release := -DNDEBUG
|
|
||||||
LDFLAGS := -lm
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# Build
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
COMPILE := ${CC} ${CCFLAGS.${BUILD}} -I include src/*.c main.c -o ${PREFIX}/${OUT} ${LDFLAGS}
|
|
||||||
|
|
||||||
all: build all.${BUILD}
|
|
||||||
all.debug: bear
|
|
||||||
all.release:
|
|
||||||
|
|
||||||
build: ${PREFIX}/${OUT}
|
|
||||||
|
|
||||||
${PREFIX}/${OUT}: ${PREFIX} include/*.h src/*.c
|
|
||||||
${COMPILE}
|
|
||||||
|
|
||||||
${PREFIX}:
|
|
||||||
mkdir -p $@
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# Compilation Database
|
|
||||||
#
|
|
||||||
# Generate a compilation database using [Bear](https://github.com/rizsotto/Bear).
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
bear: compile_commands.json
|
|
||||||
|
|
||||||
compile_commands.json: include/*.h src/*.c main.c
|
|
||||||
# This file is only constructed in debug mode. If interested in expanding this
|
|
||||||
# generation to other build types, add a release-specific dependency.
|
|
||||||
ifeq ($(BUILD), debug)
|
|
||||||
mkdir -p dist/debug
|
|
||||||
bear -- ${COMPILE}
|
|
||||||
endif
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# Documentation.
|
|
||||||
#
|
|
||||||
# Generate documentation using [Doxygen](https://www.doxygen.nl/index.html).
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
docs: docs/index.html
|
|
||||||
|
|
||||||
# The `index.html` file is regenerated on each invocation of `doxygen`.
|
|
||||||
docs/index.html: Doxyfile include/*.h src/*.c
|
|
||||||
doxygen
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# Testing.
|
|
||||||
#
|
|
||||||
# We use [Sput](https://www.use-strict.de/sput-unit-testing/) to run tests.
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
test: dist/test/suites
|
|
||||||
dist/test/suites
|
|
||||||
|
|
||||||
dist/test/suites: include/*.h src/*.c test/*.h test/*.c
|
|
||||||
mkdir -p dist/test
|
|
||||||
${CC} ${CCFLAGS.debug} -I include src/*.c test/*.c -o dist/test/suites ${LDFLAGS}
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
# Other
|
|
||||||
# ============================================================
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -r ${PREFIX}
|
|
||||||
|
|
||||||
.PHONY: test
|
|
51
README.md
51
README.md
|
@ -70,15 +70,6 @@ If flakes is not enabled or your nix version does not support
|
||||||
})).packages.${system}.default;
|
})).packages.${system}.default;
|
||||||
```
|
```
|
||||||
|
|
||||||
### Source
|
|
||||||
|
|
||||||
If you do not have Nix or prefer building from source, clone this repository and
|
|
||||||
run
|
|
||||||
```bash
|
|
||||||
$ make BUILD=release
|
|
||||||
```
|
|
||||||
The `bootstrap` binary will be made available in `dist/release` by default.
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Runners
|
### Runners
|
||||||
|
@ -220,23 +211,49 @@ functionality (or lack thereof) reflects my own needs as I have come across
|
||||||
them. If interested in adding more capabilities, please send a PR or just fork
|
them. If interested in adding more capabilities, please send a PR or just fork
|
||||||
the project for your own purposes.
|
the project for your own purposes.
|
||||||
|
|
||||||
|
### Building
|
||||||
|
|
||||||
|
We use [CMake](https://cmake.org/) (version 3.27.7) to build the project. If a
|
||||||
|
`build/` directory does not already exist, run the following:
|
||||||
|
```bash
|
||||||
|
$ mkdir -p build/{Debug,Release}
|
||||||
|
$ pushd build/Debug && cmake -DCMAKE_BUILD_TYPE=Debug ../.. && popd
|
||||||
|
$ pushd build/Release && cmake -DCMAKE_BUILD_TYPE=Release ../.. && popd
|
||||||
|
```
|
||||||
|
These commands will create a CMake cache file in each subdirectory with the
|
||||||
|
build types set. Now you can build a `Debug` or `Release` variant by navigating
|
||||||
|
to the corresponding subdirectory and running:
|
||||||
|
```bash
|
||||||
|
$ cmake --build .
|
||||||
|
```
|
||||||
|
|
||||||
|
The [clangd](https://clangd.llvm.org/) LSP (version 14.0.6) is included in this
|
||||||
|
flake. The [codelldb](https://github.com/vadimcn/codelldb) VSCode plugin is also
|
||||||
|
included to interface with the LSP. Note this plugin, despite its name, is
|
||||||
|
compatible with other editors (e.g. neovim). To configure, refer to your
|
||||||
|
editor's documentation.
|
||||||
|
|
||||||
|
To use the LSP across files, a
|
||||||
|
[compilation database](https://clang.llvm.org/docs/JSONCompilationDatabase.html)
|
||||||
|
must be generated. The `CMakeLists.txt` file already enables this in the Debug
|
||||||
|
configuration type. A top-level `compile_commands.json` symbolic link already
|
||||||
|
exists and points to this generated database.
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
|
|
||||||
We use [Sput](https://www.use-strict.de/sput-unit-testing/) for unit tests. To
|
We use [CTest](https://cmake.org/cmake/help/latest/module/CTest.html) (version
|
||||||
run tests, type:
|
3.27.7) for unit testing. To run the tests, navigate to `build/Debug` and type
|
||||||
|
the following:
|
||||||
```bash
|
```bash
|
||||||
|
$ cmake --build .
|
||||||
$ make test
|
$ make test
|
||||||
```
|
```
|
||||||
Tests are located in the `test` directory. `test/suites.c` serves as the
|
|
||||||
entrypoint for the test runner.
|
|
||||||
|
|
||||||
### Documentation
|
### Documentation
|
||||||
|
|
||||||
We use [doxygen](https://www.doxygen.nl/index.html) for documentation
|
We use [doxygen](https://www.doxygen.nl/index.html) for documentation
|
||||||
generation. Run either of the following two commands to generate documentation
|
generation. Run the following command to generate documentation locally:
|
||||||
locally:
|
|
||||||
```bash
|
```bash
|
||||||
$ make docs
|
|
||||||
$ doxygen
|
$ doxygen
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -245,10 +262,8 @@ $ doxygen
|
||||||
We use `clang-format` to ensure consistent formatting. A `pre-commit` file is
|
We use `clang-format` to ensure consistent formatting. A `pre-commit` file is
|
||||||
included in `.githooks` to enforce usage. Run the following to configure `git`
|
included in `.githooks` to enforce usage. Run the following to configure `git`
|
||||||
to use it:
|
to use it:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git config --local core.hooksPath .githooks/
|
git config --local core.hooksPath .githooks/
|
||||||
```
|
```
|
||||||
|
|
||||||
If running [direnv](https://direnv.net/), this is done automatically upon
|
If running [direnv](https://direnv.net/), this is done automatically upon
|
||||||
entering the project directory.
|
entering the project directory.
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
pname = "bootstrap";
|
pname = "bootstrap";
|
||||||
src = ./.;
|
src = ./.;
|
||||||
version = "0.1.3";
|
version = "0.1.3";
|
||||||
makeFlags = [ "BUILD=release" "PREFIX=$(out)" ];
|
|
||||||
dontInstall = true;
|
dontInstall = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -40,8 +39,8 @@
|
||||||
stdenv = pkgs.clangStdenv;
|
stdenv = pkgs.clangStdenv;
|
||||||
} {
|
} {
|
||||||
packages = with pkgs; [
|
packages = with pkgs; [
|
||||||
bear
|
|
||||||
clang-tools
|
clang-tools
|
||||||
|
cmake
|
||||||
codelldb
|
codelldb
|
||||||
doxygen
|
doxygen
|
||||||
];
|
];
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
target_sources(
|
||||||
|
bootstrap PUBLIC
|
||||||
|
cJSON.c
|
||||||
|
config.c
|
||||||
|
dyn_array.c
|
||||||
|
error.c
|
||||||
|
evaluator.c
|
||||||
|
parser.c
|
||||||
|
string_buf.c
|
||||||
|
string_utils.c
|
||||||
|
validator.c
|
||||||
|
)
|
|
@ -87,5 +87,4 @@ void config_free(struct Config *config) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
free(config);
|
free(config);
|
||||||
config = 0;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,5 +36,4 @@ void dyn_array_free(struct DynArray *a) {
|
||||||
}
|
}
|
||||||
free(a->buf);
|
free(a->buf);
|
||||||
free(a);
|
free(a);
|
||||||
a = 0;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,5 +6,4 @@ void error_free(struct Error *error) {
|
||||||
}
|
}
|
||||||
free((void *)error->message);
|
free((void *)error->message);
|
||||||
free(error);
|
free(error);
|
||||||
error = 0;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,5 +60,4 @@ void string_buf_free(struct StringBuf *sb) {
|
||||||
}
|
}
|
||||||
free((void *)sb->buf);
|
free((void *)sb->buf);
|
||||||
free(sb);
|
free(sb);
|
||||||
sb = 0;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
file(
|
||||||
|
COPY ${CMAKE_CURRENT_SOURCE_DIR}/specs
|
||||||
|
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach(
|
||||||
|
exe
|
||||||
|
config
|
||||||
|
dyn_array
|
||||||
|
parser
|
||||||
|
string_buf
|
||||||
|
string_utils
|
||||||
|
validator
|
||||||
|
)
|
||||||
|
add_executable("test-${exe}" "test_${exe}.c")
|
||||||
|
target_link_libraries("test-${exe}" PRIVATE bootstrap)
|
||||||
|
target_include_directories("test-${exe}" PRIVATE bootstrap)
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# config
|
||||||
|
# ========================================
|
||||||
|
|
||||||
|
foreach(
|
||||||
|
arg
|
||||||
|
new-invalid-args
|
||||||
|
new-spec-not-found
|
||||||
|
new-spec-not-dir
|
||||||
|
new-success
|
||||||
|
)
|
||||||
|
add_test(NAME "config: ${arg}" COMMAND test-config ${arg})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# dyn_array
|
||||||
|
# ========================================
|
||||||
|
|
||||||
|
foreach(
|
||||||
|
arg
|
||||||
|
zero-capacity
|
||||||
|
nonzero-capacity
|
||||||
|
)
|
||||||
|
add_test(NAME "dyn_array: ${arg}" COMMAND test-dyn_array ${arg})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# parser
|
||||||
|
# ========================================
|
||||||
|
|
||||||
|
foreach(
|
||||||
|
arg
|
||||||
|
missing
|
||||||
|
minimal
|
||||||
|
invalid
|
||||||
|
)
|
||||||
|
add_test(NAME "parser: ${arg}" COMMAND test-parser ${arg})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# string_buf
|
||||||
|
# ========================================
|
||||||
|
|
||||||
|
foreach(
|
||||||
|
arg
|
||||||
|
sappend
|
||||||
|
cappend
|
||||||
|
)
|
||||||
|
add_test(NAME "string_buf: ${arg}" COMMAND test-string_buf ${arg})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# string_utils
|
||||||
|
# ========================================
|
||||||
|
|
||||||
|
foreach(
|
||||||
|
arg
|
||||||
|
join-single
|
||||||
|
join-multiple
|
||||||
|
strcmp-ci
|
||||||
|
trim-leading
|
||||||
|
trim-trailing
|
||||||
|
)
|
||||||
|
add_test(NAME "string_utils: ${arg}" COMMAND test-string_utils ${arg})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# ========================================
|
||||||
|
# validator
|
||||||
|
# ========================================
|
||||||
|
|
||||||
|
foreach(
|
||||||
|
arg
|
||||||
|
toplevel-not-object
|
||||||
|
field-not-object
|
||||||
|
field-name-leading-digit
|
||||||
|
field-name-non-alnum
|
||||||
|
field-type-invalid
|
||||||
|
field-type-unknown
|
||||||
|
valid-type-ci
|
||||||
|
field-required-invalid
|
||||||
|
field-required-valid
|
||||||
|
field-prompt-invalid
|
||||||
|
valid-no-required
|
||||||
|
field-type-yes
|
||||||
|
)
|
||||||
|
add_test(NAME "validator: ${arg}" COMMAND test-validator ${arg})
|
||||||
|
endforeach()
|
0
test/specs/invalid_spec_json/run.sh → test/specs/invalid_spec_json/runner
Normal file → Executable file
0
test/specs/invalid_spec_json/run.sh → test/specs/invalid_spec_json/runner
Normal file → Executable file
0
test/specs/minimal_spec_json/run.sh → test/specs/minimal_spec_json/runner
Normal file → Executable file
0
test/specs/minimal_spec_json/run.sh → test/specs/minimal_spec_json/runner
Normal file → Executable file
300
test/sput.h
300
test/sput.h
|
@ -1,300 +0,0 @@
|
||||||
/*
|
|
||||||
* sput - Simple, Portable Unit Testing Framework for C/C++ v1.4.0
|
|
||||||
*
|
|
||||||
* http://www.use-strict.de/sput-unit-testing/
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Copyright (C) 2011-2015 Lingua-Systems Software GmbH
|
|
||||||
* Copyright (C) 2016 Alex Linke <alex@use-strict.de>
|
|
||||||
*
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
||||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef HAVE_SPUT_H
|
|
||||||
#define HAVE_SPUT_H
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
/* ===================================================================
|
|
||||||
* definitions
|
|
||||||
* =================================================================== */
|
|
||||||
|
|
||||||
#define SPUT_VERSION_MAJOR 1
|
|
||||||
#define SPUT_VERSION_MINOR 4
|
|
||||||
#define SPUT_VERSION_PATCH 0
|
|
||||||
#define SPUT_VERSION_STRING "1.4.0"
|
|
||||||
|
|
||||||
#define SPUT_DEFAULT_SUITE_NAME "Unlabeled Suite"
|
|
||||||
#define SPUT_DEFAULT_CHECK_NAME "Unlabeled Check"
|
|
||||||
|
|
||||||
#define SPUT_INITIALIZED 0x06 /* ACK */
|
|
||||||
|
|
||||||
/* ===================================================================
|
|
||||||
* sput global variable
|
|
||||||
* =================================================================== */
|
|
||||||
|
|
||||||
static struct sput {
|
|
||||||
FILE *out;
|
|
||||||
char initialized;
|
|
||||||
|
|
||||||
struct sput_overall {
|
|
||||||
unsigned long checks;
|
|
||||||
unsigned long suites;
|
|
||||||
unsigned long ok;
|
|
||||||
unsigned long nok;
|
|
||||||
} overall;
|
|
||||||
|
|
||||||
struct sput_suite {
|
|
||||||
const char *name;
|
|
||||||
unsigned long nr;
|
|
||||||
unsigned long checks;
|
|
||||||
unsigned long ok;
|
|
||||||
unsigned long nok;
|
|
||||||
} suite;
|
|
||||||
|
|
||||||
struct sput_test {
|
|
||||||
const char *name;
|
|
||||||
unsigned long nr;
|
|
||||||
} test;
|
|
||||||
|
|
||||||
struct sput_check {
|
|
||||||
const char *name;
|
|
||||||
const char *cond;
|
|
||||||
const char *type;
|
|
||||||
unsigned long line;
|
|
||||||
} check;
|
|
||||||
|
|
||||||
struct sput_time {
|
|
||||||
time_t start;
|
|
||||||
time_t end;
|
|
||||||
} time;
|
|
||||||
} __sput;
|
|
||||||
|
|
||||||
/* ==================================================================
|
|
||||||
* sput internal macros
|
|
||||||
* ================================================================== */
|
|
||||||
|
|
||||||
#define _sput_die_unless_initialized() \
|
|
||||||
if (__sput.initialized != SPUT_INITIALIZED) { \
|
|
||||||
fputs("sput_start_testing() omitted\n", stderr); \
|
|
||||||
exit(EXIT_FAILURE); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define _sput_die_unless_suite_set() \
|
|
||||||
if (!__sput.suite.name) { \
|
|
||||||
fputs("sput_enter_suite() omitted\n", __sput.out); \
|
|
||||||
exit(EXIT_FAILURE); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define _sput_die_unless_test_set() \
|
|
||||||
if (!__sput.test.name) { \
|
|
||||||
fputs("sput_run_test() omitted\n", __sput.out); \
|
|
||||||
exit(EXIT_FAILURE); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define _sput_check_failed() \
|
|
||||||
{ \
|
|
||||||
_sput_die_unless_initialized(); \
|
|
||||||
_sput_die_unless_suite_set(); \
|
|
||||||
__sput.suite.nok++; \
|
|
||||||
fprintf( \
|
|
||||||
__sput.out, \
|
|
||||||
"[%lu:%lu] %s:#%lu \"%s\" FAIL\n" \
|
|
||||||
"! Type: %s\n" \
|
|
||||||
"! Condition: %s\n" \
|
|
||||||
"! Line: %lu\n", \
|
|
||||||
__sput.suite.nr, \
|
|
||||||
__sput.suite.checks, \
|
|
||||||
__sput.test.name, \
|
|
||||||
__sput.test.nr, \
|
|
||||||
__sput.check.name, \
|
|
||||||
__sput.check.type, \
|
|
||||||
__sput.check.cond, \
|
|
||||||
__sput.check.line \
|
|
||||||
); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define _sput_check_succeeded() \
|
|
||||||
{ \
|
|
||||||
_sput_die_unless_initialized(); \
|
|
||||||
_sput_die_unless_suite_set(); \
|
|
||||||
__sput.suite.ok++; \
|
|
||||||
fprintf( \
|
|
||||||
__sput.out, \
|
|
||||||
"[%lu:%lu] %s:#%lu \"%s\" pass\n", \
|
|
||||||
__sput.suite.nr, \
|
|
||||||
__sput.suite.checks, \
|
|
||||||
__sput.test.name, \
|
|
||||||
__sput.test.nr, \
|
|
||||||
__sput.check.name \
|
|
||||||
); \
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ==================================================================
|
|
||||||
* user macros
|
|
||||||
* ================================================================== */
|
|
||||||
|
|
||||||
#define sput_start_testing() \
|
|
||||||
do { \
|
|
||||||
memset(&__sput, 0, sizeof(__sput)); \
|
|
||||||
__sput.out = stdout; \
|
|
||||||
__sput.time.start = time(NULL); \
|
|
||||||
__sput.initialized = SPUT_INITIALIZED; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define sput_leave_suite() \
|
|
||||||
do { \
|
|
||||||
float failpls = 0.0f; \
|
|
||||||
_sput_die_unless_initialized(); \
|
|
||||||
_sput_die_unless_suite_set(); \
|
|
||||||
failpls = __sput.suite.checks \
|
|
||||||
? (float)((__sput.suite.nok * 100.0) / __sput.suite.checks) \
|
|
||||||
: 0.0f; \
|
|
||||||
fprintf( \
|
|
||||||
__sput.out, \
|
|
||||||
"\n--> %lu check(s), %lu ok, %lu failed (%.2f%%)\n", \
|
|
||||||
__sput.suite.checks, \
|
|
||||||
__sput.suite.ok, \
|
|
||||||
__sput.suite.nok, \
|
|
||||||
failpls \
|
|
||||||
); \
|
|
||||||
__sput.overall.checks += __sput.suite.checks; \
|
|
||||||
__sput.overall.ok += __sput.suite.ok; \
|
|
||||||
__sput.overall.nok += __sput.suite.nok; \
|
|
||||||
memset(&__sput.suite, 0, sizeof(__sput.suite)); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define sput_get_return_value() \
|
|
||||||
(__sput.overall.nok > 0 ? EXIT_FAILURE : EXIT_SUCCESS)
|
|
||||||
|
|
||||||
#define sput_enter_suite(_name) \
|
|
||||||
do { \
|
|
||||||
_sput_die_unless_initialized(); \
|
|
||||||
if (__sput.suite.name) { \
|
|
||||||
sput_leave_suite(); \
|
|
||||||
} \
|
|
||||||
__sput.suite.name = _name != NULL ? _name : SPUT_DEFAULT_SUITE_NAME; \
|
|
||||||
__sput.suite.nr = ++__sput.overall.suites; \
|
|
||||||
fprintf( \
|
|
||||||
__sput.out, \
|
|
||||||
"\n== Entering suite #%lu, \"%s\" ==\n\n", \
|
|
||||||
__sput.suite.nr, \
|
|
||||||
__sput.suite.name \
|
|
||||||
); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define sput_finish_testing() \
|
|
||||||
do { \
|
|
||||||
float failpft = 0.0f; \
|
|
||||||
_sput_die_unless_initialized(); \
|
|
||||||
if (__sput.suite.name) { \
|
|
||||||
sput_leave_suite(); \
|
|
||||||
} \
|
|
||||||
failpft = \
|
|
||||||
__sput.overall.checks \
|
|
||||||
? (float)((__sput.overall.nok * 100.0) / __sput.overall.checks) \
|
|
||||||
: 0.0f; \
|
|
||||||
__sput.time.end = time(NULL); \
|
|
||||||
fprintf( \
|
|
||||||
__sput.out, \
|
|
||||||
"\n==> %lu check(s) in %lu suite(s) finished after %.2f " \
|
|
||||||
"second(s),\n" \
|
|
||||||
" %lu succeeded, %lu failed (%.2f%%)\n" \
|
|
||||||
"\n[%s]\n", \
|
|
||||||
__sput.overall.checks, \
|
|
||||||
__sput.overall.suites, \
|
|
||||||
difftime(__sput.time.end, __sput.time.start), \
|
|
||||||
__sput.overall.ok, \
|
|
||||||
__sput.overall.nok, \
|
|
||||||
failpft, \
|
|
||||||
(sput_get_return_value() == EXIT_SUCCESS) ? "SUCCESS" : "FAILURE" \
|
|
||||||
); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define sput_set_output_stream(_fp) \
|
|
||||||
do { \
|
|
||||||
__sput.out = _fp != NULL ? _fp : stdout; \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define sput_fail_if(_cond, _name) \
|
|
||||||
do { \
|
|
||||||
_sput_die_unless_initialized(); \
|
|
||||||
_sput_die_unless_suite_set(); \
|
|
||||||
_sput_die_unless_test_set(); \
|
|
||||||
__sput.check.name = _name != NULL ? _name : SPUT_DEFAULT_CHECK_NAME; \
|
|
||||||
__sput.check.line = __LINE__; \
|
|
||||||
__sput.check.cond = #_cond; \
|
|
||||||
__sput.check.type = "fail-if"; \
|
|
||||||
__sput.test.nr++; \
|
|
||||||
__sput.suite.checks++; \
|
|
||||||
if ((_cond)) { \
|
|
||||||
_sput_check_failed(); \
|
|
||||||
} else { \
|
|
||||||
_sput_check_succeeded(); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define sput_fail_unless(_cond, _name) \
|
|
||||||
do { \
|
|
||||||
_sput_die_unless_initialized(); \
|
|
||||||
_sput_die_unless_suite_set(); \
|
|
||||||
_sput_die_unless_test_set(); \
|
|
||||||
__sput.check.name = _name != NULL ? _name : SPUT_DEFAULT_CHECK_NAME; \
|
|
||||||
__sput.check.line = __LINE__; \
|
|
||||||
__sput.check.cond = #_cond; \
|
|
||||||
__sput.check.type = "fail-unless"; \
|
|
||||||
__sput.test.nr++; \
|
|
||||||
__sput.suite.checks++; \
|
|
||||||
if (!(_cond)) { \
|
|
||||||
_sput_check_failed(); \
|
|
||||||
} else { \
|
|
||||||
_sput_check_succeeded(); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define sput_run_test(_func) \
|
|
||||||
do { \
|
|
||||||
_sput_die_unless_initialized(); \
|
|
||||||
_sput_die_unless_suite_set(); \
|
|
||||||
memset(&__sput.test, 0, sizeof(__sput.test)); \
|
|
||||||
__sput.test.name = #_func; \
|
|
||||||
_func(); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* HAVE_SPUT_H */
|
|
||||||
|
|
||||||
/* vim: set ft=c sts=4 sw=4 ts=4 ai et: */
|
|
|
@ -1,55 +0,0 @@
|
||||||
#include "sput.h"
|
|
||||||
#include "test_config.h"
|
|
||||||
#include "test_dyn_array.h"
|
|
||||||
#include "test_parser.h"
|
|
||||||
#include "test_string_buf.h"
|
|
||||||
#include "test_string_utils.h"
|
|
||||||
#include "test_validator.h"
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
sput_start_testing();
|
|
||||||
|
|
||||||
sput_enter_suite("config");
|
|
||||||
sput_run_test(test_config_new_invalid_args);
|
|
||||||
sput_run_test(test_config_new_spec_not_found);
|
|
||||||
sput_run_test(test_config_new_spec_not_dir);
|
|
||||||
sput_run_test(test_config_new_success);
|
|
||||||
|
|
||||||
sput_enter_suite("dyn_array");
|
|
||||||
sput_run_test(test_dyn_array_zero_capacity);
|
|
||||||
sput_run_test(test_dyn_array_nonzero_capacity);
|
|
||||||
|
|
||||||
sput_enter_suite("string_buf");
|
|
||||||
sput_run_test(test_string_buf_sappend);
|
|
||||||
sput_run_test(test_string_buf_cappend);
|
|
||||||
|
|
||||||
sput_enter_suite("string_utils");
|
|
||||||
sput_run_test(test_join_single);
|
|
||||||
sput_run_test(test_join_multiple);
|
|
||||||
sput_run_test(test_strcmp_ci);
|
|
||||||
sput_run_test(test_trim_leading);
|
|
||||||
sput_run_test(test_trim_trailing);
|
|
||||||
|
|
||||||
sput_enter_suite("parser");
|
|
||||||
sput_run_test(test_parser_missing);
|
|
||||||
sput_run_test(test_parser_minimal);
|
|
||||||
sput_run_test(test_parser_invalid);
|
|
||||||
|
|
||||||
sput_enter_suite("validator");
|
|
||||||
sput_run_test(test_validator_toplevel_not_object);
|
|
||||||
sput_run_test(test_validator_field_not_object);
|
|
||||||
sput_run_test(test_validator_field_name_leading_digit);
|
|
||||||
sput_run_test(test_validator_field_name_non_alnum);
|
|
||||||
sput_run_test(test_validator_field_type_invalid);
|
|
||||||
sput_run_test(test_validator_field_type_unknown);
|
|
||||||
sput_run_test(test_validator_valid_type_ci);
|
|
||||||
sput_run_test(test_validator_field_required_invalid);
|
|
||||||
sput_run_test(test_validator_field_required_valid);
|
|
||||||
sput_run_test(test_validator_field_prompt_invalid);
|
|
||||||
sput_run_test(test_validator_valid_no_required);
|
|
||||||
sput_run_test(test_validator_field_type_yes);
|
|
||||||
|
|
||||||
sput_finish_testing();
|
|
||||||
|
|
||||||
return sput_get_return_value();
|
|
||||||
}
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef _BOOTSTRAP_TEST_CASE_H
|
||||||
|
#define _BOOTSTRAP_TEST_CASE_H
|
||||||
|
|
||||||
|
struct TestCase {
|
||||||
|
char *name;
|
||||||
|
int (*func)();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _BOOTSTRAP_TEST_CASE_H */
|
|
@ -0,0 +1,122 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "string_utils.h"
|
||||||
|
#include "test_cases.h"
|
||||||
|
|
||||||
|
struct TestConfigFixture {
|
||||||
|
char *cwd;
|
||||||
|
char *root_dir;
|
||||||
|
char *target;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct TestConfigFixture *test_setup() {
|
||||||
|
char *cwd = getcwd(0, 0);
|
||||||
|
const char *segments[] = {cwd, "specs"};
|
||||||
|
char *root_dir = join(sizeof(segments) / sizeof(char *), segments, '/');
|
||||||
|
|
||||||
|
struct TestConfigFixture *fixture = malloc(sizeof(struct TestConfigFixture));
|
||||||
|
fixture->cwd = getcwd(0, 0);
|
||||||
|
fixture->root_dir = root_dir;
|
||||||
|
fixture->target = "minimal_spec_json";
|
||||||
|
|
||||||
|
return fixture;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_new_invalid_args() {
|
||||||
|
struct TestConfigFixture *fixture = test_setup();
|
||||||
|
struct Error *error = 0;
|
||||||
|
struct Config *config = 0;
|
||||||
|
|
||||||
|
error = config_new(0, fixture->root_dir, fixture->target, &config);
|
||||||
|
if (error->code != ERROR_CONFIG_ENV_CWD_INVALID) {
|
||||||
|
printf("cwd == 0\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
error_free(error);
|
||||||
|
error = config_new(fixture->cwd, 0, fixture->target, &config);
|
||||||
|
if (error->code != ERROR_CONFIG_ENV_ROOT_DIR_INVALID) {
|
||||||
|
printf("root_dir == 0\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_new_spec_not_found() {
|
||||||
|
struct TestConfigFixture *fixture = test_setup();
|
||||||
|
struct Error *error = 0;
|
||||||
|
struct Config *config = 0;
|
||||||
|
|
||||||
|
error = config_new(fixture->cwd, fixture->root_dir, "not_found", &config);
|
||||||
|
if (error->code != ERROR_CONFIG_TARGET_NOT_FOUND) {
|
||||||
|
printf("target not found\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_new_spec_not_dir() {
|
||||||
|
struct TestConfigFixture *fixture = test_setup();
|
||||||
|
struct Error *error = 0;
|
||||||
|
struct Config *config = 0;
|
||||||
|
|
||||||
|
error = config_new(fixture->cwd, fixture->root_dir, "not_dir", &config);
|
||||||
|
if (error->code != ERROR_CONFIG_TARGET_NOT_DIR) {
|
||||||
|
printf("target not dir\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_new_success() {
|
||||||
|
struct TestConfigFixture *fixture = test_setup();
|
||||||
|
struct Error *error = 0;
|
||||||
|
struct Config *config = 0;
|
||||||
|
|
||||||
|
error = config_new(fixture->cwd, fixture->root_dir, fixture->target, &config);
|
||||||
|
if (error != 0) {
|
||||||
|
error_free(error);
|
||||||
|
printf("config_new() success\n");
|
||||||
|
return 1;
|
||||||
|
} else if (strcmp(config->cwd, fixture->cwd) != 0) {
|
||||||
|
printf("config_new() cwd\n");
|
||||||
|
return 1;
|
||||||
|
} else if (strcmp(config->root_dir, fixture->root_dir) != 0) {
|
||||||
|
printf("config_new() root_dir\n");
|
||||||
|
return 1;
|
||||||
|
} else if (strcmp(config->target, fixture->target) != 0) {
|
||||||
|
printf("config_new() target\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
if (argc != 2) {
|
||||||
|
printf("Expected exactly one argument.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TestCase cases[] = {
|
||||||
|
{"new-invalid-args", test_new_invalid_args},
|
||||||
|
{"new-spec-not-found", test_new_spec_not_found},
|
||||||
|
{"new-spec-not-dir", test_new_spec_not_dir},
|
||||||
|
{"new-success", test_new_success},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < sizeof(cases) / sizeof(struct TestCase); ++i) {
|
||||||
|
struct TestCase test_case = cases[i];
|
||||||
|
if (strcmp(argv[1], test_case.name) == 0) {
|
||||||
|
return test_case.func();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
|
@ -1,103 +0,0 @@
|
||||||
#ifndef _BOOTSTRAP_TEST_CONFIG
|
|
||||||
#define _BOOTSTRAP_TEST_CONFIG
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "sput.h"
|
|
||||||
#include "string_utils.h"
|
|
||||||
|
|
||||||
struct TestConfigFixture {
|
|
||||||
char *cwd;
|
|
||||||
char *root_dir;
|
|
||||||
char *target;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct TestConfigFixture *test_config_setup() {
|
|
||||||
char *cwd = getcwd(0, 0);
|
|
||||||
const char *segments[] = {cwd, "test", "specs"};
|
|
||||||
char *root_dir = join(sizeof(segments) / sizeof(char *), segments, '/');
|
|
||||||
|
|
||||||
struct TestConfigFixture *fixture = malloc(sizeof(struct TestConfigFixture));
|
|
||||||
fixture->cwd = getcwd(0, 0);
|
|
||||||
fixture->root_dir = root_dir;
|
|
||||||
fixture->target = "minimal_spec_json";
|
|
||||||
|
|
||||||
return fixture;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_config_teardown(struct TestConfigFixture *fixture) {
|
|
||||||
free(fixture->cwd);
|
|
||||||
free(fixture->root_dir);
|
|
||||||
free(fixture);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_config_new_invalid_args() {
|
|
||||||
struct TestConfigFixture *fixture = test_config_setup();
|
|
||||||
|
|
||||||
struct Error *error = 0;
|
|
||||||
struct Config *config = 0;
|
|
||||||
|
|
||||||
error = config_new(0, fixture->root_dir, fixture->target, &config);
|
|
||||||
sput_fail_unless(error->code == ERROR_CONFIG_ENV_CWD_INVALID, "cwd == 0");
|
|
||||||
error_free(error);
|
|
||||||
error = config_new(fixture->cwd, 0, fixture->target, &config);
|
|
||||||
sput_fail_unless(
|
|
||||||
error->code == ERROR_CONFIG_ENV_ROOT_DIR_INVALID, "root_dir == 0"
|
|
||||||
);
|
|
||||||
error_free(error);
|
|
||||||
|
|
||||||
test_config_teardown(fixture);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_config_new_spec_not_found() {
|
|
||||||
struct TestConfigFixture *fixture = test_config_setup();
|
|
||||||
|
|
||||||
struct Error *error = 0;
|
|
||||||
struct Config *config = 0;
|
|
||||||
|
|
||||||
error = config_new(fixture->cwd, fixture->root_dir, "not_found", &config);
|
|
||||||
sput_fail_unless(
|
|
||||||
error->code == ERROR_CONFIG_TARGET_NOT_FOUND, "target not found"
|
|
||||||
);
|
|
||||||
error_free(error);
|
|
||||||
|
|
||||||
test_config_teardown(fixture);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_config_new_spec_not_dir() {
|
|
||||||
struct TestConfigFixture *fixture = test_config_setup();
|
|
||||||
|
|
||||||
struct Error *error = 0;
|
|
||||||
struct Config *config = 0;
|
|
||||||
|
|
||||||
error = config_new(fixture->cwd, fixture->root_dir, "not_dir", &config);
|
|
||||||
sput_fail_unless(
|
|
||||||
error->code == ERROR_CONFIG_TARGET_NOT_DIR, "target not dir"
|
|
||||||
);
|
|
||||||
error_free(error);
|
|
||||||
|
|
||||||
test_config_teardown(fixture);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_config_new_success() {
|
|
||||||
struct TestConfigFixture *fixture = test_config_setup();
|
|
||||||
|
|
||||||
struct Error *error = 0;
|
|
||||||
struct Config *config = 0;
|
|
||||||
|
|
||||||
error = config_new(fixture->cwd, fixture->root_dir, fixture->target, &config);
|
|
||||||
sput_fail_unless(error == 0, "config_new() success");
|
|
||||||
sput_fail_unless(strcmp(config->cwd, fixture->cwd) == 0, "config_new() cwd");
|
|
||||||
sput_fail_unless(
|
|
||||||
strcmp(config->root_dir, fixture->root_dir) == 0, "config_new() root_dir"
|
|
||||||
);
|
|
||||||
sput_fail_unless(
|
|
||||||
strcmp(config->target, fixture->target) == 0, "config_new() target"
|
|
||||||
);
|
|
||||||
|
|
||||||
config_free(config);
|
|
||||||
test_config_teardown(fixture);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* _BOOTSTRAP_TEST_CONFIG */
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "dyn_array.h"
|
||||||
|
#include "test_cases.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
A @DynArray with zero capacity can be instantiated and have entries pushed onto.
|
||||||
|
*/
|
||||||
|
static int test_zero_capacity() {
|
||||||
|
struct DynArray *a = dyn_array_new(0);
|
||||||
|
|
||||||
|
if (a->size != 0) {
|
||||||
|
printf("a->size != 0\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (a->_capacity != 1) {
|
||||||
|
printf("a->_capacity != 1\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int *x = malloc(sizeof(int));
|
||||||
|
dyn_array_push(a, x);
|
||||||
|
if (a->size != 1) {
|
||||||
|
printf("a->size != 1\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (a->_capacity != 1) {
|
||||||
|
printf("a->_capacity != 1\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int *y = malloc(sizeof(int));
|
||||||
|
dyn_array_push(a, y);
|
||||||
|
if (a->size != 2) {
|
||||||
|
printf("a->size != 2\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (a->_capacity != 2) {
|
||||||
|
printf("a->_capacity != 2\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
A @DynArray with nonzero capacity can be instantiated and have entries pushed
|
||||||
|
onto.
|
||||||
|
*/
|
||||||
|
static int test_nonzero_capacity() {
|
||||||
|
struct DynArray *a = dyn_array_new(3);
|
||||||
|
|
||||||
|
if (a->size != 0) {
|
||||||
|
printf("a->size != 0\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (a->_capacity != 3) {
|
||||||
|
printf("a->_capacity != 3\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int *x = malloc(sizeof(int));
|
||||||
|
int *y = malloc(sizeof(int));
|
||||||
|
int *z = malloc(sizeof(int));
|
||||||
|
dyn_array_push(a, x);
|
||||||
|
dyn_array_push(a, y);
|
||||||
|
dyn_array_push(a, z);
|
||||||
|
if (a->size != 3) {
|
||||||
|
printf("a->size != 3\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (a->_capacity != 3) {
|
||||||
|
printf("a->_capacity != 3\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int *w = malloc(sizeof(int));
|
||||||
|
dyn_array_push(a, w);
|
||||||
|
if (a->size != 4) {
|
||||||
|
printf("a->size != 4\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (a->_capacity != 6) {
|
||||||
|
printf("a->_capacity != 6\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
if (argc != 2) {
|
||||||
|
printf("Expected exactly one argument.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TestCase cases[] = {
|
||||||
|
{"zero-capacity", test_zero_capacity},
|
||||||
|
{"nonzero-capacity", test_nonzero_capacity},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < sizeof(cases) / sizeof(struct TestCase); ++i) {
|
||||||
|
struct TestCase test_case = cases[i];
|
||||||
|
if (strcmp(argv[1], test_case.name) == 0) {
|
||||||
|
return test_case.func();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
|
@ -1,54 +0,0 @@
|
||||||
#ifndef _BOOTSTRAP_TEST_DYN_ARRAY
|
|
||||||
#define _BOOTSTRAP_TEST_DYN_ARRAY
|
|
||||||
|
|
||||||
#include "dyn_array.h"
|
|
||||||
#include "sput.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
A @DynArray with zero capacity can be instantiated and have entries pushed onto.
|
|
||||||
*/
|
|
||||||
static void test_dyn_array_zero_capacity() {
|
|
||||||
struct DynArray *a = dyn_array_new(0);
|
|
||||||
sput_fail_unless(a->size == 0, "a->size == 0");
|
|
||||||
sput_fail_unless(a->_capacity == 1, "a->_capacity == 1");
|
|
||||||
|
|
||||||
int *x = malloc(sizeof(int));
|
|
||||||
dyn_array_push(a, x);
|
|
||||||
sput_fail_unless(a->size == 1, "a->size == 1");
|
|
||||||
sput_fail_unless(a->_capacity == 1, "a->_capacity == 1");
|
|
||||||
|
|
||||||
int *y = malloc(sizeof(int));
|
|
||||||
dyn_array_push(a, y);
|
|
||||||
sput_fail_unless(a->size == 2, "a->size == 2");
|
|
||||||
sput_fail_unless(a->_capacity == 2, "a->_capacity == 2");
|
|
||||||
|
|
||||||
dyn_array_free(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
A @DynArray with nonzero capacity can be instantiated and have entries pushed
|
|
||||||
onto.
|
|
||||||
*/
|
|
||||||
static void test_dyn_array_nonzero_capacity() {
|
|
||||||
struct DynArray *a = dyn_array_new(3);
|
|
||||||
sput_fail_unless(a->size == 0, "a->size == 0");
|
|
||||||
sput_fail_unless(a->_capacity == 3, "a->_capacity == 3");
|
|
||||||
|
|
||||||
int *x = malloc(sizeof(int));
|
|
||||||
int *y = malloc(sizeof(int));
|
|
||||||
int *z = malloc(sizeof(int));
|
|
||||||
dyn_array_push(a, x);
|
|
||||||
dyn_array_push(a, y);
|
|
||||||
dyn_array_push(a, z);
|
|
||||||
sput_fail_unless(a->size == 3, "a->size == 3");
|
|
||||||
sput_fail_unless(a->_capacity == 3, "a->_capacity == 3");
|
|
||||||
|
|
||||||
int *w = malloc(sizeof(int));
|
|
||||||
dyn_array_push(a, w);
|
|
||||||
sput_fail_unless(a->size == 4, "a->size == 4");
|
|
||||||
sput_fail_unless(a->_capacity == 6, "a->_capacity == 6");
|
|
||||||
|
|
||||||
dyn_array_free(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* _BOOTSTRAP_TEST_DYN_ARRAY */
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "cJSON.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "parser.h"
|
||||||
|
#include "string_utils.h"
|
||||||
|
#include "test_cases.h"
|
||||||
|
|
||||||
|
struct TestParserFixture {
|
||||||
|
char *cwd;
|
||||||
|
char *root_dir;
|
||||||
|
const char *target;
|
||||||
|
struct Config config;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct TestParserFixture *test_setup(const char *target) {
|
||||||
|
char *cwd = getcwd(0, 0);
|
||||||
|
const char *segments[] = {cwd, "specs"};
|
||||||
|
char *root_dir = join(sizeof(segments) / sizeof(char *), segments, '/');
|
||||||
|
|
||||||
|
struct TestParserFixture *fixture = malloc(sizeof(struct TestParserFixture));
|
||||||
|
fixture->cwd = getcwd(0, 0);
|
||||||
|
fixture->root_dir = root_dir;
|
||||||
|
fixture->target = target;
|
||||||
|
|
||||||
|
// Reproduce in `Config` instance for convenience.
|
||||||
|
fixture->config.cwd = fixture->cwd;
|
||||||
|
fixture->config.root_dir = fixture->root_dir;
|
||||||
|
fixture->config.target = fixture->target;
|
||||||
|
|
||||||
|
return fixture;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
A missing `spec.json` file is not an error. Our parsed @cJSON instance should
|
||||||
|
be set to NULL in this case.
|
||||||
|
*/
|
||||||
|
static int test_missing() {
|
||||||
|
struct TestParserFixture *fixture = test_setup("no_spec_json");
|
||||||
|
struct Error *error = 0;
|
||||||
|
|
||||||
|
cJSON *parsed = 0;
|
||||||
|
error = parse_spec_json(&fixture->config, &parsed);
|
||||||
|
if (error != 0) {
|
||||||
|
printf("no spec.json, failure\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (parsed != 0) {
|
||||||
|
printf("no spec.json, parsed\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_minimal() {
|
||||||
|
struct TestParserFixture *fixture = test_setup("minimal_spec_json");
|
||||||
|
struct Error *error = 0;
|
||||||
|
cJSON *parsed = 0;
|
||||||
|
|
||||||
|
error = parse_spec_json(&fixture->config, &parsed);
|
||||||
|
if (error != 0) {
|
||||||
|
printf("minimal spec.json, failure\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (parsed == 0) {
|
||||||
|
printf("minimal spec.json, no parse\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_invalid() {
|
||||||
|
struct TestParserFixture *fixture = test_setup("invalid_spec_json");
|
||||||
|
struct Error *error = 0;
|
||||||
|
cJSON *parsed = 0;
|
||||||
|
|
||||||
|
error = parse_spec_json(&fixture->config, &parsed);
|
||||||
|
if (error->code != ERROR_PARSER_SPEC_JSON_INVALID_SYNTAX) {
|
||||||
|
printf("invalid spec, wrong code\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (parsed != 0) {
|
||||||
|
printf("invalid spec, parsed\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
if (argc != 2) {
|
||||||
|
printf("Expected exactly one argument.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TestCase cases[] = {
|
||||||
|
{"missing", test_missing},
|
||||||
|
{"minimal", test_minimal},
|
||||||
|
{"invalid", test_invalid},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < sizeof(cases) / sizeof(struct TestCase); ++i) {
|
||||||
|
struct TestCase test_case = cases[i];
|
||||||
|
if (strcmp(argv[1], test_case.name) == 0) {
|
||||||
|
return test_case.func();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
|
@ -1,84 +0,0 @@
|
||||||
#ifndef _BOOTSTRAP_TEST_PARSER
|
|
||||||
#define _BOOTSTRAP_TEST_PARSER
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "cJSON.h"
|
|
||||||
#include "config.h"
|
|
||||||
#include "parser.h"
|
|
||||||
#include "sput.h"
|
|
||||||
#include "string_utils.h"
|
|
||||||
|
|
||||||
struct TestParserFixture {
|
|
||||||
char *cwd;
|
|
||||||
char *root_dir;
|
|
||||||
const char *target;
|
|
||||||
struct Config config;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct TestParserFixture *test_parser_setup(const char *target) {
|
|
||||||
char *cwd = getcwd(0, 0);
|
|
||||||
const char *segments[] = {cwd, "test", "specs"};
|
|
||||||
char *root_dir = join(sizeof(segments) / sizeof(char *), segments, '/');
|
|
||||||
|
|
||||||
struct TestParserFixture *fixture = malloc(sizeof(struct TestParserFixture));
|
|
||||||
fixture->cwd = getcwd(0, 0);
|
|
||||||
fixture->root_dir = root_dir;
|
|
||||||
fixture->target = target;
|
|
||||||
|
|
||||||
// Reproduce in `Config` instance for convenience.
|
|
||||||
fixture->config.cwd = fixture->cwd;
|
|
||||||
fixture->config.root_dir = fixture->root_dir;
|
|
||||||
fixture->config.target = fixture->target;
|
|
||||||
|
|
||||||
return fixture;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_parser_teardown(struct TestParserFixture *fixture) {
|
|
||||||
free(fixture->cwd);
|
|
||||||
free(fixture->root_dir);
|
|
||||||
free(fixture);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
A missing `spec.json` file is not an error. Our parsed @cJSON instance should
|
|
||||||
be set to NULL in this case.
|
|
||||||
*/
|
|
||||||
static void test_parser_missing() {
|
|
||||||
struct TestParserFixture *fixture = test_parser_setup("no_spec_json");
|
|
||||||
|
|
||||||
cJSON *parsed = 0;
|
|
||||||
struct Error *error = parse_spec_json(&fixture->config, &parsed);
|
|
||||||
sput_fail_unless(error == 0, "no spec.json, success");
|
|
||||||
sput_fail_unless(parsed == 0, "no spec.json, no parsed");
|
|
||||||
|
|
||||||
test_parser_teardown(fixture);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_parser_minimal() {
|
|
||||||
struct TestParserFixture *fixture = test_parser_setup("minimal_spec_json");
|
|
||||||
|
|
||||||
cJSON *parsed = 0;
|
|
||||||
struct Error *error = parse_spec_json(&fixture->config, &parsed);
|
|
||||||
sput_fail_unless(error == 0, "minimal spec.json, success");
|
|
||||||
sput_fail_unless(parsed != 0, "minimal spec.json, parsed");
|
|
||||||
|
|
||||||
test_parser_teardown(fixture);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_parser_invalid() {
|
|
||||||
struct TestParserFixture *fixture = test_parser_setup("invalid_spec_json");
|
|
||||||
|
|
||||||
cJSON *parsed = 0;
|
|
||||||
struct Error *error = parse_spec_json(&fixture->config, &parsed);
|
|
||||||
sput_fail_unless(
|
|
||||||
error->code == ERROR_PARSER_SPEC_JSON_INVALID_SYNTAX,
|
|
||||||
"invalid spec.json, INVALID_SYNTAX"
|
|
||||||
);
|
|
||||||
error_free(error);
|
|
||||||
sput_fail_unless(parsed == 0, "invalid spec.json, not parsed");
|
|
||||||
|
|
||||||
test_parser_teardown(fixture);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* _BOOTSTRAP_TEST_PARSER */
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "string_buf.h"
|
||||||
|
#include "test_cases.h"
|
||||||
|
|
||||||
|
static int test_sappend() {
|
||||||
|
struct StringBuf *sb = string_buf_new(0);
|
||||||
|
string_buf_sappend(sb, "hello world");
|
||||||
|
string_buf_sappend(sb, "!!");
|
||||||
|
|
||||||
|
if (string_buf_size(sb) != strlen("hello world!!")) {
|
||||||
|
printf("sappend unexpected size\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *cast = string_buf_cast(sb);
|
||||||
|
if (strcmp(cast, "hello world!!") != 0) {
|
||||||
|
printf("sappend wrong cast\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_cappend() {
|
||||||
|
struct StringBuf *sb = string_buf_new(0);
|
||||||
|
string_buf_sappend(sb, "hello world");
|
||||||
|
string_buf_cappend(sb, '!');
|
||||||
|
string_buf_cappend(sb, '!');
|
||||||
|
|
||||||
|
if (string_buf_size(sb) != strlen("hello world!!")) {
|
||||||
|
printf("cappend wrong size\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *cast = string_buf_cast(sb);
|
||||||
|
if (strcmp(cast, "hello world!!") != 0) {
|
||||||
|
printf("cappend wrong cast\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
if (argc != 2) {
|
||||||
|
printf("Expected exactly one argument.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TestCase cases[] = {
|
||||||
|
{"sappend", test_sappend},
|
||||||
|
{"cappend", test_cappend},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < sizeof(cases) / sizeof(struct TestCase); ++i) {
|
||||||
|
struct TestCase test_case = cases[i];
|
||||||
|
if (strcmp(argv[1], test_case.name) == 0) {
|
||||||
|
return test_case.func();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
|
@ -1,42 +0,0 @@
|
||||||
#ifndef _BOOTSTRAP_TEST_STRING_BUF
|
|
||||||
#define _BOOTSTRAP_TEST_STRING_BUF
|
|
||||||
|
|
||||||
#include "sput.h"
|
|
||||||
#include "string_buf.h"
|
|
||||||
|
|
||||||
static void test_string_buf_sappend() {
|
|
||||||
struct StringBuf *sb = string_buf_new(0);
|
|
||||||
string_buf_sappend(sb, "hello world");
|
|
||||||
string_buf_sappend(sb, "!!");
|
|
||||||
|
|
||||||
sput_fail_unless(
|
|
||||||
string_buf_size(sb) == strlen("hello world!!"), "sappend size"
|
|
||||||
);
|
|
||||||
const char *cast = string_buf_cast(sb);
|
|
||||||
sput_fail_unless(strcmp(cast, "hello world!!") == 0, "sappend cast");
|
|
||||||
free((void *)cast);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_string_buf_cappend() {
|
|
||||||
struct StringBuf *sb = string_buf_new(0);
|
|
||||||
string_buf_sappend(sb, "hello world");
|
|
||||||
string_buf_cappend(sb, '!');
|
|
||||||
string_buf_cappend(sb, '!');
|
|
||||||
|
|
||||||
sput_fail_unless(
|
|
||||||
string_buf_size(sb) == strlen("hello world!!"), "cappend size"
|
|
||||||
);
|
|
||||||
const char *cast = string_buf_cast(sb);
|
|
||||||
sput_fail_unless(strcmp(cast, "hello world!!") == 0, "cappend cast");
|
|
||||||
free((void *)cast);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_string_buf_nonzero_capacity() {
|
|
||||||
struct StringBuf *sb = string_buf_new(100);
|
|
||||||
string_buf_sappend(sb, "hello world");
|
|
||||||
string_buf_cappend(sb, '!');
|
|
||||||
string_buf_free(sb);
|
|
||||||
sput_fail_unless(sb == 0, "free");
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* _BOOTSTRAP_TEST_STRING_BUF */
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "string_utils.h"
|
||||||
|
#include "test_cases.h"
|
||||||
|
|
||||||
|
static int test_join_single() {
|
||||||
|
const char *segments[] = {"abc"};
|
||||||
|
char *joined = join(sizeof(segments) / sizeof(char *), segments, '/');
|
||||||
|
if (strcmp(joined, "abc") != 0) {
|
||||||
|
printf("joined != abc\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_join_multiple() {
|
||||||
|
const char *segments[] = {"abc", "def", "ghi"};
|
||||||
|
char *joined = join(sizeof(segments) / sizeof(char *), segments, '/');
|
||||||
|
if (strcmp(joined, "abc/def/ghi") != 0) {
|
||||||
|
printf("joined != abc/def/ghi\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_strcmp_ci() {
|
||||||
|
const char *a1 = "aBcD";
|
||||||
|
const char *a2 = "AbCd";
|
||||||
|
if (strcmp_ci(a1, a2) != 0) {
|
||||||
|
printf("strcmp_ci != 0\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *b1 = "aBcDe";
|
||||||
|
const char *b2 = "AbCd";
|
||||||
|
if (strcmp_ci(b1, b2) <= 0) {
|
||||||
|
printf("strcmp_ci <= 0\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (strcmp_ci(b2, b1) >= 0) {
|
||||||
|
printf("strcmp_ci >= 0\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_trim_leading() {
|
||||||
|
char a1[] = {0};
|
||||||
|
char a2[] = {' ', ' ', ' ', 0};
|
||||||
|
trim_leading(a1);
|
||||||
|
trim_leading(a2);
|
||||||
|
if (a1[0] != 0) {
|
||||||
|
printf("trim leading empty string\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (strcmp(a1, a2) != 0) {
|
||||||
|
printf("trim leading whitespace string");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char b1[] = {'a', 'b', 'c', 'd', 'e', 'f', 0};
|
||||||
|
char b2[] = {' ', ' ', ' ', 'a', 'b', 'c', 'd', 'e', 'f', 0};
|
||||||
|
trim_leading(b1);
|
||||||
|
trim_leading(b2);
|
||||||
|
if (strcmp(b1, b2) != 0) {
|
||||||
|
printf("trim leading string\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char c1[] = {'a', 'b', 'c', 'd', 'e', 'f', ' ', ' ', ' ', 0};
|
||||||
|
char c2[] = {'a', 'b', 'c', 'd', 'e', 'f', ' ', ' ', ' ', 0};
|
||||||
|
trim_leading(c1);
|
||||||
|
if (strcmp(c1, c2) != 0) {
|
||||||
|
printf("trim leading ignore trailing\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_trim_trailing() {
|
||||||
|
char a1[] = {0};
|
||||||
|
char a2[] = {' ', ' ', ' ', 0};
|
||||||
|
trim_trailing(a1);
|
||||||
|
trim_trailing(a2);
|
||||||
|
if (a1[0] != 0) {
|
||||||
|
printf("trim trailing empty string\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (strcmp(a1, a2) != 0) {
|
||||||
|
printf("trim trailing whitespace string\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char b1[] = {'a', 'b', 'c', 'd', 'e', 'f', 0};
|
||||||
|
char b2[] = {'a', 'b', 'c', 'd', 'e', 'f', ' ', ' ', ' ', 0};
|
||||||
|
trim_trailing(b1);
|
||||||
|
trim_trailing(b2);
|
||||||
|
if (strcmp(b1, b2) != 0) {
|
||||||
|
printf("trim trailing string\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char c1[] = {' ', ' ', ' ', 'a', 'b', 'c', 'd', 'e', 'f', 0};
|
||||||
|
char c2[] = {' ', ' ', ' ', 'a', 'b', 'c', 'd', 'e', 'f', 0};
|
||||||
|
trim_trailing(c1);
|
||||||
|
if (strcmp(c1, c2) != 0) {
|
||||||
|
printf("trim trailing ignore leading\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
if (argc != 2) {
|
||||||
|
printf("Expected exactly one argument.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TestCase cases[] = {
|
||||||
|
{"join-single", test_join_single},
|
||||||
|
{"join-multiple", test_join_multiple},
|
||||||
|
{"strcmp-ci", test_strcmp_ci},
|
||||||
|
{"trim-leading", test_trim_leading},
|
||||||
|
{"trim-trailing", test_trim_trailing},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < sizeof(cases) / sizeof(struct TestCase); ++i) {
|
||||||
|
struct TestCase test_case = cases[i];
|
||||||
|
if (strcmp(argv[1], test_case.name) == 0) {
|
||||||
|
return test_case.func();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
|
@ -1,72 +0,0 @@
|
||||||
#ifndef _BOOTSTRAP_TEST_STRING_UTILS
|
|
||||||
#define _BOOTSTRAP_TEST_STRING_UTILS
|
|
||||||
|
|
||||||
#include "sput.h"
|
|
||||||
#include "string_utils.h"
|
|
||||||
|
|
||||||
static void test_join_single() {
|
|
||||||
const char *segments[] = {"abc"};
|
|
||||||
char *joined = join(sizeof(segments) / sizeof(char *), segments, '/');
|
|
||||||
sput_fail_unless(strcmp(joined, "abc") == 0, "abc");
|
|
||||||
free(joined);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_join_multiple() {
|
|
||||||
const char *segments[] = {"abc", "def", "ghi"};
|
|
||||||
char *joined = join(sizeof(segments) / sizeof(char *), segments, '/');
|
|
||||||
sput_fail_unless(strcmp(joined, "abc/def/ghi") == 0, "abc/def/ghi");
|
|
||||||
free(joined);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_strcmp_ci() {
|
|
||||||
const char *a1 = "aBcD";
|
|
||||||
const char *a2 = "AbCd";
|
|
||||||
sput_fail_unless(strcmp_ci(a1, a2) == 0, "strcmp_ci == 0");
|
|
||||||
|
|
||||||
const char *b1 = "aBcDe";
|
|
||||||
const char *b2 = "AbCd";
|
|
||||||
sput_fail_unless(strcmp_ci(b1, b2) > 0, "strcmp_ci > 0");
|
|
||||||
sput_fail_unless(strcmp_ci(b2, b1) < 0, "strcmp_ci < 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_trim_leading() {
|
|
||||||
char a1[] = {0};
|
|
||||||
char a2[] = {' ', ' ', ' ', 0};
|
|
||||||
trim_leading(a1);
|
|
||||||
trim_leading(a2);
|
|
||||||
sput_fail_unless(a1[0] == 0, "trim leading empty string");
|
|
||||||
sput_fail_unless(strcmp(a1, a2) == 0, "trim leading whitespace string");
|
|
||||||
|
|
||||||
char b1[] = {'a', 'b', 'c', 'd', 'e', 'f', 0};
|
|
||||||
char b2[] = {' ', ' ', ' ', 'a', 'b', 'c', 'd', 'e', 'f', 0};
|
|
||||||
trim_leading(b1);
|
|
||||||
trim_leading(b2);
|
|
||||||
sput_fail_unless(strcmp(b1, b2) == 0, "trim leading string");
|
|
||||||
|
|
||||||
char c1[] = {'a', 'b', 'c', 'd', 'e', 'f', ' ', ' ', ' ', 0};
|
|
||||||
char c2[] = {'a', 'b', 'c', 'd', 'e', 'f', ' ', ' ', ' ', 0};
|
|
||||||
trim_leading(c1);
|
|
||||||
sput_fail_unless(strcmp(c1, c2) == 0, "trim leading ignore trailing");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_trim_trailing() {
|
|
||||||
char a1[] = {0};
|
|
||||||
char a2[] = {' ', ' ', ' ', 0};
|
|
||||||
trim_trailing(a1);
|
|
||||||
trim_trailing(a2);
|
|
||||||
sput_fail_unless(a1[0] == 0, "trim trailing empty string");
|
|
||||||
sput_fail_unless(strcmp(a1, a2) == 0, "trim trailing whitespace string");
|
|
||||||
|
|
||||||
char b1[] = {'a', 'b', 'c', 'd', 'e', 'f', 0};
|
|
||||||
char b2[] = {'a', 'b', 'c', 'd', 'e', 'f', ' ', ' ', ' ', 0};
|
|
||||||
trim_trailing(b1);
|
|
||||||
trim_trailing(b2);
|
|
||||||
sput_fail_unless(strcmp(b1, b2) == 0, "trim trailing string");
|
|
||||||
|
|
||||||
char c1[] = {' ', ' ', ' ', 'a', 'b', 'c', 'd', 'e', 'f', 0};
|
|
||||||
char c2[] = {' ', ' ', ' ', 'a', 'b', 'c', 'd', 'e', 'f', 0};
|
|
||||||
trim_trailing(c1);
|
|
||||||
sput_fail_unless(strcmp(c1, c2) == 0, "trim trailing ignore leading");
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* _BOOTSTRAP_TEST_STRING_UTILS */
|
|
|
@ -0,0 +1,287 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "dyn_array.h"
|
||||||
|
#include "string_utils.h"
|
||||||
|
#include "test_cases.h"
|
||||||
|
#include "validator.h"
|
||||||
|
|
||||||
|
struct TestValidatorFixture {
|
||||||
|
const char *json;
|
||||||
|
struct DynArray *prompts;
|
||||||
|
cJSON *parsed;
|
||||||
|
struct Config config;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct TestValidatorFixture *test_setup(const char *json) {
|
||||||
|
struct TestValidatorFixture *fixture =
|
||||||
|
malloc(sizeof(struct TestValidatorFixture));
|
||||||
|
fixture->json = json;
|
||||||
|
fixture->prompts = 0;
|
||||||
|
fixture->parsed = cJSON_Parse(json);
|
||||||
|
|
||||||
|
char *cwd = getcwd(0, 0);
|
||||||
|
const char *segments[] = {cwd, "test", "specs"};
|
||||||
|
char *root_dir = join(sizeof(segments) / sizeof(char *), segments, '/');
|
||||||
|
|
||||||
|
fixture->config.cwd = cwd;
|
||||||
|
fixture->config.root_dir = root_dir;
|
||||||
|
fixture->config.target = "minimal_spec_json";
|
||||||
|
|
||||||
|
return fixture;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_toplevel_not_object() {
|
||||||
|
struct TestValidatorFixture *fixture = test_setup("[]");
|
||||||
|
|
||||||
|
struct Error *error =
|
||||||
|
validate_spec_json(&fixture->config, fixture->parsed, &fixture->prompts);
|
||||||
|
if (error->code != ERROR_VALIDATOR_TOP_LEVEL_NOT_OBJECT) {
|
||||||
|
printf("top-level not object\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_field_not_object() {
|
||||||
|
struct TestValidatorFixture *fixture = test_setup("{\"key\": \"$UNKNOWN\"}");
|
||||||
|
|
||||||
|
struct Error *error =
|
||||||
|
validate_spec_json(&fixture->config, fixture->parsed, &fixture->prompts);
|
||||||
|
if (error->code != ERROR_VALIDATOR_FIELD_NOT_OBJECT) {
|
||||||
|
printf("field not object\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_field_name_leading_digit() {
|
||||||
|
struct TestValidatorFixture *fixture = test_setup(
|
||||||
|
"{"
|
||||||
|
" \"1abc\": {"
|
||||||
|
" \"type\": \"line\""
|
||||||
|
" }"
|
||||||
|
"}"
|
||||||
|
);
|
||||||
|
|
||||||
|
struct Error *error =
|
||||||
|
validate_spec_json(&fixture->config, fixture->parsed, &fixture->prompts);
|
||||||
|
if (error->code != ERROR_VALIDATOR_FIELD_NAME_INVALID) {
|
||||||
|
printf("field name leading digit\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_field_name_non_alnum() {
|
||||||
|
struct TestValidatorFixture *fixture = test_setup(
|
||||||
|
"{"
|
||||||
|
" \"a~bc\": {"
|
||||||
|
" \"type\": \"line\""
|
||||||
|
" }"
|
||||||
|
"}"
|
||||||
|
);
|
||||||
|
|
||||||
|
struct Error *error =
|
||||||
|
validate_spec_json(&fixture->config, fixture->parsed, &fixture->prompts);
|
||||||
|
if (error->code != ERROR_VALIDATOR_FIELD_NAME_INVALID) {
|
||||||
|
printf("field name non alnum\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_field_type_invalid() {
|
||||||
|
struct TestValidatorFixture *fixture = test_setup(
|
||||||
|
"{"
|
||||||
|
" \"key\": {"
|
||||||
|
" \"type\": 2"
|
||||||
|
" }"
|
||||||
|
"}"
|
||||||
|
);
|
||||||
|
|
||||||
|
struct Error *error =
|
||||||
|
validate_spec_json(&fixture->config, fixture->parsed, &fixture->prompts);
|
||||||
|
if (error->code != ERROR_VALIDATOR_FIELD_TYPE_INVALID) {
|
||||||
|
printf("field type invalid\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_field_type_unknown() {
|
||||||
|
struct TestValidatorFixture *fixture = test_setup(
|
||||||
|
"{"
|
||||||
|
" \"key\": {"
|
||||||
|
" \"type\": \"UNKNOWN\""
|
||||||
|
" }"
|
||||||
|
"}"
|
||||||
|
);
|
||||||
|
|
||||||
|
struct Error *error =
|
||||||
|
validate_spec_json(&fixture->config, fixture->parsed, &fixture->prompts);
|
||||||
|
if (error->code != ERROR_VALIDATOR_FIELD_TYPE_UNKNOWN) {
|
||||||
|
printf("field type unknown\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_valid_type_ci() {
|
||||||
|
struct TestValidatorFixture *fixture = test_setup(
|
||||||
|
"{"
|
||||||
|
" \"key\": {"
|
||||||
|
" \"type\": \"LiNe\","
|
||||||
|
" \"prompt\": \"What value for key?\""
|
||||||
|
" }"
|
||||||
|
"}"
|
||||||
|
);
|
||||||
|
|
||||||
|
struct Error *error =
|
||||||
|
validate_spec_json(&fixture->config, fixture->parsed, &fixture->prompts);
|
||||||
|
if (error != 0) {
|
||||||
|
printf("valid ci not working\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_field_required_invalid() {
|
||||||
|
struct TestValidatorFixture *fixture = test_setup(
|
||||||
|
"{"
|
||||||
|
" \"key\": {"
|
||||||
|
" \"type\": \"line\","
|
||||||
|
" \"required\": 5"
|
||||||
|
" }"
|
||||||
|
"}"
|
||||||
|
);
|
||||||
|
|
||||||
|
struct Error *error =
|
||||||
|
validate_spec_json(&fixture->config, fixture->parsed, &fixture->prompts);
|
||||||
|
if (error->code != ERROR_VALIDATOR_FIELD_REQUIRED_INVALID) {
|
||||||
|
printf("field required invalid\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_field_required_valid() {
|
||||||
|
struct TestValidatorFixture *fixture = test_setup(
|
||||||
|
"{"
|
||||||
|
" \"key\": {"
|
||||||
|
" \"type\": \"line\","
|
||||||
|
" \"required\": true,"
|
||||||
|
" \"prompt\": \"What value for key? \""
|
||||||
|
" }"
|
||||||
|
"}"
|
||||||
|
);
|
||||||
|
|
||||||
|
struct Error *error =
|
||||||
|
validate_spec_json(&fixture->config, fixture->parsed, &fixture->prompts);
|
||||||
|
if (error != 0) {
|
||||||
|
printf("required valid\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_field_prompt_invalid() {
|
||||||
|
struct TestValidatorFixture *fixture = test_setup(
|
||||||
|
"{"
|
||||||
|
" \"key\": {"
|
||||||
|
" \"type\": \"line\","
|
||||||
|
" \"prompt\": 2"
|
||||||
|
" }"
|
||||||
|
"}"
|
||||||
|
);
|
||||||
|
|
||||||
|
struct Error *error =
|
||||||
|
validate_spec_json(&fixture->config, fixture->parsed, &fixture->prompts);
|
||||||
|
if (error->code != ERROR_VALIDATOR_FIELD_PROMPT_INVALID) {
|
||||||
|
printf("field prompt invalid\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_valid_no_required() {
|
||||||
|
struct TestValidatorFixture *fixture = test_setup(
|
||||||
|
"{"
|
||||||
|
" \"key\": {"
|
||||||
|
" \"type\": \"line\","
|
||||||
|
" \"prompt\": \"What value for key?\""
|
||||||
|
" }"
|
||||||
|
"}"
|
||||||
|
);
|
||||||
|
|
||||||
|
struct Error *error =
|
||||||
|
validate_spec_json(&fixture->config, fixture->parsed, &fixture->prompts);
|
||||||
|
if (error != 0) {
|
||||||
|
printf("valid\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_field_type_yes() {
|
||||||
|
struct TestValidatorFixture *fixture = test_setup(
|
||||||
|
"{"
|
||||||
|
" \"abc\": {"
|
||||||
|
" \"type\": \"yes\""
|
||||||
|
" \"prompt\": \"What value for key?\""
|
||||||
|
" }"
|
||||||
|
"}"
|
||||||
|
);
|
||||||
|
|
||||||
|
struct Error *error =
|
||||||
|
validate_spec_json(&fixture->config, fixture->parsed, &fixture->prompts);
|
||||||
|
if (error != 0) {
|
||||||
|
printf("yes valid\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
if (argc != 2) {
|
||||||
|
printf("Expected exactly one argument.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TestCase cases[] = {
|
||||||
|
{"toplevel-not-object", test_toplevel_not_object},
|
||||||
|
{"field-not-object", test_field_not_object},
|
||||||
|
{"field-name-leading-digit", test_field_name_leading_digit},
|
||||||
|
{"field-name-non-alnum", test_field_name_non_alnum},
|
||||||
|
{"field-type-invalid", test_field_type_invalid},
|
||||||
|
{"field-type-unknown", test_field_type_unknown},
|
||||||
|
{"valid-type-ci", test_valid_type_ci},
|
||||||
|
{"field-required-invalid", test_field_required_invalid},
|
||||||
|
{"field-required-valid", test_field_required_valid},
|
||||||
|
{"field-prompt-invalid", test_field_prompt_invalid},
|
||||||
|
{"valid-no-required", test_valid_no_required},
|
||||||
|
{"field-type-yes", test_field_type_yes},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < sizeof(cases) / sizeof(struct TestCase); ++i) {
|
||||||
|
struct TestCase test_case = cases[i];
|
||||||
|
if (strcmp(argv[1], test_case.name) == 0) {
|
||||||
|
return test_case.func();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
|
@ -1,260 +0,0 @@
|
||||||
#ifndef _BOOTSTRAP_TEST_VALIDATOR
|
|
||||||
#define _BOOTSTRAP_TEST_VALIDATOR
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "dyn_array.h"
|
|
||||||
#include "sput.h"
|
|
||||||
#include "string_utils.h"
|
|
||||||
#include "validator.h"
|
|
||||||
|
|
||||||
struct TestValidatorFixture {
|
|
||||||
const char *json;
|
|
||||||
struct DynArray *prompts;
|
|
||||||
cJSON *parsed;
|
|
||||||
struct Config config;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct TestValidatorFixture *test_validator_setup(const char *json) {
|
|
||||||
struct TestValidatorFixture *fixture =
|
|
||||||
malloc(sizeof(struct TestValidatorFixture));
|
|
||||||
fixture->json = json;
|
|
||||||
fixture->prompts = 0;
|
|
||||||
fixture->parsed = cJSON_Parse(json);
|
|
||||||
|
|
||||||
char *cwd = getcwd(0, 0);
|
|
||||||
const char *segments[] = {cwd, "test", "specs"};
|
|
||||||
char *root_dir = join(sizeof(segments) / sizeof(char *), segments, '/');
|
|
||||||
|
|
||||||
fixture->config.cwd = cwd;
|
|
||||||
fixture->config.root_dir = root_dir;
|
|
||||||
fixture->config.target = "minimal_spec_json";
|
|
||||||
|
|
||||||
return fixture;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_validator_teardown(struct TestValidatorFixture *fixture) {
|
|
||||||
if (fixture->parsed) {
|
|
||||||
cJSON_Delete(fixture->parsed);
|
|
||||||
}
|
|
||||||
free((void *)fixture->config.cwd);
|
|
||||||
free((void *)fixture->config.root_dir);
|
|
||||||
free(fixture);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_validator_toplevel_not_object() {
|
|
||||||
struct TestValidatorFixture *fixture = test_validator_setup("[]");
|
|
||||||
|
|
||||||
struct Error *error =
|
|
||||||
validate_spec_json(&fixture->config, fixture->parsed, &fixture->prompts);
|
|
||||||
sput_fail_unless(
|
|
||||||
error->code == ERROR_VALIDATOR_TOP_LEVEL_NOT_OBJECT, "top-level not object"
|
|
||||||
);
|
|
||||||
error_free(error);
|
|
||||||
|
|
||||||
test_validator_teardown(fixture);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_validator_field_not_object() {
|
|
||||||
struct TestValidatorFixture *fixture =
|
|
||||||
test_validator_setup("{\"key\": \"$UNKNOWN\"}");
|
|
||||||
|
|
||||||
struct Error *error =
|
|
||||||
validate_spec_json(&fixture->config, fixture->parsed, &fixture->prompts);
|
|
||||||
sput_fail_unless(
|
|
||||||
error->code == ERROR_VALIDATOR_FIELD_NOT_OBJECT, "field not object"
|
|
||||||
);
|
|
||||||
error_free(error);
|
|
||||||
|
|
||||||
test_validator_teardown(fixture);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_validator_field_name_leading_digit() {
|
|
||||||
struct TestValidatorFixture *fixture = test_validator_setup(
|
|
||||||
"{"
|
|
||||||
" \"1abc\": {"
|
|
||||||
" \"type\": \"line\""
|
|
||||||
" }"
|
|
||||||
"}"
|
|
||||||
);
|
|
||||||
|
|
||||||
struct Error *error =
|
|
||||||
validate_spec_json(&fixture->config, fixture->parsed, &fixture->prompts);
|
|
||||||
sput_fail_unless(
|
|
||||||
error->code == ERROR_VALIDATOR_FIELD_NAME_INVALID,
|
|
||||||
"field name leading digit"
|
|
||||||
);
|
|
||||||
error_free(error);
|
|
||||||
|
|
||||||
test_validator_teardown(fixture);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_validator_field_name_non_alnum() {
|
|
||||||
struct TestValidatorFixture *fixture = test_validator_setup(
|
|
||||||
"{"
|
|
||||||
" \"a~bc\": {"
|
|
||||||
" \"type\": \"line\""
|
|
||||||
" }"
|
|
||||||
"}"
|
|
||||||
);
|
|
||||||
|
|
||||||
struct Error *error =
|
|
||||||
validate_spec_json(&fixture->config, fixture->parsed, &fixture->prompts);
|
|
||||||
sput_fail_unless(
|
|
||||||
error->code == ERROR_VALIDATOR_FIELD_NAME_INVALID, "field name non alnum"
|
|
||||||
);
|
|
||||||
error_free(error);
|
|
||||||
|
|
||||||
test_validator_teardown(fixture);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_validator_field_type_invalid() {
|
|
||||||
struct TestValidatorFixture *fixture = test_validator_setup(
|
|
||||||
"{"
|
|
||||||
" \"key\": {"
|
|
||||||
" \"type\": 2"
|
|
||||||
" }"
|
|
||||||
"}"
|
|
||||||
);
|
|
||||||
|
|
||||||
struct Error *error =
|
|
||||||
validate_spec_json(&fixture->config, fixture->parsed, &fixture->prompts);
|
|
||||||
sput_fail_unless(
|
|
||||||
error->code == ERROR_VALIDATOR_FIELD_TYPE_INVALID, "field type invalid"
|
|
||||||
);
|
|
||||||
error_free(error);
|
|
||||||
|
|
||||||
test_validator_teardown(fixture);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_validator_field_type_unknown() {
|
|
||||||
struct TestValidatorFixture *fixture = test_validator_setup(
|
|
||||||
"{"
|
|
||||||
" \"key\": {"
|
|
||||||
" \"type\": \"UNKNOWN\""
|
|
||||||
" }"
|
|
||||||
"}"
|
|
||||||
);
|
|
||||||
|
|
||||||
struct Error *error =
|
|
||||||
validate_spec_json(&fixture->config, fixture->parsed, &fixture->prompts);
|
|
||||||
sput_fail_unless(
|
|
||||||
error->code == ERROR_VALIDATOR_FIELD_TYPE_UNKNOWN, "field type unknown"
|
|
||||||
);
|
|
||||||
error_free(error);
|
|
||||||
|
|
||||||
test_validator_teardown(fixture);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_validator_valid_type_ci() {
|
|
||||||
struct TestValidatorFixture *fixture = test_validator_setup(
|
|
||||||
"{"
|
|
||||||
" \"key\": {"
|
|
||||||
" \"type\": \"LiNe\","
|
|
||||||
" \"prompt\": \"What value for key?\""
|
|
||||||
" }"
|
|
||||||
"}"
|
|
||||||
);
|
|
||||||
|
|
||||||
struct Error *error =
|
|
||||||
validate_spec_json(&fixture->config, fixture->parsed, &fixture->prompts);
|
|
||||||
sput_fail_unless(error == 0, "valid");
|
|
||||||
|
|
||||||
test_validator_teardown(fixture);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_validator_field_required_invalid() {
|
|
||||||
struct TestValidatorFixture *fixture = test_validator_setup(
|
|
||||||
"{"
|
|
||||||
" \"key\": {"
|
|
||||||
" \"type\": \"line\","
|
|
||||||
" \"required\": 5"
|
|
||||||
" }"
|
|
||||||
"}"
|
|
||||||
);
|
|
||||||
|
|
||||||
struct Error *error =
|
|
||||||
validate_spec_json(&fixture->config, fixture->parsed, &fixture->prompts);
|
|
||||||
sput_fail_unless(
|
|
||||||
error->code == ERROR_VALIDATOR_FIELD_REQUIRED_INVALID,
|
|
||||||
"field required invalid"
|
|
||||||
);
|
|
||||||
error_free(error);
|
|
||||||
|
|
||||||
test_validator_teardown(fixture);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_validator_field_required_valid() {
|
|
||||||
struct TestValidatorFixture *fixture = test_validator_setup(
|
|
||||||
"{"
|
|
||||||
" \"key\": {"
|
|
||||||
" \"type\": \"line\","
|
|
||||||
" \"required\": true,"
|
|
||||||
" \"prompt\": \"What value for key? \""
|
|
||||||
" }"
|
|
||||||
"}"
|
|
||||||
);
|
|
||||||
|
|
||||||
struct Error *error =
|
|
||||||
validate_spec_json(&fixture->config, fixture->parsed, &fixture->prompts);
|
|
||||||
sput_fail_unless(error == 0, "required valid");
|
|
||||||
error_free(error);
|
|
||||||
|
|
||||||
test_validator_teardown(fixture);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_validator_field_prompt_invalid() {
|
|
||||||
struct TestValidatorFixture *fixture = test_validator_setup(
|
|
||||||
"{"
|
|
||||||
" \"key\": {"
|
|
||||||
" \"type\": \"line\","
|
|
||||||
" \"prompt\": 2"
|
|
||||||
" }"
|
|
||||||
"}"
|
|
||||||
);
|
|
||||||
|
|
||||||
struct Error *error =
|
|
||||||
validate_spec_json(&fixture->config, fixture->parsed, &fixture->prompts);
|
|
||||||
sput_fail_unless(
|
|
||||||
error->code == ERROR_VALIDATOR_FIELD_PROMPT_INVALID, "field prompt invalid"
|
|
||||||
);
|
|
||||||
error_free(error);
|
|
||||||
|
|
||||||
test_validator_teardown(fixture);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_validator_valid_no_required() {
|
|
||||||
struct TestValidatorFixture *fixture = test_validator_setup(
|
|
||||||
"{"
|
|
||||||
" \"key\": {"
|
|
||||||
" \"type\": \"line\","
|
|
||||||
" \"prompt\": \"What value for key?\""
|
|
||||||
" }"
|
|
||||||
"}"
|
|
||||||
);
|
|
||||||
|
|
||||||
struct Error *error =
|
|
||||||
validate_spec_json(&fixture->config, fixture->parsed, &fixture->prompts);
|
|
||||||
sput_fail_unless(error == 0, "valid");
|
|
||||||
|
|
||||||
test_validator_teardown(fixture);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_validator_field_type_yes() {
|
|
||||||
struct TestValidatorFixture *fixture = test_validator_setup(
|
|
||||||
"{"
|
|
||||||
" \"abc\": {"
|
|
||||||
" \"type\": \"yes\""
|
|
||||||
" \"prompt\": \"What value for key?\""
|
|
||||||
" }"
|
|
||||||
"}"
|
|
||||||
);
|
|
||||||
|
|
||||||
struct Error *error =
|
|
||||||
validate_spec_json(&fixture->config, fixture->parsed, &fixture->prompts);
|
|
||||||
sput_fail_unless(error == 0, "yes valid");
|
|
||||||
|
|
||||||
test_validator_teardown(fixture);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* _BOOTSTRAP_TEST_VALIDATOR */
|
|
Loading…
Reference in New Issue