diff options
| author | Ethan Werner <mail@ethanwerner.com> | 2026-07-01 09:42:39 +0400 |
|---|---|---|
| committer | Ethan Werner <mail@ethanwerner.com> | 2026-07-01 09:42:39 +0400 |
| commit | 07e18043145f3d91c8ea7ec3ad41b3c905585277 (patch) | |
| tree | 2bdeae46973f4bdfce263679a4008ac768011f28 /test/edge_cases.c | |
initial commit: ts time series analysis library v0.1.0
- 354 financial and mathematical time-series indicators
- Uniform C11 API with consistent function signatures
- 349 passing tests with -Werror
- Static and shared library builds
- pkg-config integration
- Python bindings
- Fuzzing and sanitizer support
- Security hardening flags
Diffstat (limited to 'test/edge_cases.c')
| -rw-r--r-- | test/edge_cases.c | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/test/edge_cases.c b/test/edge_cases.c new file mode 100644 index 0000000..ce47f72 --- /dev/null +++ b/test/edge_cases.c @@ -0,0 +1,324 @@ +#include <math.h> +#include <stdbool.h> +#include <stddef.h> +#include "ts.h" + +bool ts_test_edge_cases( void ) +{ + bool result = true; + + /* === NULL input === */ + + /* average with NULL input should return -1 */ + { + uint_t k = 3; + int ret = ts_average(NULL, 0, NULL, 0, 5, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* rsi with NULL input should return -1 */ + { + uint_t k = 3; + int ret = ts_rsi(NULL, 0, NULL, 0, 5, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* === n = 0 (empty input) === */ + + /* average with n=0 should return early, no crash */ + { + uint_t k = 3; + int ret = ts_average(NULL, 0, NULL, 0, 0, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* rsi with n=0 should not crash */ + { + uint_t k = 3; + int ret = ts_rsi(NULL, 0, NULL, 0, 0, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* deviation_standard with n=0 should return early */ + { + uint_t k = 3; + int ret = ts_deviation_standard(NULL, 0, NULL, 0, 0, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* variance with n=0 should return early */ + { + uint_t k = 3; + int ret = ts_variance(NULL, 0, NULL, 0, 0, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* === n = 1 (single element) === */ + + /* average with n=1, k=1 should produce the element itself */ + { + fp_t const x[] = {5}; + fp_t const e[] = {5}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 1; + result &= ts_test(ts_average, ts_average_offset, inp, exp, 1, (ts_constant_t *)&k, &ts_average_p); + } + + /* deviation_standard with n=1, k=1 should produce 0 */ + { + fp_t const x[] = {5}; + fp_t const e[] = {0}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 1; + result &= ts_test(ts_deviation_standard, ts_deviation_standard_offset, inp, exp, 1, (ts_constant_t *)&k, &ts_deviation_standard_p); + } + + /* variance with n=1, k=1 should produce 0 */ + { + fp_t const x[] = {5}; + fp_t const e[] = {0}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 1; + result &= ts_test(ts_variance, ts_variance_offset, inp, exp, 1, (ts_constant_t *)&k, &ts_variance_p); + } + + /* === k = 0 (zero period) === */ + + /* average with k=0 should return early */ + { + fp_t x[] = {1, 2, 3, 4, 5}; + fp_t y[5] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 0; + int ret = ts_average(out, 1, inp, 1, 5, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* rsi with k=0 should return early */ + { + fp_t x[] = {1, 2, 3, 4, 5}; + fp_t y[5] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 0; + int ret = ts_rsi(out, 1, inp, 1, 5, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* deviation_standard with k=0 should return early */ + { + fp_t x[] = {1, 2, 3, 4, 5}; + fp_t y[5] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 0; + int ret = ts_deviation_standard(out, 1, inp, 1, 5, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* variance with k=0 should return early */ + { + fp_t x[] = {1, 2, 3, 4, 5}; + fp_t y[5] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 0; + int ret = ts_variance(out, 1, inp, 1, 5, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* === k > n (period exceeds data) === */ + + /* average with k > n should return early */ + { + fp_t x[] = {1, 2, 3}; + fp_t y[3] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 10; + int ret = ts_average(out, 1, inp, 1, 3, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* deviation_standard with k > n should return early */ + { + fp_t x[] = {1, 2, 3}; + fp_t y[3] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 10; + int ret = ts_deviation_standard(out, 1, inp, 1, 3, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* variance with k > n should return early */ + { + fp_t x[] = {1, 2, 3}; + fp_t y[3] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 10; + int ret = ts_variance(out, 1, inp, 1, 3, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* === All-zero input === */ + + /* average with all-zero input should produce zeros */ + { + fp_t const x[] = {0, 0, 0, 0, 0}; + fp_t const e[] = {0, 0, 0}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 3; + result &= ts_test(ts_average, ts_average_offset, inp, exp, 5, (ts_constant_t *)&k, &ts_average_p); + } + + /* rsi with all-zero input should not crash (all gains/losses are 0) */ + { + fp_t const x[] = {0, 0, 0, 0, 0, 0, 0}; + fp_t const e[] = {100, 100, 100, 100, 100}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 2; + result &= ts_test(ts_rsi, ts_rsi_offset, inp, exp, 7, (ts_constant_t *)&k, &ts_rsi_p); + } + + /* deviation_standard with all-zero input should produce 0 */ + { + fp_t const x[] = {0, 0, 0, 0, 0}; + fp_t const e[] = {0, 0, 0}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 3; + result &= ts_test(ts_deviation_standard, ts_deviation_standard_offset, inp, exp, 5, (ts_constant_t *)&k, &ts_deviation_standard_p); + } + + /* variance with all-zero input should produce 0 */ + { + fp_t const x[] = {0, 0, 0, 0, 0}; + fp_t const e[] = {0, 0, 0}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 3; + result &= ts_test(ts_variance, ts_variance_offset, inp, exp, 5, (ts_constant_t *)&k, &ts_variance_p); + } + + /* === All-same input (zero variance) === */ + + /* average with all-same input should produce that value */ + { + fp_t const x[] = {7, 7, 7, 7, 7}; + fp_t const e[] = {7, 7, 7}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 3; + result &= ts_test(ts_average, ts_average_offset, inp, exp, 5, (ts_constant_t *)&k, &ts_average_p); + } + + /* rsi with all-same input should produce 100 (no losses) */ + { + fp_t const x[] = {5, 5, 5, 5, 5, 5, 5}; + fp_t const e[] = {100, 100, 100, 100, 100}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 2; + result &= ts_test(ts_rsi, ts_rsi_offset, inp, exp, 7, (ts_constant_t *)&k, &ts_rsi_p); + } + + /* deviation_standard with all-same input should produce 0 */ + { + fp_t const x[] = {3, 3, 3, 3, 3}; + fp_t const e[] = {0, 0, 0}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 3; + result &= ts_test(ts_deviation_standard, ts_deviation_standard_offset, inp, exp, 5, (ts_constant_t *)&k, &ts_deviation_standard_p); + } + + /* variance with all-same input should produce 0 */ + { + fp_t const x[] = {3, 3, 3, 3, 3}; + fp_t const e[] = {0, 0, 0}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 3; + result &= ts_test(ts_variance, ts_variance_offset, inp, exp, 5, (ts_constant_t *)&k, &ts_variance_p); + } + + /* === n == k boundary === */ + + /* average with n==k should succeed (produces 1 output) */ + { + fp_t const x[] = {1, 2, 3}; + fp_t const e[] = {2}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 3; + result &= ts_test(ts_average, ts_average_offset, inp, exp, 3, (ts_constant_t *)&k, &ts_average_p); + } + + /* rsi with n==k should fail (requires n > k) */ + { + fp_t x[] = {1, 2, 3}; + fp_t y[3] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 3; + int ret = ts_rsi(out, 1, inp, 1, 3, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* === NaN input === */ + + /* average with NaN input should return -1 (invalid input) */ + { + fp_t x[] = {NAN, 1.0, 2.0, 3.0, 4.0}; + fp_t y[5] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 3; + int ret = ts_average(out, 1, inp, 1, 5, (ts_constant_t *)&k); + result &= (ret == -1); + } + + /* === Monotonic input (RSI edge cases) === */ + + /* rsi with strictly ascending input should produce 100 */ + { + fp_t const x[] = {1, 2, 3, 4, 5, 6, 7}; + fp_t const e[] = {100, 100, 100, 100, 100}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 2; + result &= ts_test(ts_rsi, ts_rsi_offset, inp, exp, 7, (ts_constant_t *)&k, &ts_rsi_p); + } + + /* rsi with strictly descending input should produce 0 */ + { + fp_t const x[] = {7, 6, 5, 4, 3, 2, 1}; + fp_t const e[] = {0, 0, 0, 0, 0}; + fp_t const * const inp[1] = {x}; + fp_t const * const exp[1] = {e}; + uint_t k = 2; + result &= ts_test(ts_rsi, ts_rsi_offset, inp, exp, 7, (ts_constant_t *)&k, &ts_rsi_p); + } + + /* === rsi with k > n === */ + + { + fp_t x[] = {1, 2, 3}; + fp_t y[3] = {0}; + fp_t const * const inp[1] = {x}; + fp_t * const out[1] = {y}; + uint_t k = 10; + int ret = ts_rsi(out, 1, inp, 1, 3, (ts_constant_t *)&k); + result &= (ret == -1); + } + + return result; +} |