1 // © 2019 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 package org.unicode.icu.tool.cldrtoicu.mapper; 4 5 import static com.google.common.truth.Truth.assertThat; 6 import static org.unicode.icu.tool.cldrtoicu.testing.AssertUtils.assertThrows; 7 import static org.unicode.icu.tool.cldrtoicu.testing.IcuDataSubjectFactory.assertThat; 8 9 import java.util.ArrayList; 10 import java.util.Arrays; 11 import java.util.List; 12 13 import org.junit.Test; 14 import org.junit.runner.RunWith; 15 import org.junit.runners.JUnit4; 16 import org.unicode.cldr.api.CldrData; 17 import org.unicode.cldr.api.CldrPath; 18 import org.unicode.cldr.api.CldrValue; 19 import org.unicode.icu.tool.cldrtoicu.IcuData; 20 import org.unicode.icu.tool.cldrtoicu.PathValueTransformer.Result; 21 import org.unicode.icu.tool.cldrtoicu.RbValue; 22 import org.unicode.icu.tool.cldrtoicu.testing.FakeResult; 23 import org.unicode.icu.tool.cldrtoicu.testing.FakeTransformer; 24 25 @RunWith(JUnit4.class) 26 public class AbstractPathValueMapperTest { 27 @Test testUngroupedConcatenation()28 public void testUngroupedConcatenation() { 29 FakeMapper mapper = new FakeMapper(); 30 mapper.addUngroupedResult("foo/bar", "one", "two"); 31 mapper.addUngroupedResult("foo/baz", "other", "path"); 32 mapper.addUngroupedResult("foo/bar", "three", "four"); 33 IcuData icuData = mapper.addIcuData("foo"); 34 35 assertThat(icuData).getPaths().hasSize(2); 36 assertThat(icuData).hasValuesFor("foo/bar", singletonValues("one", "two", "three", "four")); 37 assertThat(icuData).hasValuesFor("foo/baz", singletonValues("other", "path")); 38 } 39 40 @Test testGrouping()41 public void testGrouping() { 42 FakeMapper mapper = new FakeMapper(); 43 mapper.addGroupedResult("foo/bar", "one", "two"); 44 mapper.addGroupedResult("foo/baz", "other", "path"); 45 mapper.addGroupedResult("foo/bar", "three", "four"); 46 IcuData icuData = mapper.addIcuData("foo"); 47 48 assertThat(icuData).getPaths().hasSize(2); 49 assertThat(icuData) 50 .hasValuesFor("foo/bar", RbValue.of("one", "two"), RbValue.of("three", "four")); 51 assertThat(icuData) 52 .hasValuesFor("foo/baz", RbValue.of("other", "path")); 53 } 54 55 @Test testFallbackResults()56 public void testFallbackResults() { 57 // The indices are important in matching up the results and their respective fallbacks. 58 Result explicit1 = FakeResult.of("foo/bar", 1, false, "one"); 59 Result explicit2 = FakeResult.of("foo/bar", 2, false, "two"); 60 Result explicit3 = FakeResult.of("foo/bar", 3, false, "three"); 61 62 Result fallback1 = FakeResult.fallback("foo/bar", 1, "<ONE>"); 63 Result fallback2 = FakeResult.fallback("foo/bar", 2, "<TWO>"); 64 Result fallback3 = FakeResult.fallback("foo/bar", 3, "<THREE>"); 65 66 FakeTransformer transformer = new FakeTransformer(); 67 transformer.addFallbacks("foo/bar", fallback1, fallback2, fallback3); 68 69 // When all results are explicitly present, no fallbacks are used. 70 IcuData noFallback = new FakeMapper(transformer) 71 .addResult(explicit1) 72 .addResult(explicit2) 73 .addResult(explicit3) 74 .addIcuData("foo"); 75 assertThat(noFallback).hasValuesFor("foo/bar", singletonValues("one", "two", "three")); 76 77 // Missing explicit results trigger fallbacks. 78 IcuData firstFallback = new FakeMapper(transformer) 79 .addResult(explicit2) 80 .addResult(explicit3) 81 .addIcuData("foo"); 82 assertThat(firstFallback).hasValuesFor("foo/bar", singletonValues("<ONE>", "two", "three")); 83 84 // Fallbacks can appear in any part of the result sequence. 85 IcuData lastFallbacks = new FakeMapper(transformer) 86 .addResult(explicit1) 87 .addIcuData("foo"); 88 assertThat(lastFallbacks) 89 .hasValuesFor("foo/bar", singletonValues("one", "<TWO>", "<THREE>")); 90 91 // Without a single result to "seed" the fallback group, nothing is emitted. 92 IcuData allFallbacks = new FakeMapper(transformer).addIcuData("foo"); 93 assertThat(allFallbacks).getPaths().isEmpty(); 94 } 95 96 @Test testAliases_ungrouped()97 public void testAliases_ungrouped() { 98 FakeMapper mapper = new FakeMapper(); 99 mapper.addUngroupedResult("foo/default", "start", "/alias/target", "end"); 100 mapper.addUngroupedResult("foo/alias-0", "start", "/alias/target[0]", "end"); 101 mapper.addUngroupedResult("foo/alias-1", "start", "/alias/target[1]", "end"); 102 mapper.addUngroupedResult("foo/alias-2", "start", "/alias/target[2]", "end"); 103 mapper.addUngroupedResult("alias/target", "first", "second", "third"); 104 IcuData icuData = mapper.addIcuData("foo"); 105 106 assertThat(icuData).getPaths().hasSize(5); 107 assertThat(icuData) 108 .hasValuesFor("foo/default", singletonValues("start", "first", "end")); 109 assertThat(icuData) 110 .hasValuesFor("foo/alias-0", singletonValues("start", "first", "end")); 111 assertThat(icuData) 112 .hasValuesFor("foo/alias-1", singletonValues("start", "second", "end")); 113 assertThat(icuData) 114 .hasValuesFor("foo/alias-2", singletonValues("start", "third", "end")); 115 assertThat(icuData) 116 .hasValuesFor("alias/target", singletonValues("first", "second", "third")); 117 } 118 119 // Grouping ignores aliases. 120 @Test testAliases_grouped()121 public void testAliases_grouped() { 122 FakeMapper mapper = new FakeMapper(); 123 mapper.addGroupedResult("foo/bar", "grouped", "/alias/target"); 124 mapper.addGroupedResult("foo/bar", "/alias/target[1]"); 125 mapper.addUngroupedResult("alias/target", "first", "second"); 126 127 IcuData icuData = mapper.addIcuData("foo"); 128 assertThat(icuData).getPaths().hasSize(2); 129 assertThat(icuData) 130 .hasValuesFor("foo/bar", 131 RbValue.of("grouped", "/alias/target"), 132 RbValue.of("/alias/target[1]")); 133 assertThat(icuData).hasValuesFor("alias/target", singletonValues("first", "second")); 134 } 135 136 @Test testAliases_explicit()137 public void testAliases_explicit() { 138 FakeMapper mapper = new FakeMapper(); 139 mapper.addUngroupedResult("foo/bar:alias", "/alias/target"); 140 mapper.addUngroupedResult("foo/bar", "/alias/target"); 141 mapper.addUngroupedResult("alias/target", "alias-value"); 142 IcuData icuData = mapper.addIcuData("foo"); 143 144 assertThat(icuData).getPaths().hasSize(3); 145 assertThat(icuData).hasValuesFor("foo/bar:alias", singletonValues("/alias/target")); 146 assertThat(icuData).hasValuesFor("foo/bar", singletonValues("alias-value")); 147 assertThat(icuData).hasValuesFor("alias/target", singletonValues("alias-value")); 148 } 149 150 @Test testAliases_ordering()151 public void testAliases_ordering() { 152 // It doesn't matter where an alias is in the order of results. 153 FakeMapper mapper = new FakeMapper(); 154 mapper.addUngroupedResult("first/alias", "hello"); 155 mapper.addUngroupedResult("foo/bar", "/first/alias", "/last/alias"); 156 mapper.addUngroupedResult("last/alias", "world"); 157 IcuData icuData = mapper.addIcuData("foo"); 158 159 assertThat(icuData).hasValuesFor("foo/bar", singletonValues("hello", "world")); 160 } 161 162 @Test testAliases_concatenation()163 public void testAliases_concatenation() { 164 // It doesn't matter where an alias is in the order of results. 165 FakeMapper mapper = new FakeMapper(); 166 mapper.addUngroupedResult("alias/target", "hello"); 167 mapper.addUngroupedResult("foo/bar", "/alias/target[0]", "/alias/target[1]"); 168 mapper.addUngroupedResult("alias/target", "world"); 169 IcuData icuData = mapper.addIcuData("foo"); 170 171 assertThat(icuData).hasValuesFor("foo/bar", singletonValues("hello", "world")); 172 } 173 174 @Test testAliases_missing()175 public void testAliases_missing() { 176 FakeMapper mapper = new FakeMapper(); 177 mapper.addUngroupedResult("alias/target", "value"); 178 mapper.addUngroupedResult("foo/bar", "/no-such-alias/target"); 179 IllegalArgumentException e = 180 assertThrows(IllegalArgumentException.class, () -> mapper.addIcuData("foo")); 181 assertThat(e).hasMessageThat().contains("no such alias value"); 182 assertThat(e).hasMessageThat().contains("/no-such-alias/target"); 183 } 184 185 @Test testAliases_badIndex()186 public void testAliases_badIndex() { 187 FakeMapper mapper = new FakeMapper(); 188 mapper.addUngroupedResult("alias/target", "value"); 189 mapper.addUngroupedResult("foo/bar", "/alias/target[1]"); 190 IllegalArgumentException e = 191 assertThrows(IllegalArgumentException.class, () -> mapper.addIcuData("foo")); 192 assertThat(e).hasMessageThat().contains("out of bounds"); 193 assertThat(e).hasMessageThat().contains("/alias/target[1]"); 194 } 195 196 @Test testAliases_noRecursion()197 public void testAliases_noRecursion() { 198 FakeMapper mapper = new FakeMapper(); 199 mapper.addUngroupedResult("alias/target", "/other/alias"); 200 mapper.addUngroupedResult("other/alias", "/other/alias"); 201 mapper.addUngroupedResult("foo/bar", "/alias/target"); 202 IllegalStateException e = 203 assertThrows(IllegalStateException.class, () -> mapper.addIcuData("foo")); 204 assertThat(e).hasMessageThat().contains("recursive alias resolution is not supported"); 205 } 206 207 @Test testAliases_explicitAliasesAreSingletonOnly()208 public void testAliases_explicitAliasesAreSingletonOnly() { 209 FakeMapper mapper = new FakeMapper(); 210 mapper.addUngroupedResult("foo/bar:alias", "first", "second"); 211 IllegalArgumentException e = 212 assertThrows(IllegalArgumentException.class, () -> mapper.addIcuData("foo")); 213 assertThat(e).hasMessageThat().contains("explicit aliases must be singleton values"); 214 assertThat(e).hasMessageThat().contains("foo/bar:alias"); 215 } 216 217 private static final class FakeMapper extends AbstractPathValueMapper { 218 private final static CldrData EXPLODING_DATA = 219 new CldrData() { 220 @Override public void accept(PathOrder pathOrder, ValueVisitor valueVisitor) { 221 throw new UnsupportedOperationException("should not be called by test"); 222 } 223 224 @Override public void accept(PathOrder pathOrder, PrefixVisitor prefixVisitor) { 225 throw new UnsupportedOperationException("should not be called by test"); 226 } 227 228 @Override public CldrValue get(CldrPath cldrPath) { 229 throw new UnsupportedOperationException("should not be called by test"); 230 } 231 }; 232 233 // This preserves insertion order in a well defined way (good for testing alias order). 234 private final List<Result> fakeResults = new ArrayList<>(); 235 FakeMapper()236 FakeMapper() { 237 this(new FakeTransformer()); 238 } 239 FakeMapper(FakeTransformer transformer)240 FakeMapper(FakeTransformer transformer) { 241 super(EXPLODING_DATA, transformer); 242 } 243 244 // Helper method to neaten up the tests a bit. addIcuData(String localeId)245 IcuData addIcuData(String localeId) { 246 IcuData icuData = new IcuData(localeId, true); 247 addIcuData(icuData); 248 return icuData; 249 } 250 addUngroupedResult(String path, String... values)251 FakeMapper addUngroupedResult(String path, String... values) { 252 int index = fakeResults.size() + 1; 253 return addResult(FakeResult.of(path, index, false, values)); 254 } 255 addGroupedResult(String path, String... values)256 FakeMapper addGroupedResult(String path, String... values) { 257 int index = fakeResults.size() + 1; 258 return addResult(FakeResult.of(path, index, true, values)); 259 } 260 addResult(Result r)261 FakeMapper addResult(Result r) { 262 fakeResults.add(r); 263 return this; 264 } 265 addResults()266 @Override void addResults() { 267 fakeResults.forEach(result -> addResult(result.getKey(), result)); 268 } 269 } 270 singletonValues(String... values)271 private static RbValue[] singletonValues(String... values) { 272 return Arrays.stream(values).map(RbValue::of).toArray(RbValue[]::new); 273 } 274 }