AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3 4#include <string.h>5#include <stdio.h>6#include <math.h>7 8/* Convenience: milliseconds per day */9#define MS_PER_DAY 86400000LL10 11/* Helper to compare 64-bit integers with a message */12static void assertI64Equal(sqlite3_int64 actual, sqlite3_int64 expected, const char *msg){13 if( actual!=expected ){14 char buf[256];15 sqlite3_snprintf(sizeof(buf), buf, "%s: expected %lld got %lld",16 msg, (long long)expected, (long long)actual);17 TEST_FAIL_MESSAGE(buf);18 }19}20 21/* Initialize a DateTime to the given Y-M-D h:m:s and force iJD via "+0 days" */22static void initYMDHMS(DateTime *p, int Y, int M, int D, int h, int m, double s){23 memset(p, 0, sizeof(*p));24 p->Y = Y; p->M = M; p->D = D; p->validYMD = 1;25 if( h>=0 ){26 p->h = h; p->m = m; p->s = s; p->validHMS = 1;27 }28 /* Force compute of iJD using a no-op relative modifier */29 TEST_ASSERT_EQUAL_INT(0, test_parseModifier(NULL, "0 days", (int)strlen("0 days"), p, 1));30}31 32void setUp(void) {33 /* no-op */34}35void tearDown(void) {36 /* no-op */37}38 39/* auto: idx constraint and conversion */40void test_parseModifier_auto_idx_and_conversion(void){41 DateTime p; memset(&p, 0, sizeof(p));42 /* Ambiguous raw number: seconds since 1970 (0) */43 p.rawS = 1; p.s = 0.0;44 int rc = test_parseModifier(NULL, "auto", (int)strlen("auto"), &p, 1);45 TEST_ASSERT_EQUAL_INT(0, rc);46 TEST_ASSERT_TRUE(p.validJD!=0);47 TEST_ASSERT_EQUAL_INT(0, p.rawS);48 /* iJD == 210866760000000 for 1970-01-01 00:00:00 */49 assertI64Equal(p.iJD, (sqlite3_int64)210866760000000LL, "auto(0) iJD");50 51 /* idx > 1 must fail with no change */52 DateTime q; memset(&q, 0, sizeof(q));53 q.rawS = 1; q.s = 0.0;54 rc = test_parseModifier(NULL, "auto", (int)strlen("auto"), &q, 2);55 TEST_ASSERT_EQUAL_INT(1, rc);56 TEST_ASSERT_EQUAL_INT(1, q.rawS);57 TEST_ASSERT_EQUAL_INT(0, q.validJD);58}59 60/* unixepoch conversion and idx constraint */61void test_parseModifier_unixepoch_conversion(void){62 DateTime p; memset(&p, 0, sizeof(p));63 p.rawS = 1; p.s = 1620000000.0; /* 2021-05-03 00:00:00 UTC */64 int rc = test_parseModifier(NULL, "unixepoch", (int)strlen("unixepoch"), &p, 1);65 TEST_ASSERT_EQUAL_INT(0, rc);66 TEST_ASSERT_TRUE(p.validJD!=0);67 TEST_ASSERT_EQUAL_INT(0, p.rawS);68 sqlite3_int64 expect = (sqlite3_int64)(p.s*1000.0 + 210866760000000.0);69 assertI64Equal(p.iJD, expect, "unixepoch iJD");70 71 /* idx > 1 => error (no conversion) */72 DateTime q; memset(&q, 0, sizeof(q));73 q.rawS = 1; q.s = 1.0;74 rc = test_parseModifier(NULL, "unixepoch", (int)strlen("unixepoch"), &q, 2);75 TEST_ASSERT_EQUAL_INT(1, rc);76 TEST_ASSERT_EQUAL_INT(1, q.rawS);77 TEST_ASSERT_EQUAL_INT(0, q.validJD);78}79 80/* julianday clears rawS only when validJD && rawS and idx==1 */81void test_parseModifier_julianday_behavior(void){82 DateTime p; memset(&p, 0, sizeof(p));83 p.validJD = 1; p.iJD = 123456789LL; p.rawS = 1;84 int rc = test_parseModifier(NULL, "julianday", (int)strlen("julianday"), &p, 1);85 TEST_ASSERT_EQUAL_INT(0, rc);86 TEST_ASSERT_EQUAL_INT(0, p.rawS);87 assertI64Equal(p.iJD, 123456789LL, "julianday preserves iJD");88 89 DateTime q; memset(&q, 0, sizeof(q));90 q.rawS = 1; /* no validJD */91 rc = test_parseModifier(NULL, "julianday", (int)strlen("julianday"), &q, 1);92 TEST_ASSERT_EQUAL_INT(1, rc);93 TEST_ASSERT_EQUAL_INT(1, q.rawS);94 95 DateTime r; memset(&r, 0, sizeof(r));96 r.validJD = 1; /* rawS is 0 */97 rc = test_parseModifier(NULL, "julianday", (int)strlen("julianday"), &r, 1);98 TEST_ASSERT_EQUAL_INT(1, rc);99 100 DateTime s; memset(&s, 0, sizeof(s)); s.validJD=1; s.rawS=1;101 rc = test_parseModifier(NULL, "julianday", (int)strlen("julianday"), &s, 2);102 TEST_ASSERT_EQUAL_INT(1, rc);103 TEST_ASSERT_EQUAL_INT(1, s.rawS);104}105 106/* weekday N: no-op when already on N, and forward otherwise */107void test_parseModifier_weekday_noop_and_forward(void){108 DateTime base; initYMDHMS(&base, 2021, 3, 17, 0, 0, 0.0); /* Wednesday => N=3 */109 sqlite3_int64 jd0 = base.iJD;110 111 DateTime p1 = base;112 int rc = test_parseModifier(NULL, "weekday 3", (int)strlen("weekday 3"), &p1, 1);113 TEST_ASSERT_EQUAL_INT(0, rc);114 assertI64Equal(p1.iJD, jd0, "weekday same day");115 116 DateTime p2 = base;117 rc = test_parseModifier(NULL, "weekday 5", (int)strlen("weekday 5"), &p2, 1);118 TEST_ASSERT_EQUAL_INT(0, rc);119 assertI64Equal(p2.iJD - jd0, 2*MS_PER_DAY, "weekday advance to Friday");120}121 122/* "+HH:MM:SS.FFF" equals adding equivalent seconds */123void test_parseModifier_add_time_hhmmss_equivalent_to_seconds(void){124 DateTime base; initYMDHMS(&base, 2000, 1, 1, 0, 0, 0.0);125 DateTime p1 = base;126 DateTime p2 = base;127 int rc1 = test_parseModifier(NULL, "+01:02:03.250", (int)strlen("+01:02:03.250"), &p1, 1);128 int rc2 = test_parseModifier(NULL, "3723.25 seconds", (int)strlen("3723.25 seconds"), &p2, 1);129 TEST_ASSERT_EQUAL_INT(0, rc1);130 TEST_ASSERT_EQUAL_INT(0, rc2);131 assertI64Equal(p1.iJD, p2.iJD, "time add equivalence");132}133 134/* Adding 1 month from 2021-01-15 equals adding 31 days (to 2021-02-15) */135void test_parseModifier_add_months_no_overflow_equals_days(void){136 DateTime d1; initYMDHMS(&d1, 2021, 1, 15, 0, 0, 0.0);137 DateTime d2 = d1;138 DateTime d3 = d1;139 TEST_ASSERT_EQUAL_INT(0, test_parseModifier(NULL, "1 month", (int)strlen("1 month"), &d2, 1));140 TEST_ASSERT_EQUAL_INT(0, test_parseModifier(NULL, "31 days", (int)strlen("31 days"), &d3, 1));141 assertI64Equal(d2.iJD, d3.iJD, "1 month == 31 days (Jan15->Feb15 2021)");142}143 144/* Month overflow: Jan 31 + 1 month; floor should roll back to Feb 28 (2021) */145void test_parseModifier_month_overflow_floor_vs_default(void){146 DateTime a; memset(&a, 0, sizeof(a)); a.Y=2021; a.M=1; a.D=31; a.validYMD=1;147 DateTime b = a;148 /* default (ceiling) behavior */149 TEST_ASSERT_EQUAL_INT(0, test_parseModifier(NULL, "1 month", (int)strlen("1 month"), &a, 1));150 sqlite3_int64 jd_ceiling = a.iJD;151 /* floor behavior */152 TEST_ASSERT_EQUAL_INT(0, test_parseModifier(NULL, "1 month", (int)strlen("1 month"), &b, 1));153 TEST_ASSERT_EQUAL_INT(0, test_parseModifier(NULL, "floor", (int)strlen("floor"), &b, 2));154 sqlite3_int64 jd_floor = b.iJD;155 assertI64Equal(jd_ceiling - jd_floor, 3*MS_PER_DAY, "Jan31 +1 month: ceiling vs floor diff");156}157 158/* start of day should zero-out the time component */159void test_parseModifier_start_of_day_zeros_time(void){160 DateTime p; initYMDHMS(&p, 2020, 6, 7, 13, 24, 56.5);161 DateTime expectedStart; initYMDHMS(&expectedStart, 2020, 6, 7, 0, 0, 0.0);162 163 /* Apply start of day then force iJD calculation with a no-op */164 TEST_ASSERT_EQUAL_INT(0, test_parseModifier(NULL, "start of day", (int)strlen("start of day"), &p, 1));165 TEST_ASSERT_EQUAL_INT(0, test_parseModifier(NULL, "0 days", (int)strlen("0 days"), &p, 2));166 167 assertI64Equal(p.iJD, expectedStart.iJD, "start of day iJD");168}169 170/* subsec and subsecond set useSubsec flag */171void test_parseModifier_subsec_flags(void){172 DateTime p; memset(&p, 0, sizeof(p));173 TEST_ASSERT_EQUAL_INT(0, test_parseModifier(NULL, "subsec", (int)strlen("subsec"), &p, 1));174 TEST_ASSERT_EQUAL_INT(1, p.useSubsec);175 p.useSubsec = 0;176 TEST_ASSERT_EQUAL_INT(0, test_parseModifier(NULL, "subsecond", (int)strlen("subsecond"), &p, 1));177 TEST_ASSERT_EQUAL_INT(1, p.useSubsec);178}179 180/* +YYYY-MM-DD with time part: compare with +HH:MM equivalence */181void test_parseModifier_compound_year_day_and_time(void){182 DateTime base; initYMDHMS(&base, 2019, 12, 31, 22, 30, 0.0);183 DateTime p1 = base;184 DateTime p2 = base;185 /* Add +0000-00-00 01:30 => +01:30:00 */186 TEST_ASSERT_EQUAL_INT(0, test_parseModifier(NULL, "+0000-00-00 01:30", (int)strlen("+0000-00-00 01:30"), &p1, 1));187 TEST_ASSERT_EQUAL_INT(0, test_parseModifier(NULL, "+01:30:00", (int)strlen("+01:30:00"), &p2, 1));188 assertI64Equal(p1.iJD, p2.iJD, "compound date+time vs hh:mm:ss");189}190 191/* +1 year from leap day, floor vs default (ceiling): expect 1-day difference */192void test_parseModifier_year_addition_floor_vs_ceiling_on_leapday(void){193 DateTime a; memset(&a, 0, sizeof(a)); a.Y=2020; a.M=2; a.D=29; a.validYMD=1;194 DateTime b = a;195 TEST_ASSERT_EQUAL_INT(0, test_parseModifier(NULL, "+0001-00-00", (int)strlen("+0001-00-00"), &a, 1));196 TEST_ASSERT_EQUAL_INT(0, test_parseModifier(NULL, "+0001-00-00", (int)strlen("+0001-00-00"), &b, 1));197 TEST_ASSERT_EQUAL_INT(0, test_parseModifier(NULL, "floor", (int)strlen("floor"), &b, 2));198 sqlite3_int64 diff = a.iJD - b.iJD;199 assertI64Equal(diff, 1*MS_PER_DAY, "Leapday +1 year ceiling vs floor");200}201 202int main(void){203 UNITY_BEGIN();204 RUN_TEST(test_parseModifier_auto_idx_and_conversion);205 RUN_TEST(test_parseModifier_unixepoch_conversion);206 RUN_TEST(test_parseModifier_julianday_behavior);207 RUN_TEST(test_parseModifier_weekday_noop_and_forward);208 RUN_TEST(test_parseModifier_add_time_hhmmss_equivalent_to_seconds);209 RUN_TEST(test_parseModifier_add_months_no_overflow_equals_days);210 RUN_TEST(test_parseModifier_month_overflow_floor_vs_default);211 RUN_TEST(test_parseModifier_start_of_day_zeros_time);212 RUN_TEST(test_parseModifier_subsec_flags);213 RUN_TEST(test_parseModifier_compound_year_day_and_time);214 RUN_TEST(test_parseModifier_year_addition_floor_vs_ceiling_on_leapday);215 return UNITY_END();216}