1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "annotator/grammar/dates/utils/date-match.h"
18
19 #include <stdint.h>
20
21 #include <string>
22
23 #include "annotator/grammar/dates/dates_generated.h"
24 #include "annotator/grammar/dates/timezone-code_generated.h"
25 #include "annotator/grammar/dates/utils/date-utils.h"
26 #include "utils/strings/append.h"
27 #include "gtest/gtest.h"
28
29 namespace libtextclassifier3 {
30 namespace dates {
31 namespace {
32
33 class DateMatchTest : public ::testing::Test {
34 protected:
35 enum {
36 X = NO_VAL,
37 };
38
DOW_X()39 static DayOfWeek DOW_X() { return DayOfWeek_DOW_NONE; }
SUN()40 static DayOfWeek SUN() { return DayOfWeek_SUNDAY; }
41
BCAD_X()42 static BCAD BCAD_X() { return BCAD_BCAD_NONE; }
BC()43 static BCAD BC() { return BCAD_BC; }
44
SetDate(DateMatch * date,int year,int8 month,int8 day,DayOfWeek day_of_week=DOW_X (),BCAD bc_ad=BCAD_X ())45 DateMatch& SetDate(DateMatch* date, int year, int8 month, int8 day,
46 DayOfWeek day_of_week = DOW_X(), BCAD bc_ad = BCAD_X()) {
47 date->year = year;
48 date->month = month;
49 date->day = day;
50 date->day_of_week = day_of_week;
51 date->bc_ad = bc_ad;
52 return *date;
53 }
54
SetTimeValue(DateMatch * date,int8 hour,int8 minute=X,int8 second=X,double fraction_second=X)55 DateMatch& SetTimeValue(DateMatch* date, int8 hour, int8 minute = X,
56 int8 second = X, double fraction_second = X) {
57 date->hour = hour;
58 date->minute = minute;
59 date->second = second;
60 date->fraction_second = fraction_second;
61 return *date;
62 }
63
SetTimeSpan(DateMatch * date,TimespanCode time_span_code)64 DateMatch& SetTimeSpan(DateMatch* date, TimespanCode time_span_code) {
65 date->time_span_code = time_span_code;
66 return *date;
67 }
68
SetTimeZone(DateMatch * date,TimezoneCode time_zone_code,int16 time_zone_offset=INT16_MIN)69 DateMatch& SetTimeZone(DateMatch* date, TimezoneCode time_zone_code,
70 int16 time_zone_offset = INT16_MIN) {
71 date->time_zone_code = time_zone_code;
72 date->time_zone_offset = time_zone_offset;
73 return *date;
74 }
75
SameDate(const DateMatch & a,const DateMatch & b)76 bool SameDate(const DateMatch& a, const DateMatch& b) {
77 return (a.day == b.day && a.month == b.month && a.year == b.year &&
78 a.day_of_week == b.day_of_week);
79 }
80
SetDayOfWeek(DateMatch * date,DayOfWeek dow)81 DateMatch& SetDayOfWeek(DateMatch* date, DayOfWeek dow) {
82 date->day_of_week = dow;
83 return *date;
84 }
85 };
86
TEST_F(DateMatchTest,BitFieldWidth)87 TEST_F(DateMatchTest, BitFieldWidth) {
88 // For DateMatch::day_of_week (:8).
89 EXPECT_GE(DayOfWeek_MIN, INT8_MIN);
90 EXPECT_LE(DayOfWeek_MAX, INT8_MAX);
91
92 // For DateMatch::bc_ad (:8).
93 EXPECT_GE(BCAD_MIN, INT8_MIN);
94 EXPECT_LE(BCAD_MAX, INT8_MAX);
95
96 // For DateMatch::time_span_code (:16).
97 EXPECT_GE(TimespanCode_MIN, INT16_MIN);
98 EXPECT_LE(TimespanCode_MAX, INT16_MAX);
99 }
100
TEST_F(DateMatchTest,IsValid)101 TEST_F(DateMatchTest, IsValid) {
102 // Valid: dates.
103 {
104 DateMatch d;
105 SetDate(&d, 2014, 1, 26);
106 EXPECT_TRUE(d.IsValid()) << d.DebugString();
107 }
108 {
109 DateMatch d;
110 SetDate(&d, 2014, 1, X);
111 EXPECT_TRUE(d.IsValid()) << d.DebugString();
112 }
113 {
114 DateMatch d;
115 SetDate(&d, 2014, X, X);
116 EXPECT_TRUE(d.IsValid()) << d.DebugString();
117 }
118 {
119 DateMatch d;
120 SetDate(&d, X, 1, 26);
121 EXPECT_TRUE(d.IsValid()) << d.DebugString();
122 }
123 {
124 DateMatch d;
125 SetDate(&d, X, 1, X);
126 EXPECT_TRUE(d.IsValid()) << d.DebugString();
127 }
128 {
129 DateMatch d;
130 SetDate(&d, X, X, 26);
131 EXPECT_TRUE(d.IsValid()) << d.DebugString();
132 }
133 {
134 DateMatch d;
135 SetDate(&d, 2014, 1, 26, SUN());
136 EXPECT_TRUE(d.IsValid()) << d.DebugString();
137 }
138 {
139 DateMatch d;
140 SetDate(&d, X, 1, 26, SUN());
141 EXPECT_TRUE(d.IsValid()) << d.DebugString();
142 }
143 {
144 DateMatch d;
145 SetDate(&d, X, X, 26, SUN());
146 EXPECT_TRUE(d.IsValid()) << d.DebugString();
147 }
148 {
149 DateMatch d;
150 SetDate(&d, 2014, 1, 26, DOW_X(), BC());
151 EXPECT_TRUE(d.IsValid()) << d.DebugString();
152 }
153 // Valid: times.
154 {
155 DateMatch d;
156 SetTimeValue(&d, 12, 30, 59, 0.99);
157 EXPECT_TRUE(d.IsValid()) << d.DebugString();
158 }
159 {
160 DateMatch d;
161 SetTimeValue(&d, 12, 30, 59);
162 EXPECT_TRUE(d.IsValid()) << d.DebugString();
163 }
164 {
165 DateMatch d;
166 SetTimeValue(&d, 12, 30);
167 EXPECT_TRUE(d.IsValid()) << d.DebugString();
168 }
169 {
170 DateMatch d;
171 SetTimeValue(&d, 12);
172 EXPECT_TRUE(d.IsValid()) << d.DebugString();
173 }
174 // Valid: mixed.
175 {
176 DateMatch d;
177 SetDate(&d, 2014, 1, 26);
178 SetTimeValue(&d, 12, 30, 59, 0.99);
179 EXPECT_TRUE(d.IsValid()) << d.DebugString();
180 }
181 {
182 DateMatch d;
183 SetDate(&d, X, 1, 26);
184 SetTimeValue(&d, 12, 30, 59);
185 EXPECT_TRUE(d.IsValid()) << d.DebugString();
186 }
187 {
188 DateMatch d;
189 SetDate(&d, X, X, X, SUN());
190 SetTimeValue(&d, 12, 30);
191 EXPECT_TRUE(d.IsValid()) << d.DebugString();
192 }
193 // Invalid: dates.
194 {
195 DateMatch d;
196 SetDate(&d, X, 1, 26, DOW_X(), BC());
197 EXPECT_FALSE(d.IsValid()) << d.DebugString();
198 }
199 {
200 DateMatch d;
201 SetDate(&d, 2014, X, 26);
202 EXPECT_FALSE(d.IsValid()) << d.DebugString();
203 }
204 {
205 DateMatch d;
206 SetDate(&d, 2014, X, X, SUN());
207 EXPECT_FALSE(d.IsValid()) << d.DebugString();
208 }
209 {
210 DateMatch d;
211 SetDate(&d, X, 1, X, SUN());
212 EXPECT_FALSE(d.IsValid()) << d.DebugString();
213 }
214 // Invalid: times.
215 {
216 DateMatch d;
217 SetTimeValue(&d, 12, X, 59);
218 EXPECT_FALSE(d.IsValid()) << d.DebugString();
219 }
220 {
221 DateMatch d;
222 SetTimeValue(&d, 12, X, X, 0.99);
223 EXPECT_FALSE(d.IsValid()) << d.DebugString();
224 }
225 {
226 DateMatch d;
227 SetTimeValue(&d, 12, 30, X, 0.99);
228 EXPECT_FALSE(d.IsValid()) << d.DebugString();
229 }
230 {
231 DateMatch d;
232 SetTimeValue(&d, X, 30);
233 EXPECT_FALSE(d.IsValid()) << d.DebugString();
234 }
235 // Invalid: mixed.
236 {
237 DateMatch d;
238 SetDate(&d, 2014, 1, X);
239 SetTimeValue(&d, 12);
240 EXPECT_FALSE(d.IsValid()) << d.DebugString();
241 }
242 // Invalid: empty.
243 {
244 DateMatch d;
245 EXPECT_FALSE(d.IsValid()) << d.DebugString();
246 }
247 }
248
DebugStrings(const std::vector<DateMatch> & instances)249 std::string DebugStrings(const std::vector<DateMatch>& instances) {
250 std::string res;
251 for (int i = 0; i < instances.size(); ++i) {
252 ::libtextclassifier3::strings::SStringAppendF(
253 &res, 0, "[%d] == %s\n", i, instances[i].DebugString().c_str());
254 }
255 return res;
256 }
257
TEST_F(DateMatchTest,IsRefinement)258 TEST_F(DateMatchTest, IsRefinement) {
259 {
260 DateMatch a;
261 SetDate(&a, 2014, 2, X);
262 DateMatch b;
263 SetDate(&b, 2014, X, X);
264 EXPECT_TRUE(IsRefinement(a, b)) << DebugStrings({a, b});
265 }
266 {
267 DateMatch a;
268 SetDate(&a, 2014, 2, 24);
269 DateMatch b;
270 SetDate(&b, 2014, 2, X);
271 EXPECT_TRUE(IsRefinement(a, b)) << DebugStrings({a, b});
272 }
273 {
274 DateMatch a;
275 SetDate(&a, 2014, 2, 24);
276 DateMatch b;
277 SetDate(&b, X, 2, 24);
278 EXPECT_TRUE(IsRefinement(a, b)) << DebugStrings({a, b});
279 }
280 {
281 DateMatch a;
282 SetDate(&a, 2014, 2, 24);
283 SetTimeValue(&a, 9, X, X);
284 DateMatch b;
285 SetDate(&b, 2014, 2, 24);
286 EXPECT_TRUE(IsRefinement(a, b)) << DebugStrings({a, b});
287 }
288 {
289 DateMatch a;
290 SetDate(&a, 2014, 2, 24);
291 SetTimeValue(&a, 9, 0, X);
292 DateMatch b;
293 SetDate(&b, 2014, 2, 24);
294 SetTimeValue(&b, 9, X, X);
295 EXPECT_TRUE(IsRefinement(a, b)) << DebugStrings({a, b});
296 }
297 {
298 DateMatch a;
299 SetDate(&a, 2014, 2, 24);
300 SetTimeValue(&a, 9, 0, 0);
301 DateMatch b;
302 SetDate(&b, 2014, 2, 24);
303 SetTimeValue(&b, 9, 0, X);
304 EXPECT_TRUE(IsRefinement(a, b)) << DebugStrings({a, b});
305 }
306 {
307 DateMatch a;
308 SetDate(&a, 2014, 2, 24);
309 SetTimeValue(&a, 9, X, X);
310 SetTimeSpan(&a, TimespanCode_AM);
311 DateMatch b;
312 SetDate(&b, 2014, 2, 24);
313 SetTimeValue(&b, 9, X, X);
314 EXPECT_TRUE(IsRefinement(a, b)) << DebugStrings({a, b});
315 }
316 {
317 DateMatch a;
318 SetDate(&a, 2014, 2, 24);
319 SetTimeValue(&a, 9, X, X);
320 SetTimeZone(&a, TimezoneCode_PST8PDT);
321 DateMatch b;
322 SetDate(&b, 2014, 2, 24);
323 SetTimeValue(&b, 9, X, X);
324 EXPECT_TRUE(IsRefinement(a, b)) << DebugStrings({a, b});
325 }
326 {
327 DateMatch a;
328 SetDate(&a, 2014, 2, 24);
329 SetTimeValue(&a, 9, X, X);
330 a.priority += 10;
331 DateMatch b;
332 SetDate(&b, 2014, 2, 24);
333 SetTimeValue(&b, 9, X, X);
334 EXPECT_TRUE(IsRefinement(a, b)) << DebugStrings({a, b});
335 }
336 {
337 DateMatch a;
338 SetDate(&a, 2014, 2, 24);
339 SetTimeValue(&a, 9, X, X);
340 DateMatch b;
341 SetDate(&b, 2014, 2, 24);
342 SetTimeValue(&b, 9, X, X);
343 EXPECT_TRUE(IsRefinement(a, b)) << DebugStrings({a, b});
344 }
345 {
346 DateMatch a;
347 SetDate(&a, 2014, 2, 24);
348 SetTimeValue(&a, 9, X, X);
349 DateMatch b;
350 SetDate(&b, X, 2, 24);
351 SetTimeValue(&b, 9, 0, X);
352 EXPECT_FALSE(IsRefinement(a, b)) << DebugStrings({a, b});
353 }
354 {
355 DateMatch a;
356 SetDate(&a, X, 2, 24);
357 SetTimeValue(&a, 9, X, X);
358 DateMatch b;
359 SetDate(&b, 2014, 2, 24);
360 EXPECT_FALSE(IsRefinement(a, b)) << DebugStrings({a, b});
361 }
362 {
363 DateMatch a;
364 SetTimeValue(&a, 9, 0, 0);
365 DateMatch b;
366 SetTimeValue(&b, 9, X, X);
367 SetTimeSpan(&b, TimespanCode_AM);
368 EXPECT_FALSE(IsRefinement(a, b)) << DebugStrings({a, b});
369 }
370 }
371
TEST_F(DateMatchTest,FillDateInstance_AnnotatorPriorityScore)372 TEST_F(DateMatchTest, FillDateInstance_AnnotatorPriorityScore) {
373 DateMatch date_match;
374 SetDate(&date_match, 2014, 2, X);
375 date_match.annotator_priority_score = 0.5;
376 DatetimeParseResultSpan datetime_parse_result_span;
377 FillDateInstance(date_match, &datetime_parse_result_span);
378 EXPECT_FLOAT_EQ(datetime_parse_result_span.priority_score, 0.5)
379 << DebugStrings({date_match});
380 }
381
TEST_F(DateMatchTest,MergeDateMatch_AnnotatorPriorityScore)382 TEST_F(DateMatchTest, MergeDateMatch_AnnotatorPriorityScore) {
383 DateMatch a;
384 SetDate(&a, 2014, 2, 4);
385 a.annotator_priority_score = 0.5;
386
387 DateMatch b;
388 SetTimeValue(&b, 10, 45, 23);
389 b.annotator_priority_score = 1.0;
390
391 MergeDateMatch(b, &a, false);
392 EXPECT_FLOAT_EQ(a.annotator_priority_score, 1.0);
393 }
394
395 } // namespace
396 } // namespace dates
397 } // namespace libtextclassifier3
398