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.bm;
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.assertNotEquals;
23  import static org.junit.jupiter.api.Assertions.assertThrows;
24  import static org.junit.jupiter.api.Assertions.assertTimeout;
25  import static org.junit.jupiter.api.Assertions.assertTrue;
26  
27  import java.time.Duration;
28  import java.util.Random;
29  import java.util.regex.Pattern;
30  
31  import org.apache.commons.codec.AbstractStringEncoderTest;
32  import org.apache.commons.codec.EncoderException;
33  import org.apache.commons.codec.StringEncoder;
34  import org.apache.commons.lang3.StringUtils;
35  import org.junit.jupiter.api.Disabled;
36  import org.junit.jupiter.api.Test;
37  import org.junit.jupiter.params.ParameterizedTest;
38  import org.junit.jupiter.params.provider.ValueSource;
39  
40  /**
41   * Tests {@link BeiderMorseEncoder}.
42   */
43  class BeiderMorseEncoderTest extends AbstractStringEncoderTest<StringEncoder> {
44  
45      private static final Pattern PIPE_PATTERN = Pattern.compile("\\|");
46  
47      private static final char[] TEST_CHARS = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'o', 'u' };
48  
49      private void assertNotEmpty(final BeiderMorseEncoder bmpm, final String value) throws EncoderException {
50          assertNotEquals("", bmpm.encode(value), value);
51      }
52  
53      private BeiderMorseEncoder createGenericApproxEncoder() {
54          final BeiderMorseEncoder encoder = new BeiderMorseEncoder();
55          encoder.setNameType(NameType.GENERIC);
56          encoder.setRuleType(RuleType.APPROX);
57          return encoder;
58      }
59  
60      @Override
61      protected StringEncoder createStringEncoder() {
62          return new BeiderMorseEncoder();
63      }
64  
65      /**
66       * Tests we do not blow up.
67       *
68       * @throws EncoderException Thrown for some failure scenarios     */
69      @Test
70      void testAllChars() throws EncoderException {
71          final BeiderMorseEncoder bmpm = createGenericApproxEncoder();
72          for (char c = Character.MIN_VALUE; c < Character.MAX_VALUE; c++) {
73              bmpm.encode(Character.toString(c));
74          }
75      }
76  
77      @Test
78      void testAsciiEncodeNotEmpty1Letter() throws EncoderException {
79          final BeiderMorseEncoder bmpm = createGenericApproxEncoder();
80          for (char c = 'a'; c <= 'z'; c++) {
81              final String value = Character.toString(c);
82              final String valueU = value.toUpperCase();
83              assertNotEmpty(bmpm, value);
84              assertNotEmpty(bmpm, valueU);
85          }
86      }
87  
88      @Test
89      void testAsciiEncodeNotEmpty2Letters() throws EncoderException {
90          final BeiderMorseEncoder bmpm = createGenericApproxEncoder();
91          for (char c1 = 'a'; c1 <= 'z'; c1++) {
92              for (char c2 = 'a'; c2 <= 'z'; c2++) {
93                  final String value = new String(new char[] { c1, c2 });
94                  final String valueU = value.toUpperCase();
95                  assertNotEmpty(bmpm, value);
96                  assertNotEmpty(bmpm, valueU);
97              }
98          }
99      }
100 
101     @ParameterizedTest
102     @ValueSource(ints = { 20_000, 100_000, 200_000, 400_000 })
103     void testDQuoteRepeatLarge(final int repeat) throws Exception {
104         final String source = StringUtils.repeat("d'", repeat) + "aaa";
105         assertEquals("(D|a|i|o)-(dD|da|di|do)",
106                 BeiderMorseEncoder.builder().setPhoneticEngine(PhoneticEngine.builder().setMaxInputLength(source.length()).get()).get().encode(source));
107     }
108 
109     @Test
110     void testEncodeAtzNotEmpty() throws EncoderException {
111         final BeiderMorseEncoder bmpm = createGenericApproxEncoder();
112         //String[] names = { "ácz", "átz", "Ignácz", "Ignátz", "Ignác" };
113         final String[] names = { "\u00e1cz", "\u00e1tz", "Ign\u00e1cz", "Ign\u00e1tz", "Ign\u00e1c" };
114         for (final String name : names) {
115             assertNotEmpty(bmpm, name);
116         }
117     }
118 
119     /**
120      * Tests https://issues.apache.org/jira/browse/CODEC-125?focusedCommentId=13071566&page=com.atlassian.jira.plugin.system.issuetabpanels:
121      * comment-tabpanel#comment-13071566
122      *
123      * @throws EncoderException Thrown for some failure scenarios.
124      */
125     @Test
126     void testEncodeGna() throws EncoderException {
127         final BeiderMorseEncoder bmpm = createGenericApproxEncoder();
128         bmpm.encode("gna");
129     }
130 
131     @Disabled("For performance testing.")
132     @ParameterizedTest
133     @ValueSource(ints = { 2_000, 8_000, 16_000, 32_000 })
134     void testEncodeLargePerf(final int target) throws EncoderException {
135         final String[] units = { "a", "e", "i", "o", "u", "ai", "ei", "ou", "au", "ie", "tsch", "sch", "zh", "kh", "ye", "yo" };
136         final Random r = new Random(1);
137         // default GENERIC/APPROX, maxPhonemes=20
138         final BeiderMorseEncoder enc = new BeiderMorseEncoder();
139         final StringBuilder sb = new StringBuilder();
140         while (sb.length() < target) {
141             sb.append(units[r.nextInt(units.length)]); // one long token, no spaces
142         }
143         assertTrue(enc.encode(sb.toString()).length() > 0);
144     }
145 
146     @Test
147     void testInvalidLangIllegalArgumentException() {
148         assertThrows(IllegalArgumentException.class, () -> Rule.getInstance(NameType.GENERIC, RuleType.APPROX, "noSuchLanguage"));
149     }
150 
151     @Test
152     void testInvalidLangIllegalStateException() {
153         assertThrows(IllegalArgumentException.class, () -> Lang.loadFromResource("thisIsAMadeUpResourceName", Languages.getInstance(NameType.GENERIC)));
154     }
155 
156     @Test
157     void testInvalidLanguageIllegalArgumentException() {
158         assertThrows(IllegalArgumentException.class, () -> Languages.getInstance("thereIsNoSuchLanguage"));
159     }
160 
161     @Test
162     void testLongestEnglishSurname() {
163         final BeiderMorseEncoder bmpm = createGenericApproxEncoder();
164         assertTimeout(Duration.ofMillis(10000L), () -> bmpm.encode("MacGhilleseatheanaich"));
165     }
166 
167     @Test
168     void testNegativeIndexForRuleMatchIndexOutOfBoundsException() {
169         final Rule r = new Rule("a", "", "", new Rule.Phoneme("", Languages.ANY_LANGUAGE));
170         assertThrows(IndexOutOfBoundsException.class, () -> r.patternAndContextMatches("bob", -1));
171     }
172 
173     @Test
174     void testOOM() throws EncoderException {
175         final String phrase = "200697900'-->&#1913348150;</  bceaeef >aadaabcf\"aedfbff<!--\'-->?>cae" +
176             "cfaaa><?&#<!--</script>&lang&fc;aadeaf?>>&bdquo<    cc =\"abff\"    /></   afe  >" +
177             "<script><!-- f(';<    cf aefbeef = \"bfabadcf\" ebbfeedd = fccabeb >";
178 
179         final BeiderMorseEncoder encoder = new BeiderMorseEncoder();
180         encoder.setNameType(NameType.GENERIC);
181         encoder.setRuleType(RuleType.EXACT);
182         encoder.setMaxPhonemes(10);
183 
184         final String phonemes = encoder.encode(phrase);
185         assertFalse(phonemes.isEmpty());
186 
187         final String[] phonemeArr = PIPE_PATTERN.split(phonemes);
188         assertTrue(phonemeArr.length <= 10);
189     }
190 
191     @Test
192     void testSetConcat() {
193         final BeiderMorseEncoder bmpm = new BeiderMorseEncoder();
194         bmpm.setConcat(false);
195         assertFalse(bmpm.isConcat(), "Should be able to set concat to false");
196     }
197 
198     @Test
199     void testSetNameTypeAsh() {
200         final BeiderMorseEncoder bmpm = new BeiderMorseEncoder();
201         bmpm.setNameType(NameType.ASHKENAZI);
202         assertEquals(NameType.ASHKENAZI, bmpm.getNameType(), "Name type should have been set to ash");
203     }
204 
205     @Test
206     void testSetRuleTypeExact() {
207         final BeiderMorseEncoder bmpm = new BeiderMorseEncoder();
208         bmpm.setRuleType(RuleType.EXACT);
209         assertEquals(RuleType.EXACT, bmpm.getRuleType(), "Rule type should have been set to exact");
210     }
211 
212     @Test
213     void testSetRuleTypeToRulesIllegalArgumentException() {
214         final BeiderMorseEncoder bmpm = new BeiderMorseEncoder();
215         assertThrows(IllegalArgumentException.class, () -> bmpm.setRuleType(RuleType.RULES));
216     }
217 
218     /**
219      * (Un)luckily, the worse performing test because of the data in the test characters.
220      *
221      * @throws EncoderException Thrown for some failure scenarios
222      */
223     @Test /* timeout = 20000L */
224     void testSpeedCheck() throws EncoderException {
225         final BeiderMorseEncoder bmpm = createGenericApproxEncoder();
226         final StringBuilder stringBuffer = new StringBuilder();
227         stringBuffer.append(TEST_CHARS[0]);
228         for (int i = 0, j = 1; i < 40; i++, j++) {
229             if (j == TEST_CHARS.length) {
230                 j = 0;
231             }
232             bmpm.encode(stringBuffer.toString());
233             stringBuffer.append(TEST_CHARS[j]);
234         }
235     }
236 
237     @Test
238     void testSpeedCheck2() throws EncoderException {
239         final BeiderMorseEncoder bmpm = createGenericApproxEncoder();
240         final String phrase = "ItstheendoftheworldasweknowitandIfeelfine";
241 
242         for (int i = 1; i <= phrase.length(); i++) {
243             bmpm.encode(phrase.subSequence(0, i));
244         }
245     }
246 
247     @Test
248     void testSpeedCheck3() throws EncoderException {
249         final BeiderMorseEncoder bmpm = createGenericApproxEncoder();
250         final String phrase = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";
251 
252         for (int i = 1; i <= phrase.length(); i++) {
253             bmpm.encode(phrase.subSequence(0, i));
254         }
255     }
256 }