Skip to content

Commit

Permalink
Merge pull request #2 from branbick/unit-testing
Browse files Browse the repository at this point in the history
Unit testing
  • Loading branch information
branbick authored May 6, 2022
2 parents 6934392 + 2226b86 commit a3a5f63
Show file tree
Hide file tree
Showing 50 changed files with 1,508 additions and 61 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
**/*.exe
**/*.out
**/build
.vscode/settings.json
24 changes: 12 additions & 12 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
"-fdiagnostics-color=always",
"-g",
"${file}",
"../init_from_input_file.c", // Project-unique
"../init_tools.c", // Project-unique
"${workspaceFolder}/src/init_from_input_file.c",
"${workspaceFolder}/src/init_tools.c",
"-Wall",
"-Wextra",
"-Wpedantic",
"-std=c90", // Project-unique
"-std=c90",
"-o",
"${fileDirname}/${fileBasenameNoExtension}.out"
],
Expand All @@ -38,12 +38,12 @@
"-fdiagnostics-color=always",
"-g",
"${file}",
"../init_from_input_file.c", // Project-unique
"../init_tools.c", // Project-unique
"${workspaceFolder}/src/init_from_input_file.c",
"${workspaceFolder}/src/init_tools.c",
"-Wall",
"-Wextra",
"-Wpedantic",
"-std=c++17", // Project-unique
"-std=c++17",
"-o",
"${fileDirname}/${fileBasenameNoExtension}.out"
],
Expand All @@ -64,12 +64,12 @@
"-fdiagnostics-color=always",
"-g",
"${file}",
"..\\init_from_input_file.c", // Project-unique
"..\\init_tools.c", // Project-unique
"${workspaceFolder}\\src\\init_from_input_file.c",
"${workspaceFolder}\\src\\init_tools.c",
"-Wall",
"-Wextra",
"-Wpedantic",
"-std=c90", // Project-unique
"-std=c90",
"-o",
"${fileDirname}\\${fileBasenameNoExtension}.exe"
],
Expand All @@ -90,12 +90,12 @@
"-fdiagnostics-color=always",
"-g",
"${file}",
"..\\init_from_input_file.c", // Project-unique
"..\\init_tools.c", // Project-unique
"${workspaceFolder}\\src\\init_from_input_file.c",
"${workspaceFolder}\\src\\init_tools.c",
"-Wall",
"-Wextra",
"-Wpedantic",
"-std=c++17", // Project-unique
"-std=c++17",
"-o",
"${fileDirname}\\${fileBasenameNoExtension}.exe"
],
Expand Down
2 changes: 1 addition & 1 deletion LICENSE.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2021 Brandon Bickerstaff
Copyright (c) 2022 Brandon Bickerstaff

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
35 changes: 28 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# init-from-input-file
## Introduction
`init-from-input-file` allows C and C++ variables to be initialized from an input text file, consequently **eliminating the need to recompile** source code merely due to a changed initialization value. For example, consider a simulation of an air-launch missile. Instead of writing ...
`init-from-input-file` allows C and C++ variables to be initialized from an input text file, consequently **eliminating the need to recompile** source code merely due to a changed initialization value. For example, consider a simulation of an air-launched missile. Instead of writing ...
```
const double kAltitudeInitial = 10000.0; /* meters (m) */
```
where the right-hand side (i.e., floating constant / floating-point literal) must be updated for different scenarios, requiring recompilation of the source code after every update, write ...
```
double altitudeInitial; /* meters (m) */
double altitudeInitial = 0.0; /* meters (m) */
const bool kSuccessFlag = initFromInputFile("missile.inp", "double", "altInit", &altitudeInitial);
```
where `init_from_input_file.h` must be `#include`d. Breaking down the above, the input text file `missile.inp` is searched for the key `altInit`, and the value corresponding to that key is assigned to `altitudeInitial` (passed by reference), which is a `double`; and, assuming success, `true` is returned by `initFromInputFile`. A relevant snippet of `missile.inp` could be ...
where `src/init_from_input_file.h` must be `#include`d. Breaking down the above, the input text file `missile.inp` is searched for the key `altInit`, and the value corresponding to that key is assigned to `altitudeInitial` (passed by reference), which is a `double`; and, assuming success, `true` is returned by `initFromInputFile`. A relevant snippet of `missile.inp` could be ...
```
latInit 33.2385 # Initial latitude in degrees (deg)
lonInit -106.3464 # Initial longitude in degrees (deg)
Expand Down Expand Up @@ -57,21 +57,22 @@ The following table lists the supported `kVarType` arguments that can be passed
| `"string"` | `char*` |

## How to use this tool
1. Download the source code from [GitHub](https://github.com/branbick/init-from-input-file) into the appropriate folder on your machine.
2. `#include` the header file `init_from_input_file.h` in the C and/or C++ source code you desire to use the aforementioned functionality in.
1. Download the source code from [GitHub](https://github.com/branbick/init-from-input-file) into the appropriate directory on your machine.
2. `#include` the header file `src/init_from_input_file.h` in the C and/or C++ source code you desire to use the aforementioned functionality in.
3. Call `initFromInputFile`--which is declared in the latter header file--with the following arguments, in order:
1. `const char* kFileName`: The relative or absolute path of the input text file
2. `const char* kVarType`: The type of the to-be-initialized variable (discussed in the previous section)
3. `const char* kKeyName`: The name of the key to search the input text file for, corresponding to the value to initialize the to-be-initialized variable with
4. `void* pVar`: The address of the to-be-initialized variable
4. Incorporate the source files `src/init_from_input_file.c` and `src/init_tools.c` into the build. (The former `#include`s `src/init_tools.h`.)

## Additional notes
## Miscellaneous notes
### Compatibility
One of the primary objectives of this project was ensuring 1) compatibility with C *and* C++ and 2) backward compatibility. Hence, this tool is written in [ANSI C](https://en.wikipedia.org/wiki/ANSI_C) and compatible with [C++17](https://en.wikipedia.org/wiki/C%2B%2B17).

### User responsibilities
Due to the nature of C, as well as various deliberate design decisions made for the sakes of both efficiency and simplicity, this tool grants the user power, which must be accompanied by responsibility. That being said, some noteworthy user responsibilities are as follows:
- Ensure the type of the to-be-initialized variable is capable of accurately storing the corresponding value listed in the input text file. For example, the value corresponding to an `int` must be between `INT_MIN` and `INT_MAX`, which are machine-dependent and `#define`d in `limits.h`. (Otherwise, integer overflow--which may lead to undefined behavior--or loss of precision will occur.)
- Ensure the type of the to-be-initialized variable is capable of accurately storing the corresponding value listed in the input text file. For example, concerning C code, the value corresponding to an `int` must be inclusively between `INT_MIN` and `INT_MAX`, which are machine-dependent and `#define`d in `limits.h`. (Otherwise, integer overflow--which may lead to undefined behavior--or loss of precision will occur.)
- Regarding strings, ensure sufficient memory is allocated for the to-be-initialized variable based on the length of the corresponding value listed in the input text file. For example, the variable corresponding to the value `Hey, Bob!` must be able to hold (at least) ten `char`s--including the null terminator--and, therefore, be defined (in C code specifically) as either `char str[10]` or `char* str = malloc(10 * sizeof(char))`. [Otherwise, you'll end up "touching" memory you shouldn't and (hopefully!) be presented with an error.]
- As previously mentioned, ensure string values listed in the input text file both start and end with a double quotation mark. [If the opening one isn't included, the first `char` of the value will be skipped; and, if the closing one isn't included, you'll likely end up "touching" memory you shouldn't and definitely encounter an `initFromInputFile` failure.]
- Check `initFromInputFile`'s return value; don't just assume it worked. There are many things--both machine and user faults (e.g., a memory shortage and an inconspicuous typo, respectively)--that can cause it to fail.
Expand All @@ -84,3 +85,23 @@ If the macro `PRINT_ERRORS` is `#define`d, then a message containing the followi
4. `kKeyName` argument passed to `initFromInputFile`

Detailed error messages greatly help with debugging. If you decide to not always `#define PRINT_ERRORS`, you should *absolutely* do so if `initFromInputFile` has returned `false`.

## Unit testing
Every function that's unique to this tool--i.e., not part of the [standard library](https://en.wikipedia.org/wiki/C_standard_library)--has been thoroughly unit tested using [GoogleTest](https://github.com/google/googletest) (framework) paired with [CMake](https://cmake.org/) (build system).

### How to run the unit tests*
`cd` into the `test` directory and execute `./run_tests.sh`, which then triggers all the `run_test.sh` scripts located in the following subdirectories:
1. `init_from_input_file/non_static_fxns`
2. `init_from_input_file/static_fxns`
3. `init_tools/non_static_fxns/other`
4. `init_tools/non_static_fxns/printError`
5. `init_tools/static_fxns`

\* Only works on Linux

### Future improvements
- Add preprocessor directives that allow the tests to run with or without `PRINT_ERRORS` `#define`d
- Currently, all but one of the unit tests only work if `PRINT_ERRORS` is *not* `#define`d
- `init_tools_non_static_fxns_printError_test` inherently requires `PRINT_ERRORS` to be `#define`d
- Try to use [test fixtures](https://google.github.io/googletest/primer.html#same-data-multiple-tests) to avoid code duplication
- Regarding the `run_test.sh` scripts, figure out how to get (or construct) the names of the `.cpp` files so they don't have to be hard-coded
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
5 changes: 5 additions & 0 deletions test/archive/build_cmds_print_errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
C:
gcc tester_c.c ../../src/init_from_input_file.c ../../src/init_tools.c -D PRINT_ERRORS -Wall -Wextra -Wpedantic -std=c90 -o tester_c_print_errors.out

C++:
g++ tester_cpp.cpp ../../src/init_from_input_file.c ../../src/init_tools.c -D PRINT_ERRORS -Wall -Wextra -Wpedantic -std=c++17 -o tester_cpp_print_errors.out
30 changes: 30 additions & 0 deletions test/archive/integral_types_min_max.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
Build command:
gcc integral_types_min_max.c -Wall -Wextra -Wpedantic -std=c90 -o integral_types_min_max.out
*/

#include <stdio.h>
#include <limits.h>

int main(void)
{
printf(" CHAR_MIN: % d\n", CHAR_MIN);
printf(" CHAR_MAX: % d\n", CHAR_MAX);
printf("SCHAR_MIN: % d\n", SCHAR_MIN);
printf("SCHAR_MAX: % d\n", SCHAR_MAX);
printf("UCHAR_MAX: %u\n\n", UCHAR_MAX);

printf(" SHRT_MIN: % hd\n", SHRT_MIN);
printf(" SHRT_MAX: % hd\n", SHRT_MAX);
printf("USHRT_MAX: %hu\n\n", USHRT_MAX);

printf(" INT_MIN: % d\n", INT_MIN);
printf(" INT_MAX: % d\n", INT_MAX);
printf(" UINT_MAX: %u\n\n", UINT_MAX);

printf(" LONG_MIN: % ld\n", LONG_MIN);
printf(" LONG_MAX: % ld\n", LONG_MAX);
printf("ULONG_MAX: %lu\n\n", ULONG_MAX);

return 0;
}
File renamed without changes.
7 changes: 4 additions & 3 deletions testing/tester_c.c → test/archive/tester_c.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "../init_from_input_file.h"
#include "../../src/init_from_input_file.h"

#include <stdbool.h>
#include <stdio.h>
Expand Down Expand Up @@ -123,8 +123,9 @@ int main(void)

/*
long double
NOTE: If using MinGW, have to prefix scanf (init_fxns ->
init_from_input_file) and printf (below) with __mingw_.
NOTE: If using MinGW, have to prefix printf (IF statement below) and fscanf
(init_from_input_file.c > initNonBoolNonStr) with "__mingw_"--without
quotation marks.
Refer to https://stackoverflow.com/a/14988103.
*/
varLdouble = 0.0L;
Expand Down
6 changes: 3 additions & 3 deletions testing/tester_cpp.cpp → test/archive/tester_cpp.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "../init_from_input_file.h"
#include "../../src/init_from_input_file.h"

#include <iostream>
#include <iomanip>
Expand Down Expand Up @@ -111,8 +111,8 @@ int main()
std::cout << "fail" << std::endl;

// long double
// NOTE: If using MinGW, have to prefix scanf (init_from_input_file ->
// initNonBoolNonStr) with __mingw_.
// NOTE: If using MinGW, have to prefix fscanf (init_from_input_file.c >
// initNonBoolNonStr) with "__mingw_"--without quotation marks.
// Refer to https://stackoverflow.com/a/14988103.
long double varLdouble = 0.0L;
successFlag = initFromInputFile("tester.inp", "ldouble", "keyLdouble", &varLdouble);
Expand Down
39 changes: 39 additions & 0 deletions test/init_from_input_file/non_static_fxns/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
cmake_minimum_required(VERSION 3.22.1)

set(project_name init_from_input_file_non_static_fxns_test)

set(CMAKE_CXX_STANDARD 17) # GoogleTest requires at least C++11

project(
${project_name}
LANGUAGES C CXX
DESCRIPTION "Testing the non-static functions declared in \
src/init_from_input_file.h"
)

# Use GoogleTest without having it installed locally beforehand
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/refs/tags/release-1.11.0.zip
)
FetchContent_MakeAvailable(googletest)

enable_testing()

add_executable(
${project_name}
${project_name}.cpp
../../../src/init_from_input_file.c
../../../src/init_tools.c
)

target_compile_options(${project_name} PRIVATE -Wall -Wextra -Wpedantic)

target_link_libraries(
${project_name}
gtest_main
)

include(GoogleTest)
gtest_discover_tests(${project_name})
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
charSuccess A
scharSuccess -1
ucharSuccess 1
shortSuccess -2
ushortSuccess 2
intSuccess -3
uintSuccess 3
longSuccess -4
ulongSuccess 4
floatSuccess 0.12345
doubleSuccess 0.123456789
ldoubleSuccess 0.12345678987654321
boolSuccess true
stringSuccess "Hello, world!"
Loading

0 comments on commit a3a5f63

Please sign in to comment.