diff --git a/Makefile b/Makefile index 145a7ee..28fc450 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ all: all-examples EXAMPLES = ex/example1 ex/example2 ex/example3 ex/example4 \ - ex/example5 ex/example6 ex/example7 ex/example8 \ + ex/example5 ex/example6 ex/example7 ex/example8 ex/example11 ex/main-wrap \ 'ex ws/example1 ws' 'ex ws/example2 ws' 'ex ws/example3 ws' 'ex ws/example4 ws' \ 'ex ws/example5 ws' 'ex ws/example6 ws' 'ex ws/example7 ws' 'ex ws/example8 ws' diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..38bf864 --- /dev/null +++ b/TODO.md @@ -0,0 +1,148 @@ +# Todo (nick) + +- [x] Look if possible to invoke task from launch.json, and shows in button. +- > conclusion: Not possible to add button, or custom "Run" task: possible to just run it +- [x] Determine if file is implementation or test (in tests directory) +- [x] CLion debugging + +## 19.04 + +- [/] investigate files appearing when running run configurations +- [/] analyze problem of shell scripts (when/where interpreter is chosen) +- [/] look workings with shell/IO tests + +- If time, generate automatically tests + - ~ CLI to generate folder and file structure + - lookup Unity C + +-> Ideas: m4 to fill up boilerplate + ```m4 + include(bt.m4) // implicit + + Suite(name) // sets current suite/file to generate + Test_compile() + Test_alloc_ok(type) // type is the type declaration + + ``` +-> Nope + +## 26.04 + +- [ ] look workings with shell/IO tests + - Run can just run everything + - Debug has to make choiches, so better to have a debug target handling it?: + - make debug file.ext + - > if ext == c; then compile, cp to debugme, and run it (ez, done); + > if ext == in | expected; then needs some input redirection (hard); + > if ext == sh; then needs to run the program as in the shell script?? (Hardest); +- [x] investigate files appearing when running run configurations + - ? touch only with `if [[ -f ... ]]; then touch ... fi;`? + - touch $fileName$ can create file in projectDir directory; + -> TODO: should all be from same dir as makefile, instead of projectDir? +- [ ] analyze problem of shell scripts (when/where interpreter is chosen) +- [x] VScode not showning tasks anymore (ex/example11) + -> .vscode or .idea folder must be in root of the project + +## 10.05 + +- Will there ever be test sets with more than one of IO / sh / c-cxx? + - (y/n): (__**Kinda, shell could also be IO, but all confined in .sh file**__) + - Specific run configuration file for each type of set? + - makefile to make all decisions, based on `TESTS_(IO|SH|C|CXX)` variables? + +- Types + - Shell: Used to test/run binary implementation with specific arguments (e.g cli), (!?!)or other uses? + - I/O: Used to test/run binary implementation, with specific stdin, and expected output + - c/cpp src files: Used to test library implementations + +- Debugging problems: + - Library: None, just copy the test + - Binary programs: + - how to select the correct compiled binary, to copy into a file named `debugme`? + - Need to open src file anyway? + - Makefile script to choose when project is library, or test suite, so which compiled binary to copy. + - usually I/O or shell, so see below problems + - Shell: + - how to get the arguments present in the shell script, to pass to the binary? + - I/O: choose which .in file to run + - Manual prompt? + + +- Proposal: + - Common `Run all` configuration + - CLion: Run configuration (Button) + - VScode: Run task, instructions needed for keyboard shortcut + - Ctrl + Shift + B + - Specific debug configuration, for each test suite type (either) + - Library: currently open `test.c` or `test.cc` file + - IO: prompt to pickup file for stdin redirect + - Clion: either file, or name prompt + - VScode: can be configurable/generetable, by adding a specific field at the end of the file + +### TODO + +- [ ] Stabilize current solution + - Check which files are necessary to make everything work + - check that current stuff works +- [ ] explore debugging for sh files +- [x] complete section 1 of report, start section 2 + +## 17.05 + +- Linker: wrap (dll-library to substitute symbols) + - anche per test IO + - studente crea un main + - altro programma con "vero" main, va a chiamare main altro! + - linker:`--wrap = main` + - Take every undefined reference to symbol, and transform in symbol `__wrap_symbol_` + - => define wrapper + - IO: intercept stodut of program: more difficult + - New version: capture IO functions (mostly O), for pinpoint comparison! + From LD manual: +```txt --wrap=symbol +Use a wrapper function for symbol. Any undefined reference to +symbol will be resolved to "__wrap_symbol". Any undefined +reference to "__real_symbol" will be resolved to symbol. + +This can be used to provide a wrapper for a system function. The +wrapper function should be called "__wrap_symbol". If it wishes to +call the system function, it should call "__real_symbol". + +Here is a trivial example: + + void * + __wrap_malloc (size_t c) + { + printf ("malloc called with %zu\n", c); + return __real_malloc (c); + } + +If you link other code with this file using --wrap malloc, then all +calls to "malloc" will call the function "__wrap_malloc" instead. +The call to "__real_malloc" in "__wrap_malloc" will call the real +"malloc" function. + +You may wish to provide a "__real_malloc" function as well, so that +links without the --wrap option will succeed. If you do this, you +should not put the definition of "__real_malloc" in the same file +as "__wrap_malloc"; if you do, the assembler may resolve the call +before the linker has a chance to wrap it to "malloc". + +Only undefined references are replaced by the linker. So, +translation unit internal references to symbol are not resolved to +"__wrap_symbol". In the next example, the call to "f" in "g" is +not resolved to "__wrap_f". + + int + f (void) + { + return 123; + } + + int + g (void) + { + return f(); + } +``` +Order of compilation may matter. diff --git a/basic_testing.h b/basic_testing.h index 92b0437..85c4b03 100644 --- a/basic_testing.h +++ b/basic_testing.h @@ -448,4 +448,26 @@ int bt_test_driver(int argc, char * argv[]) { } #endif +// IO tests macros +#define FILENAME(ext) \ + char ext##_filename[128] = {0}; \ + strncat(ext##_filename, __FILE__, strlen(__FILE__)-1); \ + strcat(ext##_filename, #ext) + +#define RUN_PROGRAM(...) \ + extern int __real_main(int, char*[]); \ + int __wrap_main() { \ + FILENAME(in); \ + FILENAME(out); \ + FILENAME(expected); \ + BT_POSSIBLY_UNUSED FILE* in_file = freopen(in_filename, "r", stdin); \ + BT_POSSIBLY_UNUSED FILE* out_file = freopen(out_filename, "w", stdout); \ + int result = __real_main(2, (char*[]) { __VA_ARGS__ }); \ + if (in_file != NULL) fclose(in_file); \ + fclose(out_file); \ + if (result != 0) { \ + fprintf(stderr, "Implementation exited with value: %d", result);} \ + return result; \ + } + #endif /* BASIC_TESTING_H_INCLUDED */ diff --git a/basic_testing.mk b/basic_testing.mk index b6f8bdf..2c772fd 100644 --- a/basic_testing.mk +++ b/basic_testing.mk @@ -21,6 +21,8 @@ TESTS_BIN:=$(patsubst $(TESTS_DIR)/%.c, $(TESTS_DIR)/%, $(TESTS_C)) \ $(patsubst $(TESTS_DIR)/%.cc, $(TESTS_DIR)/%, $(TESTS_CXX)) TESTS_BIN_NAMES:=$(sort $(patsubst $(TESTS_DIR)/%.c, %, $(TESTS_C)) $(patsubst $(TESTS_DIR)/%.cc, %, $(TESTS_CXX))) +TESTS_HYBRID:=$(wildcard $(TESTS_DIR)/*.expected) + .PHONY: all all: compile check @@ -49,7 +51,12 @@ SUPPRESS_DIAGNOSTICS=yes else SUPPRESS_DIAGNOSTICS= endif + TEST_COLORS=yes +# $TERM variable not always defined, but required by tput (Eg, CLion run console). +ifeq ($(TERM),) +TEST_COLORS=no +endif ifeq ($(TEST_COLORS),no) COLOR_RED := COLOR_GREEN := @@ -171,7 +178,7 @@ check-io-sh: compile $(TESTS_IO) $(TESTS_SH) $(PROGRAMS_DRIVERS) else \ test_ko FAIL ;\ test_diag "see $(TESTS_DIR)/$$t.sh" ;\ - test_diag "run diff $$t.out $(TESTS_DIR)/$$t.expected";\ + test_diag "run diff -c $$t.out $(TESTS_DIR)/$$t.expected";\ test_diag "to see the difference between the actual and expected output";\ fi; \ fi; \ @@ -199,19 +206,50 @@ check-bin: $(TESTS_BIN) "$(TESTS_DIR)/$$t" -q &\ fi; \ $(SCRIPT_GET_TEST_RESULT); \ + if test $$res = KO || test "$(TESTS_HYBRID)" != "" && ! cmp -s "$(TESTS_DIR)/$$t.out" "$(TESTS_DIR)/$$t.expected"; then \ + if test "$(TESTS_HYBRID)" != ""; then \ + test_ko FAIL ; \ + test_diag "run 'diff -c $(TESTS_DIR)/$$t.out $(TESTS_DIR)/$$t.expected' to see the mistakes"; \ + else \ + test_diag "run '$(TESTS_DIR)/$$t' to see what went wrong"; \ + fi; \ + test_diag "run '$(TESTS_DIR)/$$t -d' with a debugger" ; \ + else \ + test_ok PASS; \ + if test ! -z $$TESTS_HYBRID; then rm -f "$(TESTS_DIR)/$$t.out"; fi; \ + fi; \ + done; \ + test_summary 'Summary: PASS ' + +.PHONY: check-single-bin +check-single-bin: $(BIN_NAME) + @exec 2> /dev/null; \ + if test -z $$BIN_NAME; then \ + echo "Error: Missing test to run, please set BIN_NAME="; \ + echo " example: \'make check-single-bin BIN_NAME=tests/test0\'"; \ + exit 1; fi; \ + $(SCRIPT_INIT); \ + t=$$BIN_NAME; \ + test_start "$$t"; \ + if test -n "$(WITH_VALGRIND)"; then \ + echo ;\ + valgrind $(VALGRIND_FLAGS) "$(TESTS_DIR)/$$t" 2>&1 &\ + else \ + "$(TESTS_DIR)/$$t" $(BIN_FLAGS) &\ + fi; \ + $(SCRIPT_GET_TEST_RESULT); \ if test $$res = KO; then \ test_diag "run '$(TESTS_DIR)/$$t' to see what went wrong" ; \ test_diag "run '$(TESTS_DIR)/$$t -d' with a debugger" ; \ else \ test_ok PASS; \ fi; \ - done; \ test_summary 'Summary: PASS ' .PHONY: clean clean: rm -f $(PROGRAMS) *-valgrind $(OBJECTS) tests/*.o $(TESTS_BIN) \ - *.gcov *.gcda *.gcno tests/*.gcov tests/*.gcda tests/*.gcno + tests/debugme *.gcov *.gcda *.gcno tests/*.gcov tests/*.gcda tests/*.gcno tests/*.out .PHONY: coverage coverage: diff --git a/ex/.idea/.gitignore b/ex/.idea/.gitignore new file mode 100644 index 0000000..54d4519 --- /dev/null +++ b/ex/.idea/.gitignore @@ -0,0 +1,3 @@ +/workspace.xml +/vcs.xml +/editor.xml diff --git a/ex/.idea/CLion-bt-tools.zip b/ex/.idea/CLion-bt-tools.zip new file mode 100644 index 0000000..7f8a379 Binary files /dev/null and b/ex/.idea/CLion-bt-tools.zip differ diff --git a/ex/.idea/README.md b/ex/.idea/README.md new file mode 100644 index 0000000..d9b337a --- /dev/null +++ b/ex/.idea/README.md @@ -0,0 +1,17 @@ +# Basic testing Jetbrains CLion config + +## Setup + +- Import the external tools from the `CLion-bt-tools.zip` into your IDE, from `File > Manage IDE settings > import settings...`; +> These tools are used to interface the IDE with the make scripts, and are necessary to use the run/debug configurations from CLion. +> They are stored globally, but you can easely delete them once they're not needed anymore. +- ensure that the `.idea` folder is located in the root of the exercise, and the folder of the exercise is the only one opened. + +## Files + +Contains: +- 3 configurations + - `Run`: to run a single test file (file in the `tests/` folder) selected in the editor, or all the tests when an implementation file is selected (or any file in the same folder as the `Makefile`) + - `Debug Single test`: when run in `Debug` mode, allows to use the integrated debugger of CLion to debug a C or C++ test (opened and selected in the editor) + - `Debug IO`: when run in `Debug` mode, allows to debug the IO tests (with `test.in` and `test.out` files) +- `CLion-bt-tools.zip`: Tools folder to import tests diff --git a/ex/.idea/customTargets.xml b/ex/.idea/customTargets.xml new file mode 100644 index 0000000..f8984c7 --- /dev/null +++ b/ex/.idea/customTargets.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ex/.idea/misc.xml b/ex/.idea/misc.xml new file mode 100644 index 0000000..53624c9 --- /dev/null +++ b/ex/.idea/misc.xml @@ -0,0 +1,18 @@ + + + + + + + + \ No newline at end of file diff --git a/ex/.idea/runConfigurations/Debug_IO.xml b/ex/.idea/runConfigurations/Debug_IO.xml new file mode 100644 index 0000000..bc31ad5 --- /dev/null +++ b/ex/.idea/runConfigurations/Debug_IO.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/ex/.idea/runConfigurations/Debug_single_test.xml b/ex/.idea/runConfigurations/Debug_single_test.xml new file mode 100644 index 0000000..887bb14 --- /dev/null +++ b/ex/.idea/runConfigurations/Debug_single_test.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/ex/.idea/runConfigurations/Run.xml b/ex/.idea/runConfigurations/Run.xml new file mode 100644 index 0000000..10969af --- /dev/null +++ b/ex/.idea/runConfigurations/Run.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/ex/.idea/tools/Basic Testing.xml b/ex/.idea/tools/Basic Testing.xml new file mode 100644 index 0000000..2716789 --- /dev/null +++ b/ex/.idea/tools/Basic Testing.xml @@ -0,0 +1,16 @@ + + + + + + + + + + \ No newline at end of file diff --git a/ex/.vscode/README.md b/ex/.vscode/README.md new file mode 100644 index 0000000..2c9804d --- /dev/null +++ b/ex/.vscode/README.md @@ -0,0 +1,14 @@ +# Basic testing VScode config + +## Setup + +- ensure that the `.vscode` folder is located in the root of the project opened in VScode; this can either be the folder of the single exercise, or a folder containing all other exercise's folders. + +## Files + +Contains 2 json files: +- `tasks.json`: build and run tasks, to run either all tests, or a single one (C test file opened in the editor). + - Ctrl + Shift + B, to run default build + - Ctrl + P to open command panel, type "task " to show all tasks +- `launch.json`: debug binary tests or implementation, and I/O tests (name of input file selectable from the dropdown). + - Ctrl + Shift + D to open Debug panel diff --git a/ex/.vscode/launch.json b/ex/.vscode/launch.json new file mode 100644 index 0000000..797b772 --- /dev/null +++ b/ex/.vscode/launch.json @@ -0,0 +1,84 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "debug library test (gdb/lldb)", + // Requires C/C++ extension from Microsoft + "type": "cppdbg", + "request": "launch", + "cwd": "${fileDirname}", + "preLaunchTask": "Build single test", + "program": "${fileDirname}/${fileBasenameNoExtension}", + "args": ["-d"], + // Requires C/C++ extension from Microsoft + "MIMode": "${input:debugger}", + // "MIMode": "gdb", + // "MIMode": "lldb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ], + }, + { + "name": "debug binary IO (gdb/lldb)", + // Requires C/C++ extension from Microsoft + "type": "cppdbg", + "request": "launch", + "cwd": "${fileDirname}", + "preLaunchTask": "Build and run tests", + "program": "${fileDirname}/${fileBasenameNoExtension}", + "args": ["-d", "<", "tests/${input:test}"], + // Requires C/C++ extension from Microsoft + "MIMode": "${input:debugger}", + // "MIMode": "gdb", + // "MIMode": "lldb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ], + }, + ], + "inputs": [ + { + "type": "pickString", + "id": "debugger", + "description": "Which debugger do you want to use?", + "options": [ + "gdb", + "lldb", + ], + "default": "gdb" + }, + { + "type": "pickString", + "id": "test", + "description": "Pick the input file to pass to the program", + // TODO: make it so it's generated at runtime, or on test generation! + "options": [ + "test0.in", + "test1.in", + "test2.in", + ], + "default": "test0.in" + }, + ], +} diff --git a/ex/.vscode/tasks.json b/ex/.vscode/tasks.json new file mode 100644 index 0000000..caa354f --- /dev/null +++ b/ex/.vscode/tasks.json @@ -0,0 +1,74 @@ + +{ +"version": "2.0.0", +"tasks": [ + { + "label": "Build and run tests", + "detail": "Runs all tests, or a single test when opened in the editor", + "type": "shell", + "presentation": { + "reveal": "always" + }, + "options": { + "cwd": "${fileDirname}/", + }, + "command": " if [[ ${fileDirnameBasename} == tests ]]; then touch ${file}; cd .. && make check-single-bin BIN_NAME=tests/${fileBasenameNoExtension}; else touch ${file}; make; fi", + "problemMatcher": ["$gcc", + { + "owner": "make", + "fileLocation":[ + "relative", + "${fileDirname}/tests/" + ], + "pattern": + { + // Matches "Running test test_name... FAIL" + "regexp": "^Running test\\s(.+)\\.\\.\\.\\s+FAIL$", + "file": 1, + "message": 1, + "kind": "file" + }, + } + ], + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "label": "Build single test", + "detail": "Builds and runs a single test (open file in editor)", + "type": "shell", + "presentation": { + "reveal": "always", + "focus": true + }, + "options": { + "cwd": "${fileDirname}/..", + }, + "command": "make check-single-bin BIN_NAME=tests/${fileBasenameNoExtension}", + "problemMatcher": ["$gcc", + { + "owner": "make", + "fileLocation":[ + "relative", + "${fileDirname}/tests/" + ], + "pattern": + { + // Matches "Running test test_name... FAIL" + "regexp": "^Running test\\s(.+)\\.\\.\\.\\s+FAIL$", + "file": 1, + "message": 1, + "kind": "file" + }, + } + ], + "group": { + "kind": "test", + "isDefault": true + } + } + +] +} diff --git a/ex/example11.expected b/ex/example11.expected new file mode 100644 index 0000000..3e54e16 --- /dev/null +++ b/ex/example11.expected @@ -0,0 +1,4 @@ +Running test fail... FAIL +Running test test0... PASS +Running test test1... PASS +Running test timeout... FAIL diff --git a/ex/example11/.idea b/ex/example11/.idea new file mode 120000 index 0000000..a3f7d20 --- /dev/null +++ b/ex/example11/.idea @@ -0,0 +1 @@ +../.idea/ \ No newline at end of file diff --git a/ex/example11/.vscode b/ex/example11/.vscode new file mode 120000 index 0000000..7796dd0 --- /dev/null +++ b/ex/example11/.vscode @@ -0,0 +1 @@ +../.vscode/ \ No newline at end of file diff --git a/ex/example11/Makefile b/ex/example11/Makefile new file mode 100644 index 0000000..e3e112e --- /dev/null +++ b/ex/example11/Makefile @@ -0,0 +1,3 @@ +OBJECTS=implementation.o + +include ../../basic_testing.mk diff --git a/ex/example11/implementation.c b/ex/example11/implementation.c new file mode 100644 index 0000000..8d61712 --- /dev/null +++ b/ex/example11/implementation.c @@ -0,0 +1,7 @@ +#include "implementation.h" + +int init_foo(struct foo* data) { + int result = 1; + data->character = '0'; + return 1; +} diff --git a/ex/example11/implementation.h b/ex/example11/implementation.h new file mode 100644 index 0000000..b6861d8 --- /dev/null +++ b/ex/example11/implementation.h @@ -0,0 +1,5 @@ +struct foo { + char character; +}; + +int init_foo(struct foo*); \ No newline at end of file diff --git a/ex/example11/tests/fail.c b/ex/example11/tests/fail.c new file mode 100644 index 0000000..70451b4 --- /dev/null +++ b/ex/example11/tests/fail.c @@ -0,0 +1,12 @@ +#include "basic_testing.h" + +TEST(fail) { + int result = 0; + TEST_FAILED; +} + +TEST(fail_second) { + TEST_FAILED; +} + +MAIN_TEST_DRIVER(fail, fail_second); diff --git a/ex/example11/tests/test0.c b/ex/example11/tests/test0.c new file mode 100644 index 0000000..64932a1 --- /dev/null +++ b/ex/example11/tests/test0.c @@ -0,0 +1,14 @@ +#include "basic_testing.h" +#include "../implementation.h" + +TEST(compile) { + TEST_PASSED; +} + +TEST(with_variable) { + int variable = 0; + variable++; + TEST_PASSED; +} + +MAIN_TEST_DRIVER(compile, with_variable); diff --git a/ex/example11/tests/test1.c b/ex/example11/tests/test1.c new file mode 100644 index 0000000..a3ca2b2 --- /dev/null +++ b/ex/example11/tests/test1.c @@ -0,0 +1,10 @@ +#include "basic_testing.h" +#include "../implementation.h" + +TEST(initialize) { + struct foo data; + int result = init_foo(&data); + TEST_PASSED; +} + +MAIN_TEST_DRIVER(initialize); \ No newline at end of file diff --git a/ex/example11/tests/timeout.c b/ex/example11/tests/timeout.c new file mode 100644 index 0000000..0ce4ef7 --- /dev/null +++ b/ex/example11/tests/timeout.c @@ -0,0 +1,10 @@ +#include "basic_testing.h" +#include + + +TEST(timeout) { + sleep(10); + TEST_PASSED; +} + +MAIN_TEST_DRIVER(timeout); \ No newline at end of file diff --git a/ex/example5/.idea b/ex/example5/.idea new file mode 120000 index 0000000..a3f7d20 --- /dev/null +++ b/ex/example5/.idea @@ -0,0 +1 @@ +../.idea/ \ No newline at end of file diff --git a/ex/main-wrap.expected b/ex/main-wrap.expected new file mode 100644 index 0000000..2787976 --- /dev/null +++ b/ex/main-wrap.expected @@ -0,0 +1,2 @@ +Running test test0... PASS +Running test test4... PASS diff --git a/ex/main-wrap/Makefile b/ex/main-wrap/Makefile new file mode 100644 index 0000000..7b1d7c9 --- /dev/null +++ b/ex/main-wrap/Makefile @@ -0,0 +1,9 @@ +# PROGRAMS = diamond # we need to treat the binary file as an object, to be able to link on it with the --wrap option +OBJECTS=diamond.o +LDFLAGS=-Wl,--wrap=main + +# TODO: check if it's still required! +TESTS_IO:=$(wildcard $(TESTS_DIR)/*.in) +TESTS_IO_NAMES:=$(sort $(patsubst $(TESTS_DIR)/%.in, %, $(TESTS_IO))) + +include ../../basic_testing.mk diff --git a/ex/main-wrap/diamond.c b/ex/main-wrap/diamond.c new file mode 100644 index 0000000..b87dbb6 --- /dev/null +++ b/ex/main-wrap/diamond.c @@ -0,0 +1,26 @@ +#include +#include + +void print_diamond(int n) { + for (int i = 1; i <= n; ++i) { + for (int j = n - i; j > 0; --j) + putchar(' '); + for (int j = 1; j < 2*i; ++j) + putchar('#'); + putchar('\n'); + } + for (int i = n-1; i >= 1; --i) { + for (int j = n - i; j > 0; --j) + putchar(' '); + for (int j = 1; j < 2*i; ++j) + putchar('#'); + putchar('\n'); + } +} + +int main(int argc, char * argv[]) { + int n = 10; + if (argc > 1) + n = atoi(argv[1]); + print_diamond(n); +} diff --git a/ex/main-wrap/tests/test0.c b/ex/main-wrap/tests/test0.c new file mode 100644 index 0000000..a10e123 --- /dev/null +++ b/ex/main-wrap/tests/test0.c @@ -0,0 +1,4 @@ +#include "basic_testing.h" + +RUN_PROGRAM("diamond", "0"); + diff --git a/ex/main-wrap/tests/test0.expected b/ex/main-wrap/tests/test0.expected new file mode 100644 index 0000000..e69de29 diff --git a/ex/main-wrap/tests/test4.c b/ex/main-wrap/tests/test4.c new file mode 100644 index 0000000..6925789 --- /dev/null +++ b/ex/main-wrap/tests/test4.c @@ -0,0 +1,3 @@ +#include "basic_testing.h" + +RUN_PROGRAM("diamond", "10"); diff --git a/ex/main-wrap/tests/test4.expected b/ex/main-wrap/tests/test4.expected new file mode 100644 index 0000000..a70af5d --- /dev/null +++ b/ex/main-wrap/tests/test4.expected @@ -0,0 +1,19 @@ + # + ### + ##### + ####### + ######### + ########### + ############# + ############### + ################# +################### + ################# + ############### + ############# + ########### + ######### + ####### + ##### + ### + # diff --git a/ex/main-wrap/tests/test4.in b/ex/main-wrap/tests/test4.in new file mode 100644 index 0000000..3045e4e --- /dev/null +++ b/ex/main-wrap/tests/test4.in @@ -0,0 +1 @@ +HEllo world, from redirected stdin!