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/datetime/datetime-grounder.h"
18 
19 #include "annotator/datetime/datetime_generated.h"
20 #include "utils/flatbuffers/flatbuffers.h"
21 #include "utils/jvm-test-utils.h"
22 #include "gmock/gmock.h"
23 #include "gtest/gtest.h"
24 
25 using ::libtextclassifier3::grammar::datetime::AbsoluteDateTimeT;
26 using ::libtextclassifier3::grammar::datetime::ComponentType;
27 using ::libtextclassifier3::grammar::datetime::Meridiem;
28 using ::libtextclassifier3::grammar::datetime::RelativeDatetimeComponentT;
29 using ::libtextclassifier3::grammar::datetime::RelativeDateTimeT;
30 using ::libtextclassifier3::grammar::datetime::TimeZoneT;
31 using ::libtextclassifier3::grammar::datetime::UngroundedDatetime;
32 using ::libtextclassifier3::grammar::datetime::UngroundedDatetimeT;
33 using ::libtextclassifier3::grammar::datetime::RelativeDatetimeComponent_::
34     Modifier;
35 using ::testing::SizeIs;
36 
37 namespace libtextclassifier3 {
38 
39 class DatetimeGrounderTest : public testing::Test {
40  public:
SetUp()41   void SetUp() override {
42     calendarlib_ = CreateCalendarLibForTesting();
43     datetime_grounder_.reset(new DatetimeGrounder(calendarlib_.get()));
44   }
45 
46  protected:
BuildAbsoluteDatetime(const int year,const int month,const int day,const int hour,const int minute,const int second,const Meridiem meridiem)47   OwnedFlatbuffer<UngroundedDatetime, std::string> BuildAbsoluteDatetime(
48       const int year, const int month, const int day, const int hour,
49       const int minute, const int second, const Meridiem meridiem) {
50     grammar::datetime::UngroundedDatetimeT ungrounded_datetime;
51     ungrounded_datetime.absolute_datetime.reset(new AbsoluteDateTimeT);
52 
53     // Set absolute datetime value.
54     ungrounded_datetime.absolute_datetime->year = year;
55     ungrounded_datetime.absolute_datetime->month = month;
56     ungrounded_datetime.absolute_datetime->day = day;
57     ungrounded_datetime.absolute_datetime->hour = hour;
58     ungrounded_datetime.absolute_datetime->minute = minute;
59     ungrounded_datetime.absolute_datetime->second = second;
60     ungrounded_datetime.absolute_datetime->meridiem = meridiem;
61 
62     return OwnedFlatbuffer<UngroundedDatetime, std::string>(
63         PackFlatbuffer<UngroundedDatetime>(&ungrounded_datetime));
64   }
65 
BuildRelativeDatetime(const ComponentType component_type,const Modifier modifier,const int relative_count)66   OwnedFlatbuffer<UngroundedDatetime, std::string> BuildRelativeDatetime(
67       const ComponentType component_type, const Modifier modifier,
68       const int relative_count) {
69     UngroundedDatetimeT ungrounded_datetime;
70     ungrounded_datetime.relative_datetime.reset(new RelativeDateTimeT);
71     ungrounded_datetime.relative_datetime->relative_datetime_component
72         .emplace_back(new RelativeDatetimeComponentT);
73     ungrounded_datetime.relative_datetime->relative_datetime_component.back()
74         ->modifier = modifier;
75     ungrounded_datetime.relative_datetime->relative_datetime_component.back()
76         ->component_type = component_type;
77     ungrounded_datetime.relative_datetime->relative_datetime_component.back()
78         ->value = relative_count;
79     ungrounded_datetime.relative_datetime->base.reset(new AbsoluteDateTimeT);
80     ungrounded_datetime.relative_datetime->base->year = 2020;
81     ungrounded_datetime.relative_datetime->base->month = 6;
82     ungrounded_datetime.relative_datetime->base->day = 30;
83 
84     return OwnedFlatbuffer<UngroundedDatetime, std::string>(
85         PackFlatbuffer<UngroundedDatetime>(&ungrounded_datetime));
86   }
87 
VerifyValidUngroundedDatetime(const UngroundedDatetime * ungrounded_datetime)88   void VerifyValidUngroundedDatetime(
89       const UngroundedDatetime* ungrounded_datetime) {
90     EXPECT_TRUE(
91         datetime_grounder_->IsValidUngroundedDatetime(ungrounded_datetime));
92   }
93 
VerifyInValidUngroundedDatetime(const UngroundedDatetime * ungrounded_datetime)94   void VerifyInValidUngroundedDatetime(
95       const UngroundedDatetime* ungrounded_datetime) {
96     EXPECT_FALSE(
97         datetime_grounder_->IsValidUngroundedDatetime(ungrounded_datetime));
98   }
99 
100   std::unique_ptr<DatetimeGrounder> datetime_grounder_;
101   std::unique_ptr<CalendarLib> calendarlib_;
102 };
103 
TEST_F(DatetimeGrounderTest,AbsoluteDatetimeTest)104 TEST_F(DatetimeGrounderTest, AbsoluteDatetimeTest) {
105   const OwnedFlatbuffer<UngroundedDatetime, std::string> datetime =
106       BuildAbsoluteDatetime(/*year=*/2000, /*month=*/03, /*day=*/30,
107                             /*hour=*/11, /*minute=*/59, /*second=*/59,
108                             grammar::datetime::Meridiem_AM);
109   const std::vector<DatetimeParseResult> data =
110       datetime_grounder_
111           ->Ground(
112               /*reference_time_ms_utc=*/0, "Europe/Zurich", "en-US",
113               datetime.get())
114           .ValueOrDie();
115 
116   EXPECT_THAT(data, SizeIs(1));
117   EXPECT_EQ(data[0].granularity, DatetimeGranularity::GRANULARITY_SECOND);
118 
119   // Meridiem
120   EXPECT_EQ(data[0].datetime_components[0].component_type,
121             DatetimeComponent::ComponentType::MERIDIEM);
122   EXPECT_EQ(data[0].datetime_components[0].value, 0);
123 
124   EXPECT_EQ(data[0].datetime_components[1].component_type,
125             DatetimeComponent::ComponentType::SECOND);
126   EXPECT_EQ(data[0].datetime_components[1].component_type,
127             DatetimeComponent::ComponentType::SECOND);
128 
129   EXPECT_EQ(data[0].datetime_components[2].component_type,
130             DatetimeComponent::ComponentType::MINUTE);
131   EXPECT_EQ(data[0].datetime_components[2].value, 59);
132 
133   EXPECT_EQ(data[0].datetime_components[3].component_type,
134             DatetimeComponent::ComponentType::HOUR);
135   EXPECT_EQ(data[0].datetime_components[3].value, 11);
136 
137   EXPECT_EQ(data[0].datetime_components[4].component_type,
138             DatetimeComponent::ComponentType::DAY_OF_MONTH);
139   EXPECT_EQ(data[0].datetime_components[4].value, 30);
140 
141   EXPECT_EQ(data[0].datetime_components[5].component_type,
142             DatetimeComponent::ComponentType::MONTH);
143   EXPECT_EQ(data[0].datetime_components[5].value, 3);
144 
145   EXPECT_EQ(data[0].datetime_components[6].component_type,
146             DatetimeComponent::ComponentType::YEAR);
147   EXPECT_EQ(data[0].datetime_components[6].value, 2000);
148 }
149 
TEST_F(DatetimeGrounderTest,InterpretDatetimeTest)150 TEST_F(DatetimeGrounderTest, InterpretDatetimeTest) {
151   const OwnedFlatbuffer<UngroundedDatetime, std::string> datetime =
152       BuildAbsoluteDatetime(/*year=*/2000, /*month=*/03, /*day=*/30,
153                             /*hour=*/11, /*minute=*/59, /*second=*/59,
154                             grammar::datetime::Meridiem_UNKNOWN);
155   const std::vector<DatetimeParseResult> data =
156       datetime_grounder_
157           ->Ground(
158               /*reference_time_ms_utc=*/0, "Europe/Zurich", "en-US",
159               datetime.get())
160           .ValueOrDie();
161 
162   EXPECT_THAT(data, SizeIs(2));
163   EXPECT_EQ(data[0].granularity, DatetimeGranularity::GRANULARITY_SECOND);
164   EXPECT_EQ(data[1].granularity, DatetimeGranularity::GRANULARITY_SECOND);
165 
166   // Check Meridiem's values
167   EXPECT_EQ(data[0].datetime_components[0].component_type,
168             DatetimeComponent::ComponentType::MERIDIEM);
169   EXPECT_EQ(data[0].datetime_components[0].value, 0);
170   EXPECT_EQ(data[1].datetime_components[0].component_type,
171             DatetimeComponent::ComponentType::MERIDIEM);
172   EXPECT_EQ(data[1].datetime_components[0].value, 1);
173 }
174 
TEST_F(DatetimeGrounderTest,RelativeDatetimeTest)175 TEST_F(DatetimeGrounderTest, RelativeDatetimeTest) {
176   const OwnedFlatbuffer<UngroundedDatetime, std::string> datetime =
177       BuildRelativeDatetime(ComponentType::ComponentType_DAY_OF_MONTH,
178                             Modifier::Modifier_NEXT, 1);
179   const std::vector<DatetimeParseResult> data =
180       datetime_grounder_
181           ->Ground(
182               /*reference_time_ms_utc=*/0, "Europe/Zurich", "en-US",
183               datetime.get())
184           .ValueOrDie();
185 
186   EXPECT_THAT(data, SizeIs(1));
187   EXPECT_EQ(data[0].granularity, DatetimeGranularity::GRANULARITY_DAY);
188 
189   EXPECT_EQ(data[0].datetime_components[0].component_type,
190             DatetimeComponent::ComponentType::DAY_OF_MONTH);
191   EXPECT_EQ(data[0].datetime_components[0].relative_qualifier,
192             DatetimeComponent::RelativeQualifier::NEXT);
193   EXPECT_EQ(data[0].datetime_components[0].relative_count, 1);
194   EXPECT_EQ(data[0].datetime_components[1].component_type,
195             DatetimeComponent::ComponentType::MONTH);
196   EXPECT_EQ(data[0].datetime_components[2].component_type,
197             DatetimeComponent::ComponentType::YEAR);
198 }
199 
TEST_F(DatetimeGrounderTest,TimeZoneTest)200 TEST_F(DatetimeGrounderTest, TimeZoneTest) {
201   grammar::datetime::UngroundedDatetimeT ungrounded_datetime;
202   ungrounded_datetime.absolute_datetime.reset(new AbsoluteDateTimeT);
203   ungrounded_datetime.absolute_datetime->time_zone.reset(new TimeZoneT);
204   ungrounded_datetime.absolute_datetime->time_zone->utc_offset_mins = 120;
205   const OwnedFlatbuffer<UngroundedDatetime, std::string> timezone(
206       PackFlatbuffer<UngroundedDatetime>(&ungrounded_datetime));
207 
208   const std::vector<DatetimeParseResult> data =
209       datetime_grounder_
210           ->Ground(
211               /*reference_time_ms_utc=*/0, "Europe/Zurich", "en-US",
212               timezone.get())
213           .ValueOrDie();
214 
215   EXPECT_THAT(data, SizeIs(1));
216   EXPECT_EQ(data[0].granularity, DatetimeGranularity::GRANULARITY_UNKNOWN);
217   EXPECT_EQ(data[0].datetime_components[0].component_type,
218             DatetimeComponent::ComponentType::ZONE_OFFSET);
219   EXPECT_EQ(data[0].datetime_components[0].value, 120);
220 }
221 
TEST_F(DatetimeGrounderTest,InValidUngroundedDatetime)222 TEST_F(DatetimeGrounderTest, InValidUngroundedDatetime) {
223   VerifyInValidUngroundedDatetime(
224       BuildAbsoluteDatetime(/*year=*/2000, /*month=*/23, /*day=*/30,
225                             /*hour=*/11, /*minute=*/59, /*second=*/59,
226                             grammar::datetime::Meridiem_AM)
227           .get());
228 
229   VerifyInValidUngroundedDatetime(
230       BuildAbsoluteDatetime(/*year=*/2000, /*month=*/03, /*day=*/33,
231                             /*hour=*/11, /*minute=*/59, /*second=*/59,
232                             grammar::datetime::Meridiem_AM)
233           .get());
234 
235   VerifyInValidUngroundedDatetime(
236       BuildAbsoluteDatetime(/*year=*/2000, /*month=*/02, /*day=*/30,
237                             /*hour=*/11, /*minute=*/59, /*second=*/59,
238                             grammar::datetime::Meridiem_AM)
239           .get());
240 
241   VerifyInValidUngroundedDatetime(
242       BuildAbsoluteDatetime(/*year=*/2000, /*month=*/07, /*day=*/31,
243                             /*hour=*/24, /*minute=*/59, /*second=*/59,
244                             grammar::datetime::Meridiem_AM)
245           .get());
246 
247   VerifyInValidUngroundedDatetime(
248       BuildAbsoluteDatetime(/*year=*/2000, /*month=*/02, /*day=*/28,
249                             /*hour=*/24, /*minute=*/59, /*second=*/59,
250                             grammar::datetime::Meridiem_AM)
251           .get());
252 
253   VerifyInValidUngroundedDatetime(
254       BuildAbsoluteDatetime(/*year=*/2000, /*month=*/02, /*day=*/28,
255                             /*hour=*/11, /*minute=*/69, /*second=*/59,
256                             grammar::datetime::Meridiem_AM)
257           .get());
258 
259   VerifyInValidUngroundedDatetime(
260       BuildAbsoluteDatetime(/*year=*/2000, /*month=*/02, /*day=*/28,
261                             /*hour=*/11, /*minute=*/59, /*second=*/99,
262                             grammar::datetime::Meridiem_AM)
263           .get());
264 
265   VerifyInValidUngroundedDatetime(
266       BuildAbsoluteDatetime(/*year=*/2000, /*month=*/00, /*day=*/28,
267                             /*hour=*/11, /*minute=*/59, /*second=*/99,
268                             grammar::datetime::Meridiem_AM)
269           .get());
270 }
271 
TEST_F(DatetimeGrounderTest,ValidUngroundedDatetime)272 TEST_F(DatetimeGrounderTest, ValidUngroundedDatetime) {
273   VerifyValidUngroundedDatetime(
274       BuildAbsoluteDatetime(/*year=*/2000, /*month=*/2, /*day=*/29,
275                             /*hour=*/23, /*minute=*/59, /*second=*/59,
276                             grammar::datetime::Meridiem_AM)
277           .get());
278 
279   VerifyValidUngroundedDatetime(
280       BuildAbsoluteDatetime(/*year=*/2000, /*month=*/7, /*day=*/31,
281                             /*hour=*/23, /*minute=*/59, /*second=*/59,
282                             grammar::datetime::Meridiem_AM)
283           .get());
284 
285   VerifyValidUngroundedDatetime(
286       BuildAbsoluteDatetime(/*year=*/2000, /*month=*/10, /*day=*/31,
287                             /*hour=*/23, /*minute=*/59, /*second=*/59,
288                             grammar::datetime::Meridiem_AM)
289           .get());
290 }
291 
292 }  // namespace libtextclassifier3
293