View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.codec.language;
19  
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertFalse;
22  import static org.junit.jupiter.api.Assertions.assertTrue;
23  
24  import java.util.stream.Stream;
25  
26  import org.apache.commons.codec.AbstractStringEncoderTest;
27  import org.junit.jupiter.api.Named;
28  import org.junit.jupiter.api.Test;
29  import org.junit.jupiter.params.ParameterizedTest;
30  import org.junit.jupiter.params.provider.Arguments;
31  import org.junit.jupiter.params.provider.CsvSource;
32  import org.junit.jupiter.params.provider.MethodSource;
33  
34  /**
35   * Series of tests for the Match Rating Approach algorithm.
36   *
37   * General naming nomenclature for the test is of the form:
38   * GeneralMetadataOnTheTestArea_ActualTestValues_ExpectedResult
39   *
40   * An unusual value is indicated by the term "corner case"
41   */
42  class MatchRatingApproachEncoderTest extends AbstractStringEncoderTest<MatchRatingApproachEncoder> {
43  
44      static Stream<Arguments> accentRemovalCases() {
45          // @formatter:off
46          return Stream.of(
47                  Arguments.of(Named.of("lowercase accents", "áéíóú"), "aeiou"),
48                  Arguments.of(Named.of("comprehensive accent mix", "È,É,Ê,Ë,Û,Ù,Ï,Î,À,Â,Ô,è,é,ê,ë,û,ù,ï,î,à,â,ô,ç"),
49                          "E,E,E,E,U,U,I,I,A,A,O,e,e,e,e,u,u,i,i,a,a,o,c"),
50                  Arguments.of(Named.of("German, Spanish, and French characters", "äëöüßÄËÖÜñÑà"), "aeoußAEOUnNa"),
51                  Arguments.of(Named.of("preserves punctuation", "Á-e'í.,ó&ú"), "A-e'i.,o&u"),
52                  Arguments.of(Named.of("empty input", ""), ""),
53                  Arguments.of(Named.of("null input", null), null),
54                  Arguments.of(Named.of("preserves case", "ÁeíÓuu"), "AeiOuu"),
55                  Arguments.of(Named.of("preserves spaces", "áé íó  ú"), "ae io  u"),
56                  Arguments.of(Named.of("unaccented input", "Colorless green ideas sleep furiously"), "Colorless green ideas sleep furiously"));
57          // @formatter:on
58      }
59  
60      static Stream<Arguments> encodingCases() {
61          // @formatter:off
62          return Stream.of(
63                  Arguments.of(Named.of("HARPER", "HARPER"), "HRPR"),
64                  Arguments.of(Named.of("empty input", ""), ""),
65                  Arguments.of(Named.of("null input", null), ""),
66                  Arguments.of(Named.of("single letter", "E"), ""),
67                  Arguments.of(Named.of("Smith", "Smith"), "SMTH"),
68                  Arguments.of(Named.of("Smyth", "Smyth"), "SMYTH"),
69                  Arguments.of(Named.of("single space", " "), ""));
70          // @formatter:on
71      }
72  
73      static Stream<Arguments> vowelRemovalCases() {
74          // @formatter:off
75          return Stream.of(
76                  Arguments.of(Named.of("AIDAN", "AIDAN"), "ADN"),
77                  Arguments.of(Named.of("DECLAN", "DECLAN"), "DCLN"),
78                  Arguments.of(Named.of("ALESSANDRA", "ALESSANDRA"), "ALSSNDR"));
79          // @formatter:on
80      }
81  
82      @Override
83      protected MatchRatingApproachEncoder createStringEncoder() {
84          return new MatchRatingApproachEncoder();
85      }
86  
87      @Test
88      final void testCleanNameSuccessfullyClean() {
89          assertEquals("THISISATEST", getStringEncoder().cleanName("This-ís   a t.,es &t"));
90      }
91  
92      @Test
93      final void testCompare_BRIAN_BRYAN_SuccessfullyMatched() {
94          assertTrue(getStringEncoder().isEncodeEquals("Brian", "Bryan"));
95      }
96  
97      @Test
98      final void testCompare_BURNS_BOURNE_SuccessfullyMatched() {
99          assertTrue(getStringEncoder().isEncodeEquals("Burns", "Bourne"));
100     }
101 
102     @Test
103     final void testCompare_CATHERINE_KATHRYN_SuccessfullyMatched() {
104         assertTrue(getStringEncoder().isEncodeEquals("Catherine", "Kathryn"));
105     }
106 
107     @Test
108     final void testCompare_COLM_COLIN_WithAccentsAndSymbolsAndSpaces_SuccessfullyMatched() {
109         assertTrue(getStringEncoder().isEncodeEquals("Cólm.   ", "C-olín"));
110     }
111 
112     @Test
113     final void testCompare_Forenames_SEAN_JOHN_MatchExpected() {
114         assertTrue(getStringEncoder().isEncodeEquals("Sean", "John"));
115     }
116 
117     @Test
118     final void testCompare_Forenames_SEAN_PETE_NoMatchExpected() {
119         assertFalse(getStringEncoder().isEncodeEquals("Sean", "Pete"));
120     }
121 
122     @Test
123     final void testCompare_Forenames_UNA_OONAGH_ShouldSuccessfullyMatchButDoesNot() {
124         assertFalse(getStringEncoder().isEncodeEquals("Úna", "Oonagh")); // Disappointing
125     }
126 
127     @Test
128     final void testCompare_FRANCISZEK_FRANCES_SuccessfullyMatched() {
129         assertTrue(getStringEncoder().isEncodeEquals("Franciszek", "Frances"));
130     }
131 
132     @Test
133     final void testCompare_KARL_ALESSANDRO_DoesNotMatch() {
134         assertFalse(getStringEncoder().isEncodeEquals("Karl", "Alessandro"));
135     }
136 
137     @Test
138     final void testCompare_LongSurnames_MORIARTY_OMUIRCHEARTAIGH_DoesNotSuccessfulMatch() {
139         assertFalse(getStringEncoder().isEncodeEquals("Moriarty", "OMuircheartaigh"));
140     }
141 
142     @Test
143     final void testCompare_LongSurnames_OMUIRCHEARTAIGH_OMIREADHAIGH_SuccessfulMatch() {
144         assertTrue(getStringEncoder().isEncodeEquals("o'muireadhaigh", "Ó 'Muircheartaigh "));
145     }
146 
147     @Test
148     final void testCompare_MCGOWAN_MCGEOGHEGAN_SuccessfullyMatched() {
149         assertTrue(getStringEncoder().isEncodeEquals("McGowan", "Mc Geoghegan"));
150     }
151 
152     @Test
153     final void testCompare_MICKY_MICHAEL_SuccessfullyMatched() {
154         assertTrue(getStringEncoder().isEncodeEquals("Micky", "Michael"));
155     }
156 
157     @Test
158     final void testCompare_OONA_OONAGH_SuccessfullyMatched() {
159         assertTrue(getStringEncoder().isEncodeEquals("Oona", "Oonagh"));
160     }
161 
162     @Test
163     final void testCompare_PETERSON_PETERS_SuccessfullyMatched() {
164         assertTrue(getStringEncoder().isEncodeEquals("Peterson", "Peters"));
165     }
166 
167     @Test
168     final void testCompare_SAM_SAMUEL_SuccessfullyMatched() {
169         assertTrue(getStringEncoder().isEncodeEquals("Sam", "Samuel"));
170     }
171 
172     @Test
173     final void testCompare_SEAN_SHAUN_SuccessfullyMatched() {
174         assertTrue(getStringEncoder().isEncodeEquals("Séan", "Shaun"));
175     }
176 
177     @Test
178     final void testCompare_ShortNames_AL_ED_WorksButNoMatch() {
179         assertFalse(getStringEncoder().isEncodeEquals("Al", "Ed"));
180     }
181 
182     @Test
183     final void testCompare_SmallInput_CARK_Kl_SuccessfullyMatched() {
184         assertTrue(getStringEncoder().isEncodeEquals("Kl", "Karl"));
185     }
186 
187     @Test
188     final void testCompare_SMITH_SMYTH_SuccessfullyMatched() {
189         assertTrue(getStringEncoder().isEncodeEquals("smith", "smyth"));
190     }
191 
192     @Test
193     final void testCompare_SOPHIE_SOFIA_SuccessfullyMatched() {
194         assertTrue(getStringEncoder().isEncodeEquals("Sophie", "Sofia"));
195     }
196 
197     @Test
198     final void testCompare_STEPHEN_STEFAN_SuccessfullyMatched() {
199         assertTrue(getStringEncoder().isEncodeEquals("Stephen", "Stefan"));
200     }
201 
202     @Test
203     final void testCompare_STEPHEN_STEVEN_SuccessfullyMatched() {
204         assertTrue(getStringEncoder().isEncodeEquals("Stephen", "Steven"));
205     }
206 
207     @Test
208     final void testCompare_STEVEN_STEFAN_SuccessfullyMatched() {
209         assertTrue(getStringEncoder().isEncodeEquals("Steven", "Stefan"));
210     }
211 
212     @Test
213     final void testCompare_Surname_AUERBACH_UHRBACH_SuccessfullyMatched() {
214         assertTrue(getStringEncoder().isEncodeEquals("Auerbach", "Uhrbach"));
215     }
216 
217     @Test
218     final void testCompare_Surname_COOPERFLYNN_SUPERLYN_SuccessfullyMatched() {
219         assertTrue(getStringEncoder().isEncodeEquals("Cooper-Flynn", "Super-Lyn"));
220     }
221 
222     @Test
223     final void testCompare_Surname_HAILEY_HALLEY_SuccessfullyMatched() {
224         assertTrue(getStringEncoder().isEncodeEquals("Hailey", "Halley"));
225     }
226 
227     @Test
228     final void testCompare_Surname_LEWINSKY_LEVINSKI_SuccessfullyMatched() {
229         assertTrue(getStringEncoder().isEncodeEquals("LEWINSKY", "LEVINSKI"));
230     }
231 
232     @Test
233     final void testCompare_Surname_LIPSHITZ_LIPPSZYC_SuccessfullyMatched() {
234         assertTrue(getStringEncoder().isEncodeEquals("LIPSHITZ", "LIPPSZYC"));
235     }
236 
237     @Test
238     final void testCompare_Surname_MOSKOWITZ_MOSKOVITZ_SuccessfullyMatched() {
239         assertTrue(getStringEncoder().isEncodeEquals("Moskowitz", "Moskovitz"));
240     }
241 
242     @Test
243     final void testCompare_Surname_OSULLIVAN_OSUILLEABHAIN_SuccessfulMatch() {
244         assertTrue(getStringEncoder().isEncodeEquals("O'Sullivan", "Ó ' Súilleabháin"));
245     }
246 
247     @Test
248     final void testCompare_Surname_PRZEMYSL_PSHEMESHIL_SuccessfullyMatched() {
249         assertTrue(getStringEncoder().isEncodeEquals(" P rz e m y s l", " P sh e m e sh i l"));
250     }
251 
252     @Test
253     final void testCompare_Surname_ROSOCHOWACIEC_ROSOKHOVATSETS_SuccessfullyMatched() {
254         assertTrue(getStringEncoder().isEncodeEquals("R o s o ch o w a c ie c", " R o s o k ho v a ts e ts"));
255     }
256 
257     @Test
258     final void testCompare_Surname_SZLAMAWICZ_SHLAMOVITZ_SuccessfullyMatched() {
259         assertTrue(getStringEncoder().isEncodeEquals("SZLAMAWICZ", "SHLAMOVITZ"));
260     }
261 
262     @Test
263     final void testCompare_SurnameCornerCase_Nulls_NoMatch() {
264         assertFalse(getStringEncoder().isEncodeEquals(null, null));
265     }
266 
267     @Test
268     final void testCompare_Surnames_MURPHY_LYNCH_NoMatchExpected() {
269         assertFalse(getStringEncoder().isEncodeEquals("Murphy", "Lynch"));
270     }
271 
272     @Test
273     final void testCompare_SurnamesCornerCase_MURPHY_NoSpace_NoMatch() {
274         assertFalse(getStringEncoder().isEncodeEquals("Murphy", ""));
275     }
276 
277     @Test
278     final void testCompare_SurnamesCornerCase_MURPHY_Space_NoMatch() {
279         assertFalse(getStringEncoder().isEncodeEquals("Murphy", " "));
280     }
281 
282     @Test
283     final void testCompare_TOMASZ_TOM_SuccessfullyMatched() {
284         assertTrue(getStringEncoder().isEncodeEquals("Tomasz", "tom"));
285     }
286 
287     @Test
288     final void testCompare_ZACH_ZAKARIA_SuccessfullyMatched() {
289         assertTrue(getStringEncoder().isEncodeEquals("Zach", "Zacharia"));
290     }
291 
292     @Test
293     final void testCompareNameNullSpace_ReturnsFalseSuccessfully() {
294         assertFalse(getStringEncoder().isEncodeEquals(null, " "));
295     }
296 
297     @Test
298     final void testCompareNameSameNames_ReturnsFalseSuccessfully() {
299         assertTrue(getStringEncoder().isEncodeEquals("John", "John"));
300     }
301 
302     @Test
303     final void testCompareNameToSingleLetter_KARL_C_DoesNotMatch() {
304         assertFalse(getStringEncoder().isEncodeEquals("Karl", "C"));
305     }
306 
307     @Test
308     final void testCompareWithWhitespace() {
309         // sanity check
310         assertTrue(getStringEncoder().isEncodeEquals("Brian", "Bryan"));
311         // whitespace
312         assertTrue(getStringEncoder().isEncodeEquals(" Brian", "Bryan"));
313         assertTrue(getStringEncoder().isEncodeEquals("Brian ", "Bryan"));
314         assertTrue(getStringEncoder().isEncodeEquals(" Brian ", "Bryan"));
315         assertTrue(getStringEncoder().isEncodeEquals("Brian", " Bryan"));
316         assertTrue(getStringEncoder().isEncodeEquals("Brian", "Bryan "));
317         assertTrue(getStringEncoder().isEncodeEquals("Brian", " Bryan "));
318     }
319 
320     @ParameterizedTest(name = "{0}")
321     @MethodSource("encodingCases")
322     final void testEncode(final String input, final String expected) {
323         assertEquals(expected, getStringEncoder().encode(input));
324     }
325 
326     @Test
327     final void testGetFirstLast3__ALEXANDER_Returns_Aleder() {
328         assertEquals("Aleder", getStringEncoder().getFirst3Last3("Alexzander"));
329     }
330 
331     @Test
332     final void testGetFirstLast3_PETE_Returns_PETE() {
333         assertEquals("PETE", getStringEncoder().getFirst3Last3("PETE"));
334     }
335 
336     @ParameterizedTest(name = "length sum {0}: minimum rating {1}")
337     @CsvSource({ "1, 5", "2, 5", "5, 4", "6, 4", "7, 4", "8, 3", "10, 3", "11, 3", "13, 1" })
338     final void testGetMinRating(final int lengthSum, final int expected) {
339         assertEquals(expected, getStringEncoder().getMinRating(lengthSum));
340     }
341 
342     // ***** Begin Region - Test Get Encoding - Surnames
343 
344     @Test
345     final void testIsEncodeEquals_CornerCase_FirstNameJust1Letter_ReturnsFalse() {
346         assertFalse(getStringEncoder().isEncodeEquals("t", "test"));
347     }
348 
349     @Test
350     final void testIsEncodeEquals_CornerCase_FirstNameJustSpace_ReturnsFalse() {
351         assertFalse(getStringEncoder().isEncodeEquals(" ", "test"));
352     }
353 
354     @Test
355     final void testIsEncodeEquals_CornerCase_FirstNameNothing_ReturnsFalse() {
356         assertFalse(getStringEncoder().isEncodeEquals("", "test"));
357     }
358 
359     @Test
360     final void testIsEncodeEquals_CornerCase_FirstNameNull_ReturnsFalse() {
361         assertFalse(getStringEncoder().isEncodeEquals(null, "test"));
362     }
363 
364     @Test
365     final void testIsEncodeEquals_CornerCase_SecondNameJustSpace_ReturnsFalse() {
366         assertFalse(getStringEncoder().isEncodeEquals("test", " "));
367     }
368 
369     @Test
370     final void testIsEncodeEquals_CornerCase_SecondNameNothing_ReturnsFalse() {
371         assertFalse(getStringEncoder().isEncodeEquals("test", ""));
372     }
373 
374     @Test
375     final void testIsEncodeEquals_CornerCase_SecondNameNull_ReturnsFalse() {
376         assertFalse(getStringEncoder().isEncodeEquals("test", null));
377     }
378 
379     @Test
380     final void testIsEncodeEqualsSecondNameJust1Letter_ReturnsFalse() {
381         assertFalse(getStringEncoder().isEncodeEquals("test", "t"));
382     }
383 
384     @Test
385     final void testIsVowel_CapitalA_ReturnsTrue() {
386         assertTrue(getStringEncoder().isVowel("A"));
387     }
388 
389     @Test
390     final void testIsVowel_SingleVowel_ReturnsTrue() {
391         assertTrue(getStringEncoder().isVowel("I"));
392     }
393 
394     @Test
395     final void testIsVowel_SmallD_ReturnsFalse() {
396         assertFalse(getStringEncoder().isVowel("d"));
397     }
398 
399     @Test
400     final void testLeftToRightThenRightToLeft_ALEXANDER_ALEXANDRA_Returns4() {
401         assertEquals(4, getStringEncoder().leftToRightThenRightToLeftProcessing("ALEXANDER", "ALEXANDRA"));
402     }
403 
404     @Test
405     final void testLeftToRightThenRightToLeft_EINSTEIN_MICHAELA_Returns0() {
406         assertEquals(0, getStringEncoder().leftToRightThenRightToLeftProcessing("EINSTEIN", "MICHAELA"));
407     }
408 
409     @Test
410     final void testPunctuationOnly() {
411         assertEquals(getStringEncoder().encode(".,-"), "");
412     }
413 
414     @ParameterizedTest(name = "{0}")
415     @MethodSource("accentRemovalCases")
416     final void testRemoveAccents(final String input, final String expected) {
417         assertEquals(expected, getStringEncoder().removeAccents(input));
418     }
419 
420     @Test
421     final void testRemoveDoubleConsonants_MISSISSIPPI_RemovedSuccessfully() {
422         assertEquals("MISISIPI", getStringEncoder().removeDoubleConsonants("MISSISSIPPI"));
423     }
424 
425     @Test
426     final void testRemoveDoubleDoubleVowel_BEETLE_NotRemoved() {
427         assertEquals("BEETLE", getStringEncoder().removeDoubleConsonants("BEETLE"));
428     }
429 
430     @Test
431     final void testRemoveSingleDoubleConsonants_BUBLE_RemovedSuccessfully() {
432         assertEquals("BUBLE", getStringEncoder().removeDoubleConsonants("BUBBLE"));
433     }
434 
435     @ParameterizedTest(name = "{0}")
436     @MethodSource("vowelRemovalCases")
437     final void testRemoveVowels(final String input, final String expected) {
438         assertEquals(expected, getStringEncoder().removeVowels(input));
439     }
440 
441     // ***** END REGION - TEST GET MRA COMPARISONS
442 
443     @Test
444     final void testVowelAndPunctuationOnly() {
445         assertEquals(getStringEncoder().encode("uoiea.,-AEIOU"), "U");
446     }
447 
448     @Test
449     final void testVowelOnly() {
450         assertEquals(getStringEncoder().encode("aeiouAEIOU"), "A");
451     }
452 }