1 /* Copyright (c) 2011, 2021, Oracle and/or its affiliates.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License, version 2.0, for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software Foundation,
21    51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
22 
23 // First include (the generated) my_config.h, to get correct platform defines.
24 #include "my_config.h"
25 #include <gtest/gtest.h>
26 #include "test_utils.h"
27 
28 #include "decimal.h"
29 #include "my_decimal.h"
30 #include "my_time.h"
31 #include "mysql_time.h"
32 #include "sql_time.h"
33 #include "m_ctype.h"
34 
35 #include "item.h"
36 #include "item_timefunc.h"
37 
38 #include <string.h>
39 #include <sstream>
40 #include <string>
41 
42 namespace item_timefunc_unittest {
43 
44 using my_testing::Server_initializer;
45 
46 class ItemTimeFuncTest : public ::testing::Test
47 {
48 protected:
SetUp()49   virtual void SetUp()
50   {
51     initializer.SetUp();
52   }
53 
TearDown()54   virtual void TearDown() { initializer.TearDown(); }
55 
thd()56   THD *thd() { return initializer.thd(); }
57 
58   Server_initializer initializer;
59 };
60 
TEST_F(ItemTimeFuncTest,dateAddInterval)61 TEST_F(ItemTimeFuncTest, dateAddInterval)
62 {
63   Item_int *arg0= new Item_int(20130122145221LL); // 2013-01-22 14:52:21
64   Item_decimal *arg1= new Item_decimal(0.1234567, 8, 7);
65   Item *item=
66     new Item_date_add_interval(POS(),
67                                arg0, arg1, INTERVAL_SECOND_MICROSECOND, false);
68   Parse_context pc(thd(), thd()->lex->current_select());
69   EXPECT_FALSE(item->itemize(&pc, &item));
70   EXPECT_FALSE(item->fix_fields(thd(), NULL));
71 
72   // The below result is not correct, see Bug#16198372
73   EXPECT_DOUBLE_EQ(20130122145222.234567, item->val_real());
74 }
75 
76 struct test_data
77 {
78   const char *secs;
79   unsigned int hour;
80   unsigned int minute;
81   unsigned int second;
82   unsigned long second_part;
83 };
84 
operator <<(::std::ostream & os,const struct test_data & data)85 ::std::ostream& operator<<(::std::ostream& os, const struct test_data& data) {
86   return os << data.secs;
87 }
88 
89 
90 class ItemTimeFuncTestP : public ::testing::TestWithParam<test_data>
91 {
92 protected:
SetUp()93   virtual void SetUp()
94   {
95     initializer.SetUp();
96     m_t= GetParam();
97   }
98 
TearDown()99   virtual void TearDown() { initializer.TearDown(); }
100 
thd()101   THD *thd() { return initializer.thd(); }
102 
103   Server_initializer initializer;
104   test_data m_t;
105 };
106 
107 const test_data test_values[]=
108 {
109   { "0.1234564", 0, 0, 0, 123456 },
110   { "0.1234567", 0, 0, 0, 123457 },
111   { "0.1234", 0, 0, 0, 123400 },
112   { "12.1234567", 0, 0, 12, 123457},
113   { "123", 0, 2, 3, 0 },
114   { "2378.3422349", 0, 39, 38, 342235 },
115   { "3020398.999999999", 838, 59, 59, 0 },
116   { "3020399", 838, 59, 59, 0 },
117   { "99999999.99999999", 838, 59, 59, 0 }
118 };
119 
120 INSTANTIATE_TEST_CASE_P(a, ItemTimeFuncTestP,
121                         ::testing::ValuesIn(test_values));
122 
123 /**
124   Test member function of @c Item_time_func
125 
126   @param item     item of a sub-class of @c Item_time_func
127   @param ltime    time structure that contains the expected result
128   @param decimals number of significant decimals in the expected result
129 */
testItemTimeFunctions(Item_time_func * item,MYSQL_TIME * ltime,int decimals)130 void testItemTimeFunctions(Item_time_func *item, MYSQL_TIME *ltime,
131 			   int decimals)
132 {
133   long long int mysql_time=
134     10000 * ltime->hour + 100 * ltime->minute + ltime->second;
135   EXPECT_EQ(mysql_time, item->val_int());
136 
137   long long int packed= TIME_to_longlong_packed(ltime);
138   EXPECT_EQ(packed, item->val_time_temporal());
139 
140   double d= mysql_time + ltime->second_part / 1000000.0;
141   EXPECT_DOUBLE_EQ(d, item->val_real());
142 
143   my_decimal decval1, decval2;
144   my_decimal *dec= item->val_decimal(&decval1);
145   double2decimal(d, &decval2);
146   EXPECT_EQ(0, my_decimal_cmp(dec, &decval2));
147 
148   char s[20];
149   sprintf(s, "%02d:%02d:%02d", ltime->hour, ltime->minute, ltime->second);
150   if (ltime->second_part > 0) { // Avoid trailing zeroes
151     int decs= ltime->second_part;
152     while (decs % 10 == 0)
153       decs /= 10;
154     sprintf(s + strlen(s), ".%d", decs);
155   }
156   else if (decimals > 0)
157     // There were decimals, but they have disappeared due to overflow
158     sprintf(s + strlen(s), ".000000");
159   String timeStr(20);
160   EXPECT_STREQ(s, item->val_str(&timeStr)->c_ptr());
161 
162   MYSQL_TIME ldate;
163   //> Second argument of Item_func_time::get_date is not used for anything
164   item->get_date(&ldate, 0);
165   // Todo: Should check that year, month, and day is relative to current date
166   EXPECT_EQ(ltime->hour % 24, ldate.hour);
167   EXPECT_EQ(ltime->minute, ldate.minute);
168   EXPECT_EQ(ltime->second, ldate.second);
169   EXPECT_EQ(ltime->second_part, ldate.second_part);
170 
171   // Todo: Item_time_func::save_in_field is not tested
172 }
173 
TEST_P(ItemTimeFuncTestP,secToTime)174 TEST_P(ItemTimeFuncTestP, secToTime)
175 {
176   Item_decimal *sec=
177     new Item_decimal(POS(), m_t.secs, strlen(m_t.secs), &my_charset_latin1_bin);
178   Item_func_sec_to_time *time= new Item_func_sec_to_time(POS(), sec);
179 
180   Parse_context pc(thd(), thd()->lex->current_select());
181   Item *item;
182   EXPECT_FALSE(time->itemize(&pc, &item));
183   EXPECT_EQ(time, item);
184   EXPECT_FALSE(time->fix_fields(thd(), NULL));
185 
186   MYSQL_TIME ltime;
187   time->get_time(&ltime);
188   EXPECT_EQ(0U, ltime.year);
189   EXPECT_EQ(0U, ltime.month);
190   EXPECT_EQ(0U, ltime.day);
191   EXPECT_EQ(m_t.hour, ltime.hour);
192   EXPECT_EQ(m_t.minute, ltime.minute);
193   EXPECT_EQ(m_t.second, ltime.second);
194   EXPECT_EQ(m_t.second_part, ltime.second_part);
195 
196   testItemTimeFunctions(time, &ltime, sec->decimals);
197 }
198 
199 }
200