Skip to content
This repository was archived by the owner on Dec 4, 2023. It is now read-only.

Commit 80849cf

Browse files
fran893Batta32
andauthored
[SDK][Recognizers-Text] Add Recognizer-Text unit tests into the temporal Recognizer-Text folder in bot-dialogs library (#1168)
* Add timex-expression unit tests * Add Specs folder * Add datetime unit tests * Add sequence unit tests * Add number with unit unit tests * Add number unit tests * Add choice unit tests * Add text unit tests * Update bot-dialogs pom to use UTF-8 to execute unit tests correctly Co-authored-by: Martin Battaglino <martinbatta32@gmail.com>
1 parent 0b972fb commit 80849cf

File tree

488 files changed

+646013
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

488 files changed

+646013
-0
lines changed

libraries/bot-dialogs/pom.xml

+11
Original file line numberDiff line numberDiff line change
@@ -158,4 +158,15 @@
158158
</plugins>
159159
</reporting>
160160

161+
<build>
162+
<plugins>
163+
<plugin>
164+
<groupId>org.apache.maven.plugins</groupId>
165+
<artifactId>maven-surefire-plugin</artifactId>
166+
<configuration>
167+
<argLine>-Dfile.encoding=UTF-8</argLine>
168+
</configuration>
169+
</plugin>
170+
</plugins>
171+
</build>
161172
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
package com.microsoft.recognizers.text.tests;
2+
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.fasterxml.jackson.databind.MapperFeature;
5+
import com.fasterxml.jackson.databind.ObjectMapper;
6+
import com.microsoft.recognizers.text.*;
7+
import com.microsoft.recognizers.text.datetime.parsers.DateTimeParseResult;
8+
import com.microsoft.recognizers.text.tests.helpers.DateTimeParseResultMixIn;
9+
import com.microsoft.recognizers.text.tests.helpers.ExtendedModelResultMixIn;
10+
import com.microsoft.recognizers.text.tests.helpers.ExtractResultMixIn;
11+
import com.microsoft.recognizers.text.tests.helpers.ModelResultMixIn;
12+
import org.apache.commons.io.FileUtils;
13+
import org.javatuples.Pair;
14+
import org.junit.*;
15+
import org.junit.runner.RunWith;
16+
import org.junit.runners.Parameterized;
17+
18+
import java.io.File;
19+
import java.io.IOException;
20+
import java.nio.file.Files;
21+
import java.util.*;
22+
import java.util.regex.Pattern;
23+
import java.util.stream.Collectors;
24+
import java.util.stream.IntStream;
25+
26+
@RunWith(Parameterized.class)
27+
public abstract class AbstractTest {
28+
29+
private static final String SpecsPath = "Specs/..";
30+
31+
private static final List<String> SupportedCultures = Arrays.asList("English", "Spanish", "Portuguese", "French", "German", "Chinese");
32+
33+
// FEFF - UTF-8 byte order mark (EF BB BF) as Unicode char representation.
34+
private static final String UTF8_BOM = "\uFEFF";
35+
36+
protected final TestCase currentCase;
37+
38+
public AbstractTest(TestCase currentCase) {
39+
this.currentCase = currentCase;
40+
}
41+
42+
private static Map<String, Integer> testCounter;
43+
private static Map<String, Integer> passCounter;
44+
private static Map<String, Integer> failCounter;
45+
private static Map<String, Integer> skipCounter;
46+
47+
@BeforeClass
48+
public static void before() {
49+
testCounter = new LinkedHashMap<>();
50+
passCounter = new LinkedHashMap<>();
51+
failCounter = new LinkedHashMap<>();
52+
skipCounter = new LinkedHashMap<>();
53+
}
54+
55+
@AfterClass
56+
public static void after() {
57+
58+
Map<String, String> counter = new LinkedHashMap<>();
59+
60+
for (Map.Entry<String, Integer> entry : testCounter.entrySet()) {
61+
int skipped = skipCounter.getOrDefault(entry.getKey(), 0);
62+
if (entry.getValue() > skipped) {
63+
counter.put(entry.getKey(), String.format("%7d", entry.getValue()));
64+
}
65+
}
66+
67+
for (Map.Entry<String, String> entry : counter.entrySet()) {
68+
Integer passValue = passCounter.getOrDefault(entry.getKey(), 0);
69+
Integer failValue = failCounter.getOrDefault(entry.getKey(), 0);
70+
Integer skipValue = skipCounter.getOrDefault(entry.getKey(), 0);
71+
counter.put(entry.getKey(), String.format("|%s |%7d |%7d |%7d ", entry.getValue(), passValue, skipValue, failValue));
72+
}
73+
74+
print(counter);
75+
}
76+
77+
private static void print(Map<String, String> map) {
78+
System.out.println("| TOTAL | Passed | Skipped | Failed || Key");
79+
for (Map.Entry<String, String> entry : map.entrySet()) {
80+
System.out.println(entry.getValue() + "|| " + entry.getKey());
81+
}
82+
}
83+
84+
private void count(TestCase testCase) {
85+
String key = testCase.recognizerName + "-" + testCase.language + "-" + testCase.modelName;
86+
Integer current = testCounter.getOrDefault(key, 0);
87+
testCounter.put(key, current + 1);
88+
}
89+
90+
private void countPass(TestCase testCase) {
91+
String key = testCase.recognizerName + "-" + testCase.language + "-" + testCase.modelName;
92+
Integer current = passCounter.getOrDefault(key, 0);
93+
passCounter.put(key, current + 1);
94+
}
95+
96+
private void countSkip(TestCase testCase) {
97+
String key = testCase.recognizerName + "-" + testCase.language + "-" + testCase.modelName;
98+
Integer current = skipCounter.getOrDefault(key, 0);
99+
skipCounter.put(key, current + 1);
100+
}
101+
102+
private void countFail(TestCase testCase) {
103+
String key = testCase.recognizerName + "-" + testCase.language + "-" + testCase.modelName;
104+
Integer current = failCounter.getOrDefault(key, 0);
105+
failCounter.put(key, current + 1);
106+
}
107+
108+
@Test
109+
public void test() {
110+
111+
count(currentCase);
112+
113+
if (!isJavaSupported(this.currentCase.notSupported)) {
114+
countSkip(currentCase);
115+
throw new AssumptionViolatedException("Test case wih input '" + this.currentCase.input + "' not supported.");
116+
}
117+
118+
if (this.currentCase.debug) {
119+
// Add breakpoint here to stop on those TestCases marked with "Debug": true
120+
System.out.println("Debug Break!");
121+
}
122+
123+
try {
124+
recognizeAndAssert(currentCase);
125+
countPass(this.currentCase);
126+
} catch (AssumptionViolatedException ex) {
127+
countSkip(currentCase);
128+
throw ex;
129+
} catch (Throwable err) {
130+
countFail(currentCase);
131+
throw err;
132+
}
133+
}
134+
135+
// TODO Override in specific models
136+
protected abstract List<ModelResult> recognize(TestCase currentCase);
137+
138+
protected void recognizeAndAssert(TestCase currentCase) {
139+
List<ModelResult> results = recognize(currentCase);
140+
assertResults(currentCase, results);
141+
}
142+
143+
public static void assertResults(TestCase currentCase, List<ModelResult> results) {
144+
assertResultsWithKeys(currentCase, results, Collections.emptyList());
145+
}
146+
147+
public static void assertResultsWithKeys(TestCase currentCase, List<ModelResult> results, List<String> testResolutionKeys) {
148+
149+
List<ModelResult> expectedResults = readExpectedResults(ModelResult.class, currentCase.results);
150+
Assert.assertEquals(getMessage(currentCase, "\"Result Count\""), expectedResults.size(), results.size());
151+
152+
IntStream.range(0, expectedResults.size())
153+
.mapToObj(i -> Pair.with(expectedResults.get(i), results.get(i)))
154+
.forEach(t -> {
155+
ModelResult expected = t.getValue0();
156+
ModelResult actual = t.getValue1();
157+
158+
Assert.assertEquals(getMessage(currentCase, "typeName"), expected.typeName, actual.typeName);
159+
Assert.assertEquals(getMessage(currentCase, "text"), expected.text, actual.text);
160+
161+
if (expected.resolution.containsKey(ResolutionKey.Value)) {
162+
Assert.assertEquals(getMessage(currentCase, "resolution.value"),
163+
expected.resolution.get(ResolutionKey.Value), actual.resolution.get(ResolutionKey.Value));
164+
}
165+
166+
for (String key : testResolutionKeys) {
167+
Assert.assertEquals(getMessage(currentCase, key), expected.resolution.get(key), actual.resolution.get(key));
168+
}
169+
});
170+
}
171+
172+
public static Collection<TestCase> enumerateTestCases(String recognizerType, String modelName) {
173+
174+
String recognizerTypePath = String.format(File.separator + recognizerType + File.separator);
175+
176+
// Deserializer
177+
ObjectMapper mapper = new ObjectMapper();
178+
mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
179+
180+
// Map json to TestCases
181+
return FileUtils.listFiles(new File(SpecsPath), new String[]{"json"}, true)
182+
.stream().filter(f -> f.getPath().contains(recognizerTypePath))
183+
.map(f -> parseSpecFile(f, mapper))
184+
.flatMap(ts -> Arrays.stream(ts))
185+
// Ignore tests with NotSupportedByDesign = Java
186+
.filter(ts -> isJavaSupported(ts.notSupportedByDesign))
187+
// Filter supported languages only
188+
.filter(ts -> SupportedCultures.contains(ts.language))
189+
.filter(ts -> ts.modelName.contains(modelName))
190+
.collect(Collectors.toCollection(ArrayList::new));
191+
}
192+
193+
public static TestCase[] parseSpecFile(File f, ObjectMapper mapper) {
194+
195+
List<String> paths = Arrays.asList(f.toPath().toString().split(Pattern.quote(File.separator)));
196+
List<String> testInfo = paths.subList(paths.size() - 3, paths.size());
197+
198+
try {
199+
200+
// Workaround to consume a possible UTF-8 BOM byte
201+
// https://stackoverflow.com/questions/4897876/reading-utf-8-bom-marker
202+
String contents = new String(Files.readAllBytes(f.toPath()));
203+
String json = StringUtf8Bom(contents);
204+
205+
TestCase[] tests = mapper.readValue(json, TestCase[].class);
206+
Arrays.stream(tests).forEach(t -> {
207+
t.recognizerName = testInfo.get(0);
208+
t.language = testInfo.get(1);
209+
t.modelName = testInfo.get(2).split(Pattern.quote("."))[0];
210+
});
211+
212+
return tests;
213+
214+
} catch (IOException ex) {
215+
216+
System.out.println("Error reading Spec file: " + f.toString() + " | " + ex.getMessage());
217+
218+
// @TODO: This should cause a test run failure.
219+
return new TestCase[0];
220+
}
221+
}
222+
223+
public static <T extends ExtractResult> T parseExtractResult(Class<T> extractorResultClass, Object result) {
224+
// Deserializer
225+
ObjectMapper mapper = new ObjectMapper();
226+
mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
227+
mapper.addMixIn(ExtractResult.class, ExtractResultMixIn.class);
228+
229+
try {
230+
String json = mapper.writeValueAsString(result);
231+
return mapper.readValue(json, extractorResultClass);
232+
233+
} catch (JsonProcessingException e) {
234+
e.printStackTrace();
235+
return null;
236+
237+
} catch (IOException e) {
238+
e.printStackTrace();
239+
return null;
240+
}
241+
}
242+
243+
public static <T extends DateTimeParseResult> T parseDateTimeParseResult(Class<T> dateTimeParseResultClass, Object result) {
244+
// Deserializer
245+
ObjectMapper mapper = new ObjectMapper();
246+
mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
247+
mapper.addMixIn(DateTimeParseResult.class, DateTimeParseResultMixIn.class);
248+
249+
try {
250+
String json = mapper.writeValueAsString(result);
251+
return mapper.readValue(json, dateTimeParseResultClass);
252+
253+
} catch (JsonProcessingException e) {
254+
e.printStackTrace();
255+
return null;
256+
257+
} catch (IOException e) {
258+
e.printStackTrace();
259+
return null;
260+
}
261+
}
262+
263+
public static <T extends ModelResult> T parseResult(Class<T> modelResultClass, Object result) {
264+
// Deserializer
265+
ObjectMapper mapper = new ObjectMapper();
266+
mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
267+
mapper.addMixIn(ModelResult.class, ModelResultMixIn.class);
268+
mapper.addMixIn(ExtendedModelResult.class, ExtendedModelResultMixIn.class);
269+
270+
try {
271+
String json = mapper.writeValueAsString(result);
272+
return mapper.readValue(json, modelResultClass);
273+
274+
} catch (JsonProcessingException e) {
275+
e.printStackTrace();
276+
return null;
277+
278+
} catch (IOException e) {
279+
e.printStackTrace();
280+
return null;
281+
}
282+
}
283+
284+
public static <T extends ModelResult> List<T> readExpectedResults(Class<T> modelResultClass, List<Object> results) {
285+
return results.stream().map(r -> parseResult(modelResultClass, r))
286+
.collect(Collectors.toCollection(ArrayList::new));
287+
}
288+
289+
public static <T extends ExtractResult> List<T> readExpectedExtractResults(Class<T> extractorResultClass, List<Object> results) {
290+
return results.stream().map(r -> parseExtractResult(extractorResultClass, r))
291+
.collect(Collectors.toCollection(ArrayList::new));
292+
}
293+
294+
public static <T extends DateTimeParseResult> List<T> readExpectedDateTimeParseResults(Class<T> dateTimeParseResultClass, List<Object> results) {
295+
return results.stream().map(r -> parseDateTimeParseResult(dateTimeParseResultClass, r))
296+
.collect(Collectors.toCollection(ArrayList::new));
297+
}
298+
299+
public static String getCultureCode(String language) {
300+
return Arrays.stream(Culture.SupportedCultures)
301+
.filter(c -> c.cultureName.equalsIgnoreCase(language))
302+
.findFirst().get().cultureCode;
303+
}
304+
305+
public static boolean isJavaSupported(String notSupported) {
306+
// definition for "not supported" missing, should be supported then
307+
if (notSupported == null) return true;
308+
309+
return !Arrays.asList(notSupported.toLowerCase().trim().split("\\s*,\\s*")).contains("java");
310+
}
311+
312+
public static String getMessage(TestCase testCase, String propName) {
313+
return "Does not match " + propName + " on Input: \"" + testCase.input + "\"";
314+
}
315+
316+
private static String StringUtf8Bom(String input) {
317+
318+
if (input.startsWith(UTF8_BOM)) {
319+
input = input.substring(1);
320+
}
321+
322+
return input;
323+
}
324+
325+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.microsoft.recognizers.text.tests;
2+
3+
public class DependencyConstants {
4+
5+
public static final String BASE_RECOGNIZERS_MODEL_UNAVAILABLE = "could not find model with the specified configuration";
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.microsoft.recognizers.text.tests;
2+
3+
public enum Models {
4+
Number,
5+
NumberPercentMode,
6+
Ordinal,
7+
Percent,
8+
PercentPercentMode,
9+
NumberRange,
10+
CustomNumber,
11+
Age,
12+
Currency,
13+
Dimension,
14+
Temperature,
15+
DateTime,
16+
DateTimeSplitDateAndTime,
17+
DateTimeCalendarMode,
18+
DateTimeExtendedTypes,
19+
DateTimeComplexCalendar,
20+
PhoneNumber,
21+
IpAddress,
22+
Mention,
23+
Hashtag,
24+
Email,
25+
URL,
26+
Boolean,
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.microsoft.recognizers.text.tests;
2+
3+
// This Exception represents a extractor/parser/model not yet implemented in Java
4+
public class NotSupportedException extends Exception {
5+
6+
public NotSupportedException(String message) {
7+
super(message);
8+
}
9+
10+
public NotSupportedException(String message, Exception ex) {
11+
super(message, ex);
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.microsoft.recognizers.text.tests;
2+
3+
public enum Platform {
4+
dotNet,
5+
javascript,
6+
python,
7+
java
8+
}

0 commit comments

Comments
 (0)