AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3 4#include <string.h>5#include <stdlib.h>6#include <time.h>7#include <math.h>8 9/* The wrapper for the static function under test */10int test_toLocaltime(DateTime *p, sqlite3_context *pCtx);11 12/* Save/restore globals for osLocaltime fault injection */13static int gSavedLocaltimeFault = 0;14static int (*gSavedAltLocaltime)(const void*, void*) = 0;15 16/* An xAltLocaltime implementation that treats "local time" as UTC using gmtime() */17static int altLocaltimeUTC(const void *pIn, void *pOut){18 time_t t = *(const time_t*)pIn;19 struct tm *pTmX = NULL;20#if defined(HAVE_GMTIME_R) && HAVE_GMTIME_R21 if( gmtime_r(&t, (struct tm*)pOut)==NULL ) return 1;22 return 0;23#else24 pTmX = gmtime(&t);25 if( pTmX==NULL ) return 1;26 *(struct tm*)pOut = *pTmX;27 return 0;28#endif29}30 31/* An xAltLocaltime that always fails */32static int altLocaltimeFail(const void *pIn, void *pOut){33 (void)pIn; (void)pOut;34 return 1;35}36 37/* Helper: initialize a DateTime with Y/M/D h:m s (assumed UTC) */38static void initDateTime(DateTime *p, int Y, int M, int D, int h, int m, double s){39 memset(p, 0, sizeof(*p));40 p->Y = Y;41 p->M = M;42 p->D = D;43 p->h = h;44 p->m = m;45 p->s = s;46 p->validYMD = 1;47 p->validHMS = 1;48 p->tz = 0;49 p->rawS = 0;50}51 52/* Create a minimal sqlite3_context suitable for sqlite3_result_error() */53static void createDummyContext(sqlite3_context *pCtx, Mem *pOut, sqlite3 **ppDb){54 memset(pCtx, 0, sizeof(*pCtx));55 memset(pOut, 0, sizeof(*pOut));56 *ppDb = 0;57 /* Open a database so Mem allocations have a db handle */58 TEST_ASSERT_EQUAL_INT(SQLITE_OK, sqlite3_open(":memory:", ppDb));59 pOut->db = *ppDb;60 pCtx->pOut = pOut;61}62 63void setUp(void) {64 /* Save existing config and force use of our alt localtime implementation */65 gSavedLocaltimeFault = sqlite3GlobalConfig.bLocaltimeFault;66 gSavedAltLocaltime = sqlite3GlobalConfig.xAltLocaltime;67 sqlite3GlobalConfig.bLocaltimeFault = 1;68 sqlite3GlobalConfig.xAltLocaltime = altLocaltimeUTC;69}70 71void tearDown(void) {72 /* Restore global config */73 sqlite3GlobalConfig.bLocaltimeFault = gSavedLocaltimeFault;74 sqlite3GlobalConfig.xAltLocaltime = gSavedAltLocaltime;75}76 77/* Test: basic in-range conversion using deterministic UTC "localtime" */78void test_toLocaltime_basic_inrange(void){79 DateTime dt;80 initDateTime(&dt, 2001, 1, 1, 0, 0, 0.0);81 82 int rc = test_toLocaltime(&dt, NULL);83 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);84 85 TEST_ASSERT_EQUAL_INT(2001, dt.Y);86 TEST_ASSERT_EQUAL_INT(1, dt.M);87 TEST_ASSERT_EQUAL_INT(1, dt.D);88 TEST_ASSERT_EQUAL_INT(0, dt.h);89 TEST_ASSERT_EQUAL_INT(0, dt.m);90 TEST_ASSERT_DOUBLE_WITHIN(1e-9, 0.0, dt.s);91 92 TEST_ASSERT_EQUAL_INT(1, dt.validYMD);93 TEST_ASSERT_EQUAL_INT(1, dt.validHMS);94 TEST_ASSERT_EQUAL_INT(0, dt.validJD);95 TEST_ASSERT_EQUAL_INT(0, dt.tz);96 TEST_ASSERT_EQUAL_INT(0, dt.rawS);97 TEST_ASSERT_EQUAL_INT(0, dt.isError);98}99 100/* Test: milliseconds preserved in p->s after conversion */101void test_toLocaltime_preserves_milliseconds(void){102 DateTime dt;103 initDateTime(&dt, 2010, 3, 5, 6, 7, 8.789);104 105 int rc = test_toLocaltime(&dt, NULL);106 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);107 108 TEST_ASSERT_EQUAL_INT(2010, dt.Y);109 TEST_ASSERT_EQUAL_INT(3, dt.M);110 TEST_ASSERT_EQUAL_INT(5, dt.D);111 TEST_ASSERT_EQUAL_INT(6, dt.h);112 TEST_ASSERT_EQUAL_INT(7, dt.m);113 TEST_ASSERT_DOUBLE_WITHIN(1e-6, 8.789, dt.s);114}115 116/* Test: year mapping for a pre-1970 date (e.g., 1900) and mapping back */117void test_toLocaltime_pre1970_year_mapping(void){118 DateTime dt;119 initDateTime(&dt, 1900, 1, 1, 12, 34, 56.250);120 121 int rc = test_toLocaltime(&dt, NULL);122 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);123 124 TEST_ASSERT_EQUAL_INT(1900, dt.Y);125 TEST_ASSERT_EQUAL_INT(1, dt.M);126 TEST_ASSERT_EQUAL_INT(1, dt.D);127 TEST_ASSERT_EQUAL_INT(12, dt.h);128 TEST_ASSERT_EQUAL_INT(34, dt.m);129 TEST_ASSERT_DOUBLE_WITHIN(1e-6, 56.250, dt.s);130}131 132/* Test: year mapping for a post-2038 date (e.g., 2090) and mapping back */133void test_toLocaltime_post2038_year_mapping(void){134 DateTime dt;135 initDateTime(&dt, 2090, 7, 15, 10, 20, 30.125);136 137 int rc = test_toLocaltime(&dt, NULL);138 TEST_ASSERT_EQUAL_INT(SQLITE_OK, rc);139 140 TEST_ASSERT_EQUAL_INT(2090, dt.Y);141 TEST_ASSERT_EQUAL_INT(7, dt.M);142 TEST_ASSERT_EQUAL_INT(15, dt.D);143 TEST_ASSERT_EQUAL_INT(10, dt.h);144 TEST_ASSERT_EQUAL_INT(20, dt.m);145 TEST_ASSERT_DOUBLE_WITHIN(1e-6, 30.125, dt.s);146}147 148/* Test: osLocaltime failure path triggers SQLITE_ERROR and leaves fields intact */149void test_toLocaltime_oslocaltime_failure(void){150 /* Force osLocaltime to fail */151 sqlite3GlobalConfig.xAltLocaltime = altLocaltimeFail;152 153 DateTime dt;154 initDateTime(&dt, 2005, 12, 31, 23, 59, 59.5);155 156 /* Prepare a minimal context to allow sqlite3_result_error() to run safely */157 sqlite3_context ctx;158 Mem out;159 sqlite3 *db = 0;160 createDummyContext(&ctx, &out, &db);161 162 int rc = test_toLocaltime(&dt, &ctx);163 TEST_ASSERT_EQUAL_INT(SQLITE_ERROR, rc);164 165 /* Ensure original Y/M/D/H/M/S remained unchanged on failure */166 TEST_ASSERT_EQUAL_INT(2005, dt.Y);167 TEST_ASSERT_EQUAL_INT(12, dt.M);168 TEST_ASSERT_EQUAL_INT(31, dt.D);169 TEST_ASSERT_EQUAL_INT(23, dt.h);170 TEST_ASSERT_EQUAL_INT(59, dt.m);171 TEST_ASSERT_DOUBLE_WITHIN(1e-6, 59.5, dt.s);172 173 /* validJD likely set by computeJD(), but validYMD/HMS should be unchanged */174 TEST_ASSERT_EQUAL_INT(1, dt.validYMD);175 TEST_ASSERT_EQUAL_INT(1, dt.validHMS);176 177 /* Cleanup context memory */178 sqlite3VdbeMemRelease(&out);179 sqlite3_close(db);180}181 182int main(void){183 UNITY_BEGIN();184 RUN_TEST(test_toLocaltime_basic_inrange);185 RUN_TEST(test_toLocaltime_preserves_milliseconds);186 RUN_TEST(test_toLocaltime_pre1970_year_mapping);187 RUN_TEST(test_toLocaltime_post2038_year_mapping);188 RUN_TEST(test_toLocaltime_oslocaltime_failure);189 return UNITY_END();190}