AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <string.h>4#include <math.h>5#include <stdlib.h>6 7/* Declaration of the provided test wrapper for the static function */8extern void test_juliandayFunc(sqlite3_context *context, int argc, sqlite3_value **argv);9 10static sqlite3 *gDb = NULL;11 12/* Unity setup/teardown */13void setUp(void) {14 int rc = sqlite3_open(":memory:", &gDb);15 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "Failed to open in-memory SQLite DB");16}17 18void tearDown(void) {19 if (gDb) {20 sqlite3_close(gDb);21 gDb = NULL;22 }23}24 25/* Helper to allocate a sqlite3_value containing UTF-8 text */26static sqlite3_value *makeTextValue(const char *z){27 Mem *pVal = sqlite3ValueNew(gDb);28 TEST_ASSERT_NOT_NULL_MESSAGE(pVal, "sqlite3ValueNew returned NULL");29 sqlite3ValueSetStr(pVal, (int)strlen(z), z, SQLITE_UTF8, SQLITE_TRANSIENT);30 return (sqlite3_value*)pVal;31}32 33/* Helper to allocate a sqlite3_value containing a double */34static sqlite3_value *makeDoubleValue(double r){35 Mem *pVal = sqlite3ValueNew(gDb);36 TEST_ASSERT_NOT_NULL_MESSAGE(pVal, "sqlite3ValueNew returned NULL");37 sqlite3VdbeMemSetDouble(pVal, r);38 return (sqlite3_value*)pVal;39}40 41/* Helper to free a sqlite3_value */42static void freeValue(sqlite3_value *v){43 if( v ){44 sqlite3ValueFree((Mem*)v);45 }46}47 48/* Invoke julianday via the wrapper with prepared argv and capture result.49 Returns the output type via *pTypeOut (SQLITE_*) and the double value. */50static double invoke_julianday_and_get_double(int argc, sqlite3_value **argv, int *pTypeOut){51 sqlite3_context ctx;52 Mem out;53 memset(&ctx, 0, sizeof(ctx));54 memset(&out, 0, sizeof(out));55 out.db = gDb;56 ctx.pOut = &out;57 58 test_juliandayFunc(&ctx, argc, argv);59 60 if( pTypeOut ){61 *pTypeOut = sqlite3_value_type((sqlite3_value*)&out);62 }63 64 /* Even if NULL, returning this is fine for callers that only check type first */65 return sqlite3_value_double((sqlite3_value*)&out);66}67 68/* Convenience: invoke with a single text argument */69static double call_julianday_text1(const char *z, int *pTypeOut){70 sqlite3_value *argv[1];71 argv[0] = makeTextValue(z);72 double r = invoke_julianday_and_get_double(1, argv, pTypeOut);73 freeValue(argv[0]);74 return r;75}76 77/* Convenience: invoke with a text argument plus one text modifier */78static double call_julianday_text_plus1mod(const char *z, const char *mod, int *pTypeOut){79 sqlite3_value *argv[2];80 argv[0] = makeTextValue(z);81 argv[1] = makeTextValue(mod);82 double r = invoke_julianday_and_get_double(2, argv, pTypeOut);83 freeValue(argv[0]);84 freeValue(argv[1]);85 return r;86}87 88/* Tests */89 90void test_juliandayFunc_epoch_1970_01_01(void){91 int t = 0;92 double jd = call_julianday_text1("1970-01-01 00:00:00", &t);93 TEST_ASSERT_EQUAL_INT(SQLITE_FLOAT, t);94 TEST_ASSERT_DOUBLE_WITHIN(1e-9, 2440587.5, jd);95}96 97void test_juliandayFunc_2000_01_01(void){98 int t = 0;99 double jd = call_julianday_text1("2000-01-01", &t);100 TEST_ASSERT_EQUAL_INT(SQLITE_FLOAT, t);101 TEST_ASSERT_DOUBLE_WITHIN(1e-9, 2451544.5, jd);102}103 104void test_juliandayFunc_numeric_passthrough(void){105 /* Pass a numeric JD and expect the same value */106 sqlite3_value *argv[1];107 argv[0] = makeDoubleValue(2451544.5);108 int t = 0;109 double jd = invoke_julianday_and_get_double(1, argv, &t);110 TEST_ASSERT_EQUAL_INT(SQLITE_FLOAT, t);111 TEST_ASSERT_DOUBLE_WITHIN(1e-9, 2451544.5, jd);112 freeValue(argv[0]);113}114 115void test_juliandayFunc_with_modifier_plus_one_day(void){116 int t = 0;117 double jd = call_julianday_text_plus1mod("1970-01-01", "+1 day", &t);118 TEST_ASSERT_EQUAL_INT(SQLITE_FLOAT, t);119 TEST_ASSERT_DOUBLE_WITHIN(1e-9, 2440588.5, jd);120}121 122void test_juliandayFunc_timezone_Z(void){123 int t = 0;124 double jd = call_julianday_text1("1970-01-01T00:00:00Z", &t);125 TEST_ASSERT_EQUAL_INT(SQLITE_FLOAT, t);126 TEST_ASSERT_DOUBLE_WITHIN(1e-9, 2440587.5, jd);127}128 129void test_juliandayFunc_invalid_input_results_in_null(void){130 int t = -1;131 double jd = call_julianday_text1("not a date", &t);132 (void)jd; /* Unused: result should be NULL */133 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_NULL, t, "Invalid input should produce a NULL result");134}135 136void test_juliandayFunc_normalizes_overflowed_day(void){137 int t1 = 0, t2 = 0;138 double jd1 = call_julianday_text1("2023-02-31", &t1);139 double jd2 = call_julianday_text1("2023-03-03", &t2);140 TEST_ASSERT_EQUAL_INT(SQLITE_FLOAT, t1);141 TEST_ASSERT_EQUAL_INT(SQLITE_FLOAT, t2);142 TEST_ASSERT_DOUBLE_WITHIN(1e-9, jd2, jd1);143}144 145/* Unity main */146int main(void){147 UNITY_BEGIN();148 RUN_TEST(test_juliandayFunc_epoch_1970_01_01);149 RUN_TEST(test_juliandayFunc_2000_01_01);150 RUN_TEST(test_juliandayFunc_numeric_passthrough);151 RUN_TEST(test_juliandayFunc_with_modifier_plus_one_day);152 RUN_TEST(test_juliandayFunc_timezone_Z);153 RUN_TEST(test_juliandayFunc_invalid_input_results_in_null);154 RUN_TEST(test_juliandayFunc_normalizes_overflowed_day);155 return UNITY_END();156}