1 package org.unicode.cldr.unittest; 2 /* 3 * TODO: rename this file and class to avoid confusion with org.unicode.cldr.util TestUtilities.java 4 * When Eclipse console shows an error such as 5 * Error: (TestUtilities.java:1154) : 8 value: expected "old-value", got null 6 * the link wrongly opens the wrong file named TestUtilities.java. The two files are: 7 * cldr/tools/cldr-code/src/main/java/org/unicode/cldr/util/TestUtilities.java 8 * cldr/tools/cldr-code/src/test/java/org/unicode/cldr/unittest/TestUtilities.java 9 */ 10 11 import com.google.common.collect.ImmutableMap; 12 import com.google.common.collect.Ordering; 13 import com.ibm.icu.dev.util.UnicodeMap; 14 import com.ibm.icu.impl.Relation; 15 import com.ibm.icu.impl.Utility; 16 import com.ibm.icu.lang.UCharacter; 17 import com.ibm.icu.lang.UProperty; 18 import com.ibm.icu.text.Collator; 19 import com.ibm.icu.text.UnicodeSet; 20 import com.ibm.icu.util.ULocale; 21 import java.io.StringWriter; 22 import java.text.NumberFormat; 23 import java.util.ArrayList; 24 import java.util.Arrays; 25 import java.util.Collections; 26 import java.util.Comparator; 27 import java.util.HashMap; 28 import java.util.HashSet; 29 import java.util.LinkedHashSet; 30 import java.util.List; 31 import java.util.Map; 32 import java.util.Map.Entry; 33 import java.util.Random; 34 import java.util.Set; 35 import java.util.TreeMap; 36 import java.util.TreeSet; 37 import java.util.regex.Matcher; 38 import org.unicode.cldr.test.SubmissionLocales; 39 import org.unicode.cldr.tool.ConvertLanguageData.InverseComparator; 40 import org.unicode.cldr.util.*; 41 import org.unicode.cldr.util.PathHeader.PageId; 42 import org.unicode.cldr.util.SupplementalDataInfo.PluralInfo.Count; 43 import org.unicode.cldr.util.VettingViewer.MissingStatus; 44 import org.unicode.cldr.util.VoteResolver.Level; 45 import org.unicode.cldr.util.VoteResolver.Status; 46 import org.unicode.cldr.util.VoteResolver.VoteStatus; 47 import org.unicode.cldr.util.VoteResolver.VoterInfo; 48 import org.unicode.cldr.util.props.ICUPropertyFactory; 49 50 public class TestUtilities extends TestFmwkPlus { 51 public static boolean DEBUG = true; 52 53 private static final UnicodeSet DIGITS = new UnicodeSet("[0-9]"); 54 static CLDRConfig testInfo = CLDRConfig.getInstance(); 55 private static final SupplementalDataInfo SUPPLEMENTAL_DATA_INFO = 56 testInfo.getSupplementalDataInfo(); 57 private static final int STRING_ID_TEST_COUNT = 1024 * 16; 58 59 final int ONE_VETTER_BAR = Level.vetter.getVotes(Organization.unaffiliated); 60 final int TWO_VETTER_BAR = VoteResolver.LOWER_BAR; 61 main(String[] args)62 public static void main(String[] args) { 63 new TestUtilities().run(args); 64 } 65 TestPluralSamples()66 public void TestPluralSamples() { 67 checkPluralSamples("en"); 68 checkPluralSamples("cs"); 69 checkPluralSamples("ar"); 70 } 71 checkPluralSamples(String locale)72 private void checkPluralSamples(String locale) { 73 PluralSamples pluralSamples = PluralSamples.getInstance(locale); 74 Set<Count> counts = SUPPLEMENTAL_DATA_INFO.getPlurals(locale).getCounts(); 75 for (int i = 1; i < 5; ++i) { 76 Map<Count, Double> samplesForDigits = pluralSamples.getSamples(i); 77 if (!counts.containsAll(samplesForDigits.keySet())) { 78 errln( 79 locale 80 + ": mismatch in samples, expected " 81 + counts 82 + ", got: " 83 + samplesForDigits); 84 } else if (samplesForDigits.size() == 0) { 85 errln(locale + ": no sample for digit " + i); 86 } else { 87 logln(locale + " plural samples: " + samplesForDigits); 88 } 89 } 90 } 91 92 public static class StringIdException extends RuntimeException { 93 private static final long serialVersionUID = 1L; 94 } 95 96 public class StringIdThread extends Thread { 97 private final Random r = new Random(); 98 private final int id; 99 StringIdThread(int i)100 StringIdThread(int i) { 101 super("Demo Thread"); 102 id = i; 103 } 104 105 @Override run()106 public void run() { 107 logln("Starting thread: " + this); 108 for (int i = 0; i < STRING_ID_TEST_COUNT; ++i) { 109 String s = String.valueOf(r.nextInt()); 110 long l = StringId.getId(s); 111 String s2 = StringId.getStringFromId(l); 112 if (!s.equals(s2)) { 113 throw new StringIdException(); 114 } 115 } 116 logln("Ending thread: " + this); 117 } 118 119 @Override toString()120 public String toString() { 121 return "StringIdThread " + id; 122 } 123 } 124 TestStringId()125 public void TestStringId() { 126 ArrayList<StringIdThread> threads = new ArrayList<>(); 127 128 for (int i = 0; i < 8; i++) { 129 StringIdThread thread = new StringIdThread(i); 130 threads.add(thread); 131 thread.start(); 132 } 133 for (StringIdThread thread : threads) { 134 try { 135 thread.join(); 136 } catch (InterruptedException e) { 137 errln(e.toString()); 138 } 139 } 140 } 141 TestUrlEscape()142 public void TestUrlEscape() { 143 Matcher byte1 = PatternCache.get("%[A-Za-z0-9]{2}").matcher(""); 144 Matcher byte2 = PatternCache.get("%[A-Za-z0-9]{2}%[A-Za-z0-9]{2}").matcher(""); 145 Matcher byte3 = 146 PatternCache.get("%[A-Za-z0-9]{2}%[A-Za-z0-9]{2}%[A-Za-z0-9]{2}").matcher(""); 147 Matcher byte4 = 148 PatternCache.get("%[A-Za-z0-9]{2}%[A-Za-z0-9]{2}%[A-Za-z0-9]{2}%[A-Za-z0-9]{2}") 149 .matcher(""); 150 for (int i = 1; i <= 0x10FFFF; i = i * 3 / 2 + 1) { 151 String escaped = 152 EscapingUtilities.urlEscape(new StringBuilder().appendCodePoint(i).toString()); 153 logln(Integer.toHexString(i) + " => " + escaped); 154 if (EscapingUtilities.OK_TO_NOT_QUOTE.contains(i)) { 155 assertTrue("Should be unquoted", escaped.length() == 1); 156 } else if (i < 0x80) { 157 assertTrue("Should be %xx", byte1.reset(escaped).matches()); 158 } else if (i < 0x800) { 159 assertTrue("Should be %xx%xx", byte2.reset(escaped).matches()); 160 } else if (i < 0x10000) { 161 assertTrue("Should be %xx%xx%xx", byte3.reset(escaped).matches()); 162 } else { 163 assertTrue("Should be %xx%xx%xx%xx", byte4.reset(escaped).matches()); 164 } 165 } 166 } 167 TestDelegatingIterator()168 public void TestDelegatingIterator() { 169 Set<String> s = new TreeSet<>(Arrays.asList(new String[] {"a", "b", "c"})); 170 Set<String> t = new LinkedHashSet<>(Arrays.asList(new String[] {"f", "d", "e"})); 171 StringBuilder result = new StringBuilder(); 172 173 for (String u : DelegatingIterator.iterable(s, t)) { 174 result.append(u); 175 } 176 assertEquals("Iterator", "abcfde", result.toString()); 177 178 result.setLength(0); 179 for (String u : DelegatingIterator.array("s", "t", "u")) { 180 result.append(u); 181 } 182 assertEquals("Iterator", "stu", result.toString()); 183 184 int count = 0; 185 result.setLength(0); 186 for (int u : DelegatingIterator.array(1, 3, 5)) { 187 count += u; 188 } 189 assertEquals("Iterator", 9, count); 190 191 result.setLength(0); 192 for (Object u : DelegatingIterator.array(1, "t", "u", new UnicodeSet("[a-z]"))) { 193 result.append(u); 194 } 195 assertEquals("Iterator", "1tu[a-z]", result.toString()); 196 } 197 TestUntimedCounter()198 public void TestUntimedCounter() { 199 // simulates how Counter is used in VettingViewer 200 Counter<NotificationCategory> problemCounter = new Counter<>(); 201 problemCounter.increment(NotificationCategory.error); 202 problemCounter.increment(NotificationCategory.error); 203 problemCounter.increment(NotificationCategory.warning); 204 205 assertEquals("problemCounter error", 2, problemCounter.get(NotificationCategory.error)); 206 assertEquals("problemCounter warning", 1, problemCounter.get(NotificationCategory.warning)); 207 assertEquals("problemCounter weLost", 0, problemCounter.get(NotificationCategory.weLost)); 208 209 Counter<NotificationCategory> otherCounter = new Counter<>(); 210 otherCounter.addAll(problemCounter); 211 otherCounter.increment(NotificationCategory.error); 212 213 assertEquals("otherCounter error", 3, otherCounter.get(NotificationCategory.error)); 214 assertEquals("otherCounter warning", 1, otherCounter.get(NotificationCategory.warning)); 215 assertEquals("otherCounter weLost", 0, otherCounter.get(NotificationCategory.weLost)); 216 } 217 TestCounter()218 public void TestCounter() { 219 Counter<String> counter = new Counter<>(true); 220 Comparator<String> uca = 221 new Comparator<>() { 222 Collator col = Collator.getInstance(ULocale.ENGLISH); 223 224 @Override 225 public int compare(String o1, String o2) { 226 return col.compare(o1, o2); 227 } 228 }; 229 InverseComparator ucaDown = new InverseComparator(uca); 230 231 counter.add("c", 95); 232 counter.add("b", 50); 233 counter.add("b", 101); 234 counter.add("a", 100); 235 counter.add("a", -5); 236 counter.add("d", -3); 237 assertEquals("getCount(b)", counter.getCount("b"), 151); 238 assertEquals("getCount(a)", counter.getCount("a"), 95); 239 assertEquals("getCount(a)", counter.getTotal(), 338); 240 assertEquals("getItemCount", counter.getItemCount(), 4); 241 242 assertEquals("getMap", "{a=95, b=151, c=95, d=-3}", counter.toString()); 243 244 assertEquals( 245 "getKeysetSortedByKey", 246 Arrays.asList("a", "b", "c", "d"), 247 new ArrayList<>(counter.getKeysetSortedByKey())); 248 249 assertEquals( 250 "getKeysetSortedByCount(true, ucaDown)", 251 Arrays.asList("d", "c", "a", "b"), 252 new ArrayList<String>(counter.getKeysetSortedByCount(true, ucaDown))); 253 254 assertEquals( 255 "getKeysetSortedByCount(true, null), value", 256 Arrays.asList("d", "a", "c", "b"), 257 new ArrayList<>(counter.getKeysetSortedByCount(true, uca))); 258 259 assertEquals( 260 "getKeysetSortedByCount(false, ucaDown), descending", 261 Arrays.asList("b", "c", "a", "d"), 262 new ArrayList<String>(counter.getKeysetSortedByCount(false, ucaDown))); 263 264 assertEquals( 265 "getKeysetSortedByCount(false, null), descending, value", 266 Arrays.asList("b", "a", "c", "d"), 267 new ArrayList<>(counter.getKeysetSortedByCount(false, uca))); 268 } 269 TestOrganizationOrder()270 public void TestOrganizationOrder() { 271 Map<String, Organization> stringToOrg = new TreeMap<>(); 272 for (Organization org : Organization.values()) { 273 stringToOrg.put(org.toString(), org); 274 } 275 List<Organization> reordered = new ArrayList<>(stringToOrg.values()); 276 List<Organization> plain = Arrays.asList(Organization.values()); 277 for (int i = 0; i < reordered.size(); ++i) { 278 assertEquals("Items not in alphabetical order", reordered.get(i), plain.get(i)); 279 } 280 } 281 TestOrganizationNames()282 public void TestOrganizationNames() { 283 UnicodeSet uppercase = new UnicodeSet("[:uppercase:]"); 284 for (Organization org : Organization.values()) { 285 if (!uppercase.contains(org.getDisplayName().codePointAt(0))) { 286 errln("Organization name isn't titlecased: " + org + ", " + org.getDisplayName()); 287 } 288 assertEquals( 289 "Organization from enum name", org, Organization.fromString(org.toString())); 290 assertEquals( 291 "Organization from display name", 292 org, 293 Organization.fromString(org.getDisplayName())); 294 } 295 } 296 297 static final boolean SHOW_DETAILS = CldrUtility.getProperty("showdetails", false); 298 private static final CharSequence DEBUG_COMMENT = 299 "set up a case of conflict within organization"; 300 301 static class PathValueInfo { 302 private static Map<Integer, String> voteInfo; 303 private CLDRFile file; 304 PathValueInfo(Factory factory, String locale)305 public PathValueInfo(Factory factory, String locale) { 306 this.file = factory.make(locale, false); 307 } 308 getRealValue(int id)309 public String getRealValue(int id) { 310 return file.getStringValue(getRealPath(id)); 311 } 312 getRealPath(int id)313 public String getRealPath(int id) { 314 return voteInfo.get(id); 315 } 316 } 317 318 /** Test user data. Restructured to be easier to read, more typesafe */ 319 public enum TestUser { 320 unaffiliatedS(801, Organization.unaffiliated, Level.guest), 321 gnomeS(701, Organization.gnome, Level.guest), 322 gnomeV(702, Organization.gnome, Level.vetter), 323 googleV(404, Organization.google, Level.vetter), 324 googleS(411, Organization.google, Level.guest), 325 googleV2(424, Organization.google, Level.vetter), 326 appleV(304, Organization.apple, Level.vetter), 327 adobeE(204, Organization.adobe, Level.manager), 328 adobeV(209, Organization.adobe, Level.vetter), 329 ibmS(101, Organization.ibm, Level.guest), 330 microsoftV(134, Organization.microsoft, Level.vetter), 331 ibmE(114, Organization.ibm, Level.manager), 332 ibmT(129, Organization.ibm, Level.tc), 333 unaffiliatedS2(802, Organization.unaffiliated, Level.guest); 334 335 public static final Map<Integer, VoterInfo> TEST_USERS; 336 public final Integer voterId; 337 public final VoterInfo voterInfo; 338 TestUser(int intVoterId, Organization organization, Level level)339 TestUser(int intVoterId, Organization organization, Level level) { 340 voterId = intVoterId; 341 voterInfo = new VoterInfo(organization, level, name()); 342 } 343 344 static { 345 ImmutableMap.Builder<Integer, VoterInfo> temp = ImmutableMap.builder(); 346 for (TestUser testUser : values()) { temp.put(testUser.voterId, testUser.voterInfo)347 temp.put(testUser.voterId, testUser.voterInfo); 348 } 349 TEST_USERS = temp.build(); 350 } 351 } 352 353 public static final Map<Integer, VoterInfo> testdata = TestUser.TEST_USERS; 354 toVoterId(String s)355 private int toVoterId(String s) { 356 return TestUser.valueOf(s).voterId; 357 } 358 359 /** Public to use from other tests */ getTestVoterInfoList()360 public static VoterInfoList getTestVoterInfoList() { 361 return new VoterInfoList().setVoterToInfo(testdata); 362 } 363 TestTrunkStatus()364 public void TestTrunkStatus() { 365 VoteResolver<String> resolver = new VoteResolver<>(getTestVoterInfoList()); 366 resolver.setLocale(CLDRLocale.getInstance("de"), null); 367 resolver.setBaileyValue("bailey"); 368 resolver.setBaseline("new-item", Status.approved); 369 assertEquals("", "new-item", resolver.getWinningValue()); 370 371 /* 372 * Formerly last-release would win over trunk in a 2nd scenario here, due to 373 * the difference in status. Now last-release plays no role, that test is obsolete. 374 * Reference: https://unicode.org/cldr/trac/ticket/11916 375 */ 376 } 377 TestVoteResolverNgombaTrunkStatus()378 public void TestVoteResolverNgombaTrunkStatus() { 379 VoteResolver<String> resolver = new VoteResolver<>(getTestVoterInfoList()); 380 resolver.setBaileyValue("bailey"); 381 resolver.setLocale(CLDRLocale.getInstance("jgo"), null); 382 final String jgo22trunk = 383 "\uA78C"; // "[a á â ǎ b c d ɛ {ɛ́} {ɛ̂} {ɛ̌} {ɛ̀} {ɛ̄} f ɡ h i í î ǐ j k l m ḿ {m̀} 384 // {m̄} n ń ǹ {n̄} ŋ {ŋ́} {ŋ̀} {ŋ̄} ɔ {ɔ́} {ɔ̂} {ɔ̌} p {pf} s {sh} t {ts} 385 // u ú û ǔ ʉ {ʉ́} {ʉ̂} {ʉ̌} {ʉ̈} v w ẅ y z ꞌ]"; 386 resolver.setBaseline(jgo22trunk, Status.approved); // seed/jgo.xml from 22 387 // trunk 388 logln("SVN: " + jgo22trunk); 389 logln(resolver.toString()); 390 assertEquals("Winning Value", jgo22trunk, resolver.getWinningValue()); 391 } 392 TestVoteStatus()393 public void TestVoteStatus() { 394 VoteResolver<String> resolver = new VoteResolver<>(getTestVoterInfoList()); 395 396 resolver.setLocale(CLDRLocale.getInstance("de"), null); 397 resolver.setBaileyValue("bailey"); 398 resolver.setBaseline("foo", Status.approved); 399 resolver.add("fii", toVoterId("adobeE")); 400 resolver.add("fii", toVoterId("appleV")); 401 VoteStatus voteStatus; 402 voteStatus = resolver.getStatusForOrganization(Organization.google); 403 assertEquals("", VoteResolver.VoteStatus.ok, voteStatus); 404 voteStatus = resolver.getStatusForOrganization(Organization.apple); 405 assertEquals("", VoteResolver.VoteStatus.ok, voteStatus); 406 407 // make non-equal foo 408 String s1 = "foo"; 409 String s2 = new StringBuilder("fo").append("o").toString(); 410 if (s1 == s2) { 411 errln("Test problem"); 412 } 413 resolver.clear(); 414 resolver.setBaileyValue("bailey"); 415 resolver.setBaseline(s1, Status.approved); 416 resolver.add(s2, toVoterId("appleV")); 417 voteStatus = resolver.getStatusForOrganization(Organization.apple); 418 assertEquals("", VoteResolver.VoteStatus.ok, voteStatus); 419 } 420 TestLosingStatus()421 public void TestLosingStatus() { 422 // af 423 // losing? {baseline: {BQ, missing}, trunk: {null, null}, 424 // {orgToVotes: , totals: {}, conflicted: []}, 425 // sameVotes: [BQ], O: null, N: null, totals: {}, winning: {BQ, 426 // missing}} 427 // XPath: //ldml/localeDisplayNames/territories/territory[@type="BQ"] 428 // gcvs.openoffice_org.example.com 429 VoteResolver<String> resolver = new VoteResolver<>(getTestVoterInfoList()); 430 431 resolver.setLocale(CLDRLocale.getInstance("af"), null); 432 resolver.setBaseline("BQ", Status.missing); 433 resolver.setBaileyValue("bailey"); 434 VoteStatus status = resolver.getStatusForOrganization(Organization.openoffice_org); 435 assertEquals("", VoteResolver.VoteStatus.provisionalOrWorse, status); 436 437 // {lastRelease: {{0}: {1}, missing}, trunk: {null, null}, {orgToVotes: 438 // pakistan={{0}: {1}=8}, totals: {{0}: 439 // {1}=8}, conflicted: []}, sameVotes: [{0}: {1}], O: {0}: {1}, N: null, 440 // totals: {{0}: {1}=8}, winning: {{0}: 441 // {1}, approved}} 442 resolver.clear(); 443 resolver.setBaileyValue("bailey"); 444 // resolver.setLastRelease("{0}: {1}", Status.missing); 445 resolver.add("{0}: {1}", toVoterId("adobeE")); 446 status = resolver.getStatusForOrganization(Organization.openoffice_org); 447 assertEquals("", VoteResolver.VoteStatus.ok, status); 448 449 // {lastRelease: {Arabisch, approved}, trunk: {Arabisch, approved}, 450 // {orgToVotes: , totals: {}, conflicted: []}, 451 // sameVotes: [Arabisch], O: null, N: null, totals: {}, winning: 452 // {Arabisch, approved}} 453 resolver.clear(); 454 resolver.setBaileyValue("bailey"); 455 // resolver.setLastRelease("Arabisch", Status.approved); 456 resolver.setBaseline("Arabisch", Status.approved); 457 status = resolver.getStatusForOrganization(Organization.openoffice_org); 458 assertEquals("", VoteResolver.VoteStatus.ok_novotes, status); 459 } 460 TestTotalVotesStatus()461 public void TestTotalVotesStatus() { 462 VoteResolver<String> resolver = new VoteResolver<>(getTestVoterInfoList()); 463 464 Status oldStatus = Status.unconfirmed; 465 466 resolver.setBaileyValue("bailey"); 467 resolver.setLocale(CLDRLocale.getInstance("de"), null); 468 resolver.setBaseline("foo", oldStatus); 469 resolver.add("zebra", toVoterId("googleV")); 470 resolver.add("apple", toVoterId("appleV")); 471 472 // check that alphabetical wins when votes are equal 473 String winner = resolver.getWinningValue(); 474 Status winningStatus = resolver.getWinningStatus(); 475 assertEquals("", "apple", winner); 476 assertEquals("", Status.provisional, winningStatus); 477 478 resolver.clear(); 479 resolver.setBaileyValue("bailey"); 480 resolver.setLocale(CLDRLocale.getInstance("de"), null); 481 resolver.setBaseline("foo", oldStatus); 482 resolver.add("zebra", toVoterId("googleV")); 483 resolver.add("zebra", toVoterId("googleS")); 484 resolver.add("apple", toVoterId("appleV")); 485 486 // check that total votes over alphabetical 487 winner = resolver.getWinningValue(); 488 winningStatus = resolver.getWinningStatus(); 489 assertEquals("", "zebra", winner); 490 assertEquals("", Status.provisional, winningStatus); 491 } 492 TestVoteDowngrade()493 public void TestVoteDowngrade() { 494 VoteResolver<String> resolver = new VoteResolver<>(getTestVoterInfoList()); 495 496 Status oldStatus = Status.unconfirmed; 497 498 resolver.setBaileyValue("bailey"); 499 resolver.setLocale(CLDRLocale.getInstance("mt"), null); 500 resolver.setBaseline("foo", oldStatus); 501 resolver.add("aardvark", toVoterId("adobeE")); 502 resolver.add("zebra", toVoterId("ibmT")); 503 assertEquals("", "zebra", resolver.getWinningValue()); // TC vote of 20 504 // beats 505 // expert's 8 506 assertEquals("", Status.approved, resolver.getWinningStatus()); 507 508 resolver.clear(); 509 resolver.setBaileyValue("bailey"); 510 resolver.setLocale(CLDRLocale.getInstance("mt"), null); 511 resolver.setBaseline("foo", oldStatus); 512 resolver.add("aardvark", toVoterId("adobeE")); 513 resolver.add("zebra", toVoterId("ibmT")); 514 resolver.add("aardvark", toVoterId("ibmE")); 515 assertEquals("", "zebra", resolver.getWinningValue()); // TC vote of 20 516 // beats 517 // manager's 4 518 // and its own 519 // manager's 4 520 assertEquals("", Status.approved, resolver.getWinningStatus()); 521 522 resolver.clear(); 523 resolver.setBaileyValue("bailey"); 524 resolver.setLocale(CLDRLocale.getInstance("mt"), null); 525 resolver.setBaseline("foo", oldStatus); 526 resolver.add("aardvark", toVoterId("adobeE")); 527 resolver.add("zebra", toVoterId("ibmT"), Level.vetter.getVotes(Organization.ibm)); // NOTE: 528 // reduced 529 // votes: 530 // as 531 // vetter. 532 resolver.add("aardvark", toVoterId("ibmE")); 533 assertEquals("", "aardvark", resolver.getWinningValue()); // Now 534 // aardvark 535 // wins - 536 // managers 537 // win out as provisional 538 assertEquals("", Status.provisional, resolver.getWinningStatus()); 539 540 resolver.clear(); 541 resolver.setBaileyValue("bailey"); 542 resolver.setLocale(CLDRLocale.getInstance("mt"), null); 543 resolver.setBaseline("foo", oldStatus); 544 resolver.add("aardvark", toVoterId("adobeE")); 545 resolver.add("zebra", toVoterId("ibmT"), Level.vetter.getVotes(Organization.ibm)); // NOTE: 546 // reduced 547 // votes: 548 // as 549 // vetter. 550 assertEquals("", "aardvark", resolver.getWinningValue()); // Now 551 // aardvark 552 // wins - 553 // managers 554 // win out. 555 assertEquals("", Status.provisional, resolver.getWinningStatus()); 556 } 557 TestResolvedVoteCounts()558 public void TestResolvedVoteCounts() { 559 VoteResolver<String> resolver = new VoteResolver<>(getTestVoterInfoList()); 560 561 Status oldStatus = Status.unconfirmed; 562 563 resolver.setBaileyValue("bailey"); 564 resolver.setLocale(CLDRLocale.getInstance("de"), null); 565 resolver.setBaseline("foo", oldStatus); 566 resolver.add("zebra", toVoterId("googleV")); 567 resolver.add("apple", toVoterId("appleV")); 568 569 // check that alphabetical wins when votes are equal 570 Map<String, Long> counts = resolver.getResolvedVoteCountsIncludingIntraOrgDisputes(); 571 logln(counts.toString()); 572 assertEquals("", "foo", new ArrayList<>(counts.keySet()).get(2)); 573 574 resolver.clear(); 575 resolver.setBaileyValue("bailey"); 576 resolver.setLocale(CLDRLocale.getInstance("de"), null); 577 resolver.setBaseline("foo", Status.approved); 578 resolver.add("zebra", toVoterId("googleV")); 579 resolver.add("apple", toVoterId("appleV")); 580 counts = resolver.getResolvedVoteCountsIncludingIntraOrgDisputes(); 581 logln(counts.toString()); 582 assertEquals("", "foo", new ArrayList<>(counts.keySet()).get(0)); 583 584 resolver.clear(); 585 resolver.setBaileyValue("bailey"); 586 resolver.setLocale(CLDRLocale.getInstance("de"), null); 587 resolver.setBaseline("foo", Status.approved); 588 resolver.add("zebra", toVoterId("googleS")); 589 counts = resolver.getResolvedVoteCountsIncludingIntraOrgDisputes(); 590 logln(counts.toString()); 591 assertEquals("", "foo", new ArrayList<>(counts.keySet()).get(0)); 592 } 593 verifyRequiredVotes( VoteResolver<String> resolver, String locale, String xpath, Status baselineStatus, int required)594 private void verifyRequiredVotes( 595 VoteResolver<String> resolver, 596 String locale, 597 String xpath, 598 Status baselineStatus, 599 int required) { 600 StringBuilder sb = new StringBuilder(); 601 sb.append("Locale: " + locale); 602 resolver.clear(); 603 resolver.setBaileyValue("bailey"); 604 resolver.setBaseline("foo", baselineStatus); 605 PathHeader ph = null; 606 if (xpath != null) { 607 sb.append(" XPath: " + xpath); 608 ph = PathHeader.getFactory(testInfo.getEnglish()).fromPath(xpath); 609 } 610 resolver.setLocale(CLDRLocale.getInstance(locale), ph); 611 if (!assertEquals( 612 locale + " verifyRequiredVotes: " + ph.toString(), 613 required, 614 resolver.getRequiredVotes())) { 615 int debug = 0; 616 } 617 } 618 TestRequiredVotes()619 public void TestRequiredVotes() { 620 VoteResolver<String> resolver = new VoteResolver<>(getTestVoterInfoList()); 621 verifyRequiredVotes( 622 resolver, 623 "mt", 624 "//ldml/localeDisplayNames/languages/language[@type=\"fr_CA\"]", 625 Status.missing, 626 ONE_VETTER_BAR); 627 verifyRequiredVotes( 628 resolver, 629 "fr", 630 "//ldml/localeDisplayNames/languages/language[@type=\"fr_CA\"]", 631 Status.provisional, 632 TWO_VETTER_BAR); 633 verifyRequiredVotes( 634 resolver, 635 "es", 636 "//ldml/numbers/symbols[@numberSystem=\"latn\"]/group", 637 Status.approved, 638 VoteResolver.HIGH_BAR); 639 verifyRequiredVotes( 640 resolver, 641 "es", 642 "//ldml/numbers/symbols[@numberSystem=\"latn\"]/decimal", 643 Status.approved, 644 VoteResolver.HIGH_BAR); 645 verifyRequiredVotes( 646 resolver, 647 "hi", 648 "//ldml/numbers/symbols[@numberSystem=\"deva\"]/decimal", 649 Status.approved, 650 VoteResolver.HIGH_BAR); 651 verifyRequiredVotes( 652 resolver, 653 "hi", 654 "//ldml/numbers/symbols[@numberSystem=\"deva\"]/group", 655 Status.approved, 656 VoteResolver.HIGH_BAR); 657 verifyRequiredVotes( 658 resolver, 659 "ast", 660 "//ldml/numbers/symbols[@numberSystem=\"latn\"]/decimal", 661 Status.approved, 662 ONE_VETTER_BAR); 663 verifyRequiredVotes( 664 resolver, 665 "mt", 666 "//ldml/characters/exemplarCharacters", 667 Status.approved, 668 VoteResolver.HIGH_BAR); 669 verifyRequiredVotes( 670 resolver, 671 "mt", 672 "//ldml/characters/exemplarCharacters", 673 Status.approved, 674 VoteResolver.HIGH_BAR); 675 verifyRequiredVotes( 676 resolver, 677 "mt", 678 "//ldml/characters/exemplarCharacters[@type=\"auxiliary\"]", 679 Status.approved, 680 VoteResolver.HIGH_BAR); 681 verifyRequiredVotes( 682 resolver, 683 "mt", 684 "//ldml/characters/exemplarCharacters[@type=\"numbers\"]", 685 Status.approved, 686 VoteResolver.HIGH_BAR); 687 verifyRequiredVotes( 688 resolver, 689 "mt", 690 "//ldml/characters/exemplarCharacters[@type=\"punctuation\"]", 691 Status.approved, 692 VoteResolver.HIGH_BAR); 693 verifyRequiredVotes( 694 resolver, 695 "mt", 696 "//ldml/characters/exemplarCharacters[@type=\"index\"]", 697 Status.approved, 698 VoteResolver.HIGH_BAR); 699 verifyRequiredVotes( 700 resolver, 701 "es", 702 "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/days/dayContext[@type=\"format\"]/dayWidth[@type=\"wide\"]/day[@type=\"sun\"]", 703 Status.approved, 704 VoteResolver.HIGH_BAR); 705 verifyRequiredVotes( 706 resolver, 707 "ast", 708 "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/days/dayContext[@type=\"format\"]/dayWidth[@type=\"wide\"]/day[@type=\"sun\"]", 709 Status.approved, 710 ONE_VETTER_BAR); 711 verifyRequiredVotes( 712 resolver, 713 "es", 714 "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/months/monthContext[@type=\"format\"]/monthWidth[@type=\"wide\"]/month[@type=\"1\"]", 715 Status.provisional, 716 VoteResolver.LOWER_BAR); 717 verifyRequiredVotes( 718 resolver, 719 "ast", 720 "//ldml/dates/calendars/calendar[@type=\"gregorian\"]/months/monthContext[@type=\"format\"]/monthWidth[@type=\"wide\"]/month[@type=\"1\"]", 721 Status.approved, 722 ONE_VETTER_BAR); 723 } 724 725 /** 726 * In sublocales, for a typical path, the required votes should be 4, except for a few specified 727 * locales. 728 */ TestSublocaleRequiredVotes()729 public void TestSublocaleRequiredVotes() { 730 final Set<String> eightVoteSublocales = 731 new HashSet<>( 732 Arrays.asList( 733 "pt_PT", 734 "zh_Hant", 735 "zh_Hant_HK", 736 "en_AU", 737 "en_GB", 738 "es_MX", 739 "fr_CA", 740 "es_419")); 741 final VoteResolver<String> resolver = new VoteResolver<>(getTestVoterInfoList()); 742 final String path = "//ldml/annotations/annotation[@cp=\"\"][@type=\"tts\"]"; 743 for (String locale : SubmissionLocales.CLDR_OR_HIGH_LEVEL_LOCALES) { 744 if (locale.contains("_")) { 745 int expectedRequiredVotes = 746 eightVoteSublocales.contains(locale) ? TWO_VETTER_BAR : ONE_VETTER_BAR; 747 verifyRequiredVotes(resolver, locale, path, Status.approved, expectedRequiredVotes); 748 } 749 } 750 } 751 TestVoteResolver()752 public void TestVoteResolver() { 753 // to make it easier to debug failures, the first digit is an org, 754 // second is the individual in that org, and 755 // third is the voting weight. 756 VoteResolver<String> resolver = new VoteResolver<>(getTestVoterInfoList()); 757 String[] tests = { 758 "bailey=BAILEY", 759 "comment=regression case from John Emmons", 760 "locale=wae", 761 "oldValue=2802", 762 "oldStatus=approved", 763 "304=208027", // Apple vetter 764 // expected values 765 "value=208027", 766 "status=approved", 767 "sameVotes=208027", 768 "conflicts=[]", 769 "check", 770 // first test 771 "oldValue=old-value", 772 "oldStatus=provisional", 773 "comment=Check that identical values get the top overall vote, and that org is maxed (eg vetter + guest = vetter)", 774 "404=next", 775 "411=next", 776 "304=best", 777 // expected values 778 "value=next", 779 "sameVotes=next,best", 780 "conflicts=[]", 781 "status=provisional", 782 "check", 783 "comment=now give next a slight edge (5 to 4) with a different organization", 784 "404=next", 785 "304=best", 786 "801=next", 787 // expected values 788 "value=next", 789 "sameVotes=next", 790 "status=approved", 791 "check", 792 "comment=set up a case of conflict within organization", 793 "404=next", 794 "424=best", 795 // expected values 796 "value=best", // alphabetical 797 "sameVotes=best", 798 "conflicts=[google]", 799 "status=approved", 800 "check", 801 "comment=now cross-organizational conflict, also check for max value in same organization (4, 1) => 4 not 5", 802 "404=next", 803 "424=best", 804 "411=best", 805 "304=best", 806 // expected values 807 "conflicts=[google]", 808 "value=best", 809 "sameVotes=best", 810 "status=approved", 811 "check", 812 "comment=now clear winner 8 over 4", 813 "404=next", 814 // "424=best", 815 "411=best", 816 "304=best", 817 "204=primo", 818 "114=primo", 819 // expected values 820 "conflicts=[]", 821 "value=primo", 822 "sameVotes=primo", 823 "status=approved", 824 "check", 825 "comment=now not so clear, throw in a guest value. So it is 8 to 5. (used to be provisional)", 826 "404=next", 827 // "424=best", 828 "411=best", 829 "304=best", 830 "204=primo", 831 "114=primo", 832 "101=best", 833 // expected values 834 "conflicts=[]", 835 "value=primo", 836 "status=approved", 837 "check", 838 "comment=set up vote of 4 in established locale, with old provisional value", 839 "locale=fr", 840 "404=best", 841 "oldStatus=provisional", 842 // expected values 843 "value=best", 844 "sameVotes=best", 845 "status=contributed", 846 "conflicts=[]", 847 "check", 848 "comment=now set up vote of 4 in established locale, but with old contributed value", 849 "oldStatus=contributed", 850 // expected values 851 "value=old-value", 852 "sameVotes=old-value", 853 "status=contributed", 854 "conflicts=[]", 855 "check", 856 "comment=now set up vote of 1 + 1 in established locale, and with old contributed value", 857 "411=best", 858 "101=best", 859 "oldStatus=contributed", 860 // expected values 861 "value=best", 862 "sameVotes=best", 863 "status=contributed", 864 "conflicts=[]", 865 "check", 866 }; 867 String expectedValue = null; 868 String expectedConflicts = null; 869 Status expectedStatus = null; 870 String oldValue = null; 871 Status oldStatus = null; 872 String baileyValue = null; 873 List<String> sameVotes = null; 874 String locale = null; 875 Map<Integer, String> values = new TreeMap<>(); 876 int counter = -1; 877 878 for (String test : tests) { 879 String[] item = test.split("="); 880 String name = item[0]; 881 String value = item.length < 2 ? null : item[1]; 882 if (name.equalsIgnoreCase("comment")) { 883 logln("#\t" + value); 884 // System.out.println("#\t" + value); 885 if (DEBUG_COMMENT != null && value.contains(DEBUG_COMMENT)) { 886 int x = 0; 887 } 888 } else if (name.equalsIgnoreCase("locale")) { 889 locale = value; 890 } else if (name.equalsIgnoreCase("bailey")) { 891 baileyValue = value; 892 } else if (name.equalsIgnoreCase("oldValue")) { 893 oldValue = value; 894 } else if (name.equalsIgnoreCase("oldStatus")) { 895 oldStatus = Status.valueOf(value); 896 } else if (name.equalsIgnoreCase("value")) { 897 expectedValue = value; 898 } else if (name.equalsIgnoreCase("sameVotes")) { 899 sameVotes = 900 value == null ? new ArrayList<>(0) : Arrays.asList(value.split(",\\s*")); 901 } else if (name.equalsIgnoreCase("status")) { 902 expectedStatus = Status.valueOf(value); 903 } else if (name.equalsIgnoreCase("conflicts")) { 904 expectedConflicts = value; 905 } else if (DIGITS.containsAll(name)) { 906 final int voter = Integer.parseInt(name); 907 if (value == null || value.equals("null")) { 908 values.remove(voter); 909 } else { 910 values.put(voter, value); 911 } 912 } else if (name.equalsIgnoreCase("check")) { 913 counter++; 914 // load the resolver 915 resolver.setBaileyValue(baileyValue); 916 resolver.setLocale(CLDRLocale.getInstance(locale), null); 917 resolver.setBaseline(oldValue, oldStatus); 918 for (int voter : values.keySet()) { 919 resolver.add(values.get(voter), voter); 920 } 921 // print the contents 922 logln(counter + "\t" + values); 923 logln(resolver.toString()); 924 // now print the values 925 assertEquals(counter + " value", expectedValue, resolver.getWinningValue()); 926 assertEquals( 927 counter + " sameVotes", 928 sameVotes.toString(), 929 resolver.getValuesWithSameVotes().toString()); 930 assertEquals(counter + " status", expectedStatus, resolver.getWinningStatus()); 931 assertEquals( 932 counter + " conflicts", 933 expectedConflicts, 934 resolver.getConflictedOrganizations().toString()); 935 resolver.clear(); 936 resolver.setBaileyValue("bailey"); 937 values.clear(); 938 } else { 939 errln("unknown command:\t" + test); 940 } 941 } 942 } 943 944 void assertSpecialLocale(String loc, SpecialLocales.Type type) { 945 assertEquals( 946 "SpecialLocales type for " + loc, 947 type, 948 SpecialLocales.getType(CLDRLocale.getInstance(loc))); 949 } 950 951 public void TestSpecialLocales() { 952 assertSpecialLocale("sr", null); 953 assertSpecialLocale("ha_NE", SpecialLocales.Type.algorithmic); 954 assertSpecialLocale("sr_Latn", SpecialLocales.Type.algorithmic); 955 assertSpecialLocale("sr_Latn_BA", SpecialLocales.Type.algorithmic); 956 assertSpecialLocale("yue_Hans", null); // not readonly, because it is not policy DISCARD 957 assertSpecialLocale("en", SpecialLocales.Type.readonly); 958 assertSpecialLocale("en_ZZ_PROGRAMMERESE", null); // not defined 959 assertSpecialLocale(LocaleNames.UND, null); 960 assertSpecialLocale(LocaleNames.MUL, SpecialLocales.Type.scratch); 961 assertSpecialLocale("mul_ZZ", SpecialLocales.Type.scratch); 962 assertSpecialLocale("und_001", null); // not defined 963 964 CLDRLocale sr_Latn = CLDRLocale.getInstance("sr_Latn"); 965 CLDRLocale sr_Latn_BA = CLDRLocale.getInstance("sr_Latn_BA"); 966 logln("sr_Latn raw comment = " + SpecialLocales.getCommentRaw(sr_Latn)); 967 assertTrue( 968 "sr_Latn raw contains @ sign", SpecialLocales.getCommentRaw(sr_Latn).contains("@")); 969 970 logln("sr_Latn comment = " + SpecialLocales.getComment(sr_Latn)); 971 assertTrue( 972 "sr_Latn comment does NOT contain @ sign", 973 !SpecialLocales.getComment(sr_Latn).contains("@")); 974 logln("sr_Latn_BA raw comment = " + SpecialLocales.getCommentRaw(sr_Latn_BA)); 975 assertTrue( 976 "sr_Latn_BA raw contains '@sr_Latn_BA'", 977 SpecialLocales.getCommentRaw(sr_Latn_BA).contains("@sr_Latn_BA")); 978 } 979 980 public void TestCLDRURLS() { 981 final String KOREAN_LANGUAGE = "//ldml/localeDisplayNames/languages/language[@type=\"ko\"]"; 982 final String KOREAN_LANGUAGE_STRID = "821c2a2fc5c206d"; 983 final CLDRLocale maltese = CLDRLocale.getInstance("mt"); 984 assertEquals( 985 "base", "https://st.unicode.org/cldr-apps", CLDRConfig.getInstance().urls().base()); 986 assertEquals( 987 "locales list", 988 "https://st.unicode.org/cldr-apps/v#locales///", 989 CLDRConfig.getInstance().urls().forSpecial(CLDRURLS.Special.Locales)); 990 assertEquals( 991 "maltese", 992 "https://st.unicode.org/cldr-apps/v#/mt//", 993 CLDRConfig.getInstance().urls().forLocale(maltese)); 994 assertEquals( 995 "korean in maltese", 996 "https://st.unicode.org/cldr-apps/v#/mt//" + KOREAN_LANGUAGE_STRID, 997 CLDRConfig.getInstance().urls().forXpath(maltese, KOREAN_LANGUAGE)); 998 assertEquals( 999 "korean in maltese via stringid", 1000 "https://st.unicode.org/cldr-apps/v#/mt//" + KOREAN_LANGUAGE_STRID, 1001 CLDRConfig.getInstance().urls().forXpathHexId(maltese, KOREAN_LANGUAGE_STRID)); 1002 assertEquals( 1003 "south east asia in maltese", 1004 "https://st.unicode.org/cldr-apps/v#/mt/C_SEAsia/", 1005 CLDRConfig.getInstance().urls().forPage(maltese, PageId.C_SEAsia)); 1006 try { 1007 String ret = CLDRConfig.getInstance().urls().forXpathHexId(maltese, KOREAN_LANGUAGE); 1008 errln("Error- expected forXpathHexId to choke on an xpath but got " + ret); 1009 } catch (IllegalArgumentException iae) { 1010 logln("GOOD: forXpathHexId Caught expected " + iae); 1011 } 1012 try { 1013 String ret = CLDRConfig.getInstance().urls().forXpath(maltese, KOREAN_LANGUAGE_STRID); 1014 errln("Error- expected forXpath to choke on a hexid but got " + ret); 1015 } catch (IllegalArgumentException iae) { 1016 logln("GOOD: forXpath Caught expected " + iae); 1017 } 1018 1019 assertEquals( 1020 "korean in maltese - absoluteUrl", 1021 "https://st.unicode.org/cldr-apps/v#/mt//" + KOREAN_LANGUAGE_STRID, 1022 CLDRConfig.getInstance().absoluteUrls().forXpath(maltese, KOREAN_LANGUAGE)); 1023 } 1024 1025 static final UnicodeMap<String> SCRIPTS = 1026 ICUPropertyFactory.make().getProperty("script").getUnicodeMap_internal(); 1027 static final UnicodeMap<String> GC = 1028 ICUPropertyFactory.make().getProperty("general_category").getUnicodeMap_internal(); 1029 1030 public void TestUnicodeMapCompose() { 1031 logln("Getting Scripts"); 1032 1033 UnicodeMap.Composer<String> composer = 1034 new UnicodeMap.Composer<>() { 1035 @Override 1036 public String compose(int codepoint, String string, String a, String b) { 1037 return a.toString() + "_" + b.toString(); 1038 } 1039 }; 1040 1041 logln("Trying Compose"); 1042 1043 UnicodeMap<String> composed = 1044 ((UnicodeMap) SCRIPTS.cloneAsThawed()).composeWith(GC, composer); 1045 String last = ""; 1046 for (int i = 0; i < 0x10FFFF; ++i) { 1047 String comp = composed.getValue(i); 1048 String gc = GC.getValue(i); 1049 String sc = SCRIPTS.getValue(i); 1050 if (!comp.equals(composer.compose(i, null, sc, gc))) { 1051 errln("Failed compose at: " + i); 1052 break; 1053 } 1054 if (!last.equals(comp)) { 1055 logln(Utility.hex(i) + "\t" + comp); 1056 last = comp; 1057 } 1058 } 1059 } 1060 1061 private static final int SET_LIMIT = 0x10FFFF; 1062 private static final int CHECK_LIMIT = 0xFFFF; 1063 private static final NumberFormat pf = NumberFormat.getPercentInstance(); 1064 private static final NumberFormat nf = NumberFormat.getInstance(); 1065 1066 public void TestUnicodeMapTime() { 1067 boolean shortTest = getInclusion() < 10; 1068 double hashTime, umTime, icuTime, treeTime; 1069 int warmup = shortTest ? 1 : 20; 1070 umTime = checkUnicodeMapSetTime(warmup, 0); 1071 hashTime = checkUnicodeMapSetTime(warmup, 1); 1072 logln("Percentage: " + pf.format(hashTime / umTime)); 1073 treeTime = checkUnicodeMapSetTime(warmup, 3); 1074 logln("Percentage: " + pf.format(treeTime / umTime)); 1075 1076 if (shortTest) { 1077 return; 1078 } 1079 1080 umTime = checkUnicodeMapGetTime(1000, 0); 1081 hashTime = checkUnicodeMapGetTime(1000, 1); 1082 logln("Percentage: " + pf.format(hashTime / umTime)); 1083 icuTime = checkUnicodeMapGetTime(1000, 2); 1084 logln("Percentage: " + pf.format(icuTime / umTime)); 1085 treeTime = checkUnicodeMapGetTime(1000, 3); 1086 logln("Percentage: " + pf.format(treeTime / umTime)); 1087 } 1088 1089 private static final int propEnum = UProperty.GENERAL_CATEGORY; 1090 1091 private double checkUnicodeMapSetTime(int iterations, int type) { 1092 _checkUnicodeMapSetTime(1, type); 1093 double result = _checkUnicodeMapSetTime(iterations, type); 1094 logln( 1095 (type == 0 ? "UnicodeMap" : type == 1 ? "HashMap" : type == 2 ? "ICU" : "TreeMap") 1096 + "\t" 1097 + nf.format(result)); 1098 return result; 1099 } 1100 1101 private double _checkUnicodeMapSetTime(int iterations, int type) { 1102 UnicodeMap<String> map1 = SCRIPTS; 1103 Map<Integer, String> map2 = map1.putAllCodepointsInto(new HashMap<Integer, String>()); 1104 Map<Integer, String> map3 = new TreeMap<>(map2); 1105 System.gc(); 1106 double start = System.currentTimeMillis(); 1107 for (int j = 0; j < iterations; ++j) 1108 for (int cp = 0; cp <= SET_LIMIT; ++cp) { 1109 int enumValue = UCharacter.getIntPropertyValue(cp, propEnum); 1110 if (enumValue <= 0) continue; // for smaller set 1111 String value = 1112 UCharacter.getPropertyValueName( 1113 propEnum, enumValue, UProperty.NameChoice.LONG); 1114 switch (type) { 1115 case 0: 1116 map1.put(cp, value); 1117 break; 1118 case 1: 1119 map2.put(cp, value); 1120 break; 1121 case 3: 1122 map3.put(cp, value); 1123 break; 1124 } 1125 } 1126 double end = System.currentTimeMillis(); 1127 return (end - start) / 1000 / iterations; 1128 } 1129 1130 private double checkUnicodeMapGetTime(int iterations, int type) { 1131 UnicodeMap<String> map1 = new UnicodeMap<>(); 1132 Map<Integer, String> map2 = map1.putAllCodepointsInto(new HashMap<Integer, String>()); 1133 Map<Integer, String> map3 = new TreeMap<>(); 1134 _checkUnicodeMapGetTime(map1, map2, map3, 1, type); // warmup 1135 double result = _checkUnicodeMapGetTime(map1, map2, map3, iterations, type); 1136 logln( 1137 (type == 0 ? "UnicodeMap" : type == 1 ? "HashMap" : type == 2 ? "ICU" : "TreeMap") 1138 + "\t" 1139 + nf.format(result)); 1140 return result; 1141 } 1142 1143 private double _checkUnicodeMapGetTime( 1144 UnicodeMap<String> map1, 1145 Map<Integer, String> map2, 1146 Map<Integer, String> map3, 1147 int iterations, 1148 int type) { 1149 System.gc(); 1150 double start = System.currentTimeMillis(); 1151 for (int j = 0; j < iterations; ++j) 1152 for (int cp = 0; cp < CHECK_LIMIT; ++cp) { 1153 switch (type) { 1154 case 0: 1155 map1.getValue(cp); 1156 break; 1157 case 1: 1158 map2.get(cp); 1159 break; 1160 case 2: 1161 int enumValue = UCharacter.getIntPropertyValue(cp, propEnum); 1162 UCharacter.getPropertyValueName( 1163 propEnum, enumValue, UProperty.NameChoice.LONG); 1164 break; 1165 case 3: 1166 map3.get(cp); 1167 break; 1168 } 1169 } 1170 double end = System.currentTimeMillis(); 1171 return (end - start) / 1000 / iterations; 1172 } 1173 1174 public void TestStevenTest() { 1175 VoteResolver<String> resolver = new VoteResolver<>(getTestVoterInfoList()); 1176 1177 String tests[] = { 1178 "bailey=BAILEY", 1179 "comment=Steven Loomis test case tweaked by Parthinator", 1180 "locale=wae", 1181 "oldValue=_", 1182 "oldStatus=approved", 1183 "304=test", // Apple vetter 1184 // expected values 1185 "value=test", 1186 "status=approved", 1187 "sameVotes=test", 1188 "conflicts=[]", 1189 "check", 1190 1191 // test1 1192 "comment=timestamp case1", 1193 "locale=de", 1194 "oldValue=old-value", 1195 "oldStatus=provisional", 1196 "404=Foo", 1197 "424=Bar", 1198 // expected 1199 "value=Bar", 1200 "status=provisional", 1201 "sameVotes=Bar, test", 1202 "conflicts=[google]", 1203 "check", 1204 1205 // test2 1206 "comment=timestamp case2", 1207 "locale=de", 1208 "oldValue=Bar", 1209 "oldStatus=provisional", 1210 "424=Foo", 1211 "404=Bar", 1212 // expected values 1213 "value=Bar", 1214 "status=provisional", 1215 "sameVotes=Bar, test", 1216 "conflicts=[google]", 1217 "check", 1218 1219 // test 3 1220 "comment=timestamp unaffiliated case", 1221 "locale=de", 1222 "oldValue=_", 1223 "oldStatus=unconfirmed", 1224 // # // G vetter A 1225 // timestamp=1 1226 "801=Foo", 1227 // timestamp=2 1228 "802=Bar", 1229 // expected values 1230 "value=Bar", 1231 "status=contributed", 1232 "sameVotes=Bar", 1233 "conflicts=[google, unaffiliated]", 1234 "check", 1235 }; 1236 1237 String expectedValue = null; 1238 String expectedConflicts = null; 1239 Status expectedStatus = null; 1240 String oldValue = null; 1241 Status oldStatus = null; 1242 String baileyValue = null; 1243 List<String> sameVotes = null; 1244 String locale = null; 1245 int voteEntries = 0; 1246 Map<Integer, String> values = new TreeMap<>(); 1247 Map<Integer, VoteEntries> valuesMap = new TreeMap<>(); 1248 1249 int counter = -1; 1250 1251 for (String test : tests) { 1252 String[] item = test.split("="); 1253 String name = item[0]; 1254 String value = item.length < 2 ? null : item[1]; 1255 if (name.equalsIgnoreCase("comment")) { 1256 logln("#\t" + value); 1257 // System.out.println("#\t" + value); 1258 if (DEBUG_COMMENT != null && value.contains(DEBUG_COMMENT)) { 1259 int x = 0; 1260 } 1261 } else if (name.equalsIgnoreCase("locale")) { 1262 locale = value; 1263 } else if (name.equalsIgnoreCase("oldValue")) { 1264 oldValue = value; 1265 } else if (name.equalsIgnoreCase("oldStatus")) { 1266 oldStatus = Status.valueOf(value); 1267 } else if (name.equalsIgnoreCase("value")) { 1268 expectedValue = value; 1269 } else if (name.equalsIgnoreCase("bailey")) { 1270 baileyValue = value; 1271 } else if (name.equalsIgnoreCase("sameVotes")) { 1272 sameVotes = 1273 value == null ? new ArrayList<>(0) : Arrays.asList(value.split(",\\s*")); 1274 } else if (name.equalsIgnoreCase("status")) { 1275 expectedStatus = Status.valueOf(value); 1276 } else if (name.equalsIgnoreCase("conflicts")) { 1277 expectedConflicts = value; 1278 } else if (DIGITS.containsAll(name)) { 1279 final int voter = Integer.parseInt(name); 1280 if (value == null || value.equals("null")) { 1281 values.remove(voter); 1282 for (Map.Entry<Integer, VoteEntries> entry : valuesMap.entrySet()) { 1283 if (entry.getValue().getVoter() == voter) { 1284 valuesMap.remove(entry.getKey()); 1285 } 1286 } 1287 } else { 1288 values.put(voter, value); 1289 valuesMap.put(++voteEntries, new VoteEntries(voter, value)); 1290 } 1291 } else if (name.equalsIgnoreCase("check")) { 1292 counter++; 1293 // load the resolver 1294 resolver.setBaileyValue(baileyValue); 1295 resolver.setLocale(CLDRLocale.getInstance(locale), null); 1296 resolver.setBaseline(oldValue, oldStatus); 1297 for (int voteEntry : valuesMap.keySet()) { 1298 1299 resolver.add( 1300 valuesMap.get(voteEntry).getValue(), 1301 valuesMap.get(voteEntry).getVoter()); 1302 } 1303 // print the contents 1304 logln(counter + "\t" + values); 1305 logln(resolver.toString()); 1306 // now print the values 1307 assertEquals(counter + " value", expectedValue, resolver.getWinningValue()); 1308 assertEquals( 1309 counter + " sameVotes", 1310 sameVotes.toString(), 1311 resolver.getValuesWithSameVotes().toString()); 1312 assertEquals(counter + " status", expectedStatus, resolver.getWinningStatus()); 1313 assertEquals( 1314 counter + " conflicts", 1315 expectedConflicts, 1316 resolver.getConflictedOrganizations().toString()); 1317 resolver.clear(); 1318 values.clear(); 1319 } else { 1320 errln("unknown command:\t" + test); 1321 } 1322 } 1323 } 1324 1325 public void testBaileyVotes() { 1326 VoterInfoList vil = new VoterInfoList().setVoterToInfo(TestUser.TEST_USERS); 1327 VoteResolver<String> resolver = new VoteResolver<>(vil); 1328 CLDRLocale locale = CLDRLocale.getInstance("de"); 1329 PathHeader path = null; 1330 1331 /* 1332 * Simple case, all = bailey -- should get INHERITANCE_MARKER now that we have "dropped hard inheritance" 1333 */ 1334 resolver.setLocale(locale, path); 1335 resolver.setBaileyValue("bailey"); 1336 resolver.setBaseline("foo", Status.approved); 1337 1338 resolver.add("bailey", TestUser.appleV.voterId); 1339 resolver.add("bailey", TestUser.microsoftV.voterId); 1340 resolver.add("bailey", TestUser.googleV.voterId); 1341 if (VoteResolver.DROP_HARD_INHERITANCE) { 1342 assertEquals( 1343 "Simple case, all = bailey", 1344 CldrUtility.INHERITANCE_MARKER, 1345 resolver.getWinningValue()); 1346 } else { 1347 assertEquals("Simple case, all = bailey", "bailey", resolver.getWinningValue()); 1348 } 1349 1350 /* 1351 * Another simple case, all = INHERITANCE_MARKER 1352 * Added per https://unicode.org/cldr/trac/ticket/11299 1353 */ 1354 resolver.clear(); 1355 resolver.setLocale(locale, path); 1356 resolver.setBaileyValue("bailey"); 1357 resolver.setBaseline("foo", Status.approved); 1358 1359 resolver.add(CldrUtility.INHERITANCE_MARKER, TestUser.appleV.voterId); 1360 resolver.add(CldrUtility.INHERITANCE_MARKER, TestUser.microsoftV.voterId); 1361 resolver.add(CldrUtility.INHERITANCE_MARKER, TestUser.googleV.voterId); 1362 assertEquals( 1363 "Another simple case, all = INHERITANCE_MARKER", 1364 CldrUtility.INHERITANCE_MARKER, 1365 resolver.getWinningValue()); 1366 1367 /* 1368 * INHERITANCE_MARKER should win here, having more votes than bailey. 1369 * Changed per https://unicode.org/cldr/trac/ticket/11299 1370 */ 1371 resolver.clear(); 1372 resolver.setLocale(locale, path); 1373 resolver.setBaileyValue("bailey"); 1374 resolver.setBaseline("foo", Status.approved); 1375 1376 resolver.add("bailey", TestUser.appleV.voterId); 1377 resolver.add(CldrUtility.INHERITANCE_MARKER, TestUser.microsoftV.voterId); 1378 resolver.add(CldrUtility.INHERITANCE_MARKER, TestUser.googleV.voterId); 1379 assertEquals( 1380 "The bailey value and explicit value combine to win", 1381 CldrUtility.INHERITANCE_MARKER, 1382 resolver.getWinningValue()); 1383 1384 /* 1385 * INHERITANCE_MARKER should win here, having equal number of votes with bailey; 1386 * first they combine to win over other-vote. 1387 * Changed per https://unicode.org/cldr/trac/ticket/11299 1388 */ 1389 resolver.clear(); 1390 resolver.setLocale(locale, path); 1391 resolver.setBaileyValue("bailey"); 1392 resolver.setBaseline("foo", Status.approved); 1393 1394 resolver.add("bailey", TestUser.appleV.voterId); 1395 resolver.add(CldrUtility.INHERITANCE_MARKER, TestUser.microsoftV.voterId); 1396 resolver.add("other-vote", TestUser.googleV.voterId); 1397 assertEquals( 1398 "The bailey value and explicit value combine to win again", 1399 CldrUtility.INHERITANCE_MARKER, 1400 resolver.getWinningValue()); 1401 1402 /* 1403 * Split vote, no action 1404 */ 1405 resolver.clear(); 1406 resolver.setLocale(locale, path); 1407 resolver.setBaileyValue("bailey"); 1408 resolver.setBaseline("foo", Status.approved); 1409 1410 resolver.add("bailey", TestUser.appleV.voterId); 1411 resolver.add("not-bailey", TestUser.microsoftV.voterId); 1412 resolver.add("other-vote", TestUser.googleV.voterId); 1413 assertEquals("Split vote, no action", "foo", resolver.getWinningValue()); 1414 1415 /* 1416 * Bailey should lose even if it has MORE votes than INHERITANCE_MARKER, now that we 1417 * have "dropped hard inheritance" 1418 */ 1419 resolver.clear(); 1420 resolver.setLocale(locale, path); 1421 resolver.setBaileyValue("bailey"); 1422 resolver.setBaseline("foo", Status.approved); 1423 1424 resolver.add("bailey", TestUser.googleV.voterId); 1425 resolver.add("bailey", TestUser.appleV.voterId); 1426 resolver.add(CldrUtility.INHERITANCE_MARKER, TestUser.microsoftV.voterId); 1427 resolver.add("other-vote", TestUser.adobeV.voterId); 1428 resolver.add("other-vote", TestUser.gnomeV.voterId); 1429 if (VoteResolver.DROP_HARD_INHERITANCE) { 1430 assertEquals( 1431 "Bailey never beats INHERITANCE_MARKER", 1432 CldrUtility.INHERITANCE_MARKER, 1433 resolver.getWinningValue()); 1434 } else { 1435 assertEquals( 1436 "Bailey can beat INHERITANCE_MARKER if not dropped", 1437 "bailey", 1438 resolver.getWinningValue()); 1439 } 1440 } 1441 1442 /** Test XMLUploader.writeBulkInfoHtml */ 1443 public void TestBulkUploadHtml() { 1444 StringWriter out = new StringWriter(); 1445 final String bulkStage = "submit"; 1446 try { 1447 XMLUploader.writeBulkInfoHtml(bulkStage, out); 1448 } catch (Exception e) { 1449 errln("Exception for writeBulkInfoHtml in TestBulkUploadHtml: " + e); 1450 } 1451 final String expected = 1452 "<div class='bulkNextInfo'>\n<ul>\n<li class='header'>Bulk Upload:</li>\n" 1453 + "<li class='inactive'>\n<h1>1. upload</h1>\n<h2>Upload XML file</h2>\n</li>\n" 1454 + "<li class='inactive'>\n<h1>2. check</h1>\n<h2>Verify valid XML</h2>\n</li>\n" 1455 + "<li class='inactive'>\n<h1>3. test</h1>\n<h2>Test for CLDR errors</h2>\n</li>\n" 1456 + "<li class='active'>\n<h1>4. submit</h1>\n<h2>Data submitted into SurveyTool</h2>\n</li>\n" 1457 + "</ul>\n</div>\n"; 1458 assertEquals("writeBulkInfoHtml", expected, out.toString()); 1459 } 1460 1461 /** 1462 * Verify that VettingViewer.getMissingStatus returns MissingStatus.PRESENT for a typical path 1463 * in a well-populated locale 1464 * 1465 * <p>Ideally we should also test for MissingStatus.DISPUTED, etc.; that's more difficult 1466 */ 1467 public void TestMissingStatus() { 1468 final String path = 1469 "//ldml/units/unitLength[@type=\"short\"]/unit[@type=\"volume-cup\"]/displayName"; 1470 final String locale = "fr"; 1471 final CLDRFile cldrFile = testInfo.getCLDRFile(locale, true); 1472 final MissingStatus expected = MissingStatus.PRESENT; 1473 // Note: VettingViewer.getMissingStatus reports PRESENT for items with ↑↑↑ and absent if the 1474 // item 1475 // is removed to inherit from root, even though the value obtained is the same in either 1476 // case; 1477 // so for path pick an item that does not have ↑↑↑, otherwise when that item is stripped for 1478 // production data the test will fail. 1479 final MissingStatus status = 1480 VettingViewer.getMissingStatus(cldrFile, path, true /* latin */); 1481 if (status != expected) { 1482 errln( 1483 "Got getMissingStatus = " 1484 + status.toString() 1485 + "; expected " 1486 + expected.toString()); 1487 } 1488 } 1489 1490 /** Check that expected paths are Aliased, and have debugging code */ 1491 public void TestMissingGrammar() { 1492 // https://cldr-smoke.unicode.org/cldr-apps/v#/hu/Length/a4915bf505ffb49 1493 final String path = 1494 "//ldml/units/unitLength[@type=\"long\"]/unit[@type=\"length-meter\"]/unitPattern[@count=\"one\"][@case=\"accusative\"]"; 1495 checkGrammarCoverage( 1496 "hr", 1497 path, 1498 MissingStatus.PRESENT, 1499 DEBUG, 1500 1, 1501 0, 1502 0, 1503 0, 1504 0); // this isn't a very good test, since we have to adjust each time. Should create 1505 // fake cldr data instead 1506 checkGrammarCoverage("kw", path, MissingStatus.ABSENT, false, 0, 0, 1, 1, 0); 1507 checkGrammarCoverage("en_NZ", path, MissingStatus.ALIASED, DEBUG, 1, 0, 0, 0, 0); 1508 } 1509 1510 /** 1511 * Check the getMissingStatus and getStatus. Note that the values may need to be adjusted in 1512 * successive versions. The sizes are expected sizes. 1513 * 1514 * @param locale 1515 * @param path 1516 * @param statusExpected 1517 * @param debug TODO 1518 */ 1519 public void checkGrammarCoverage( 1520 final String locale, 1521 final String path, 1522 MissingStatus statusExpected, 1523 boolean debug, 1524 int... sizes) { 1525 final CLDRFile cldrFile = testInfo.getCLDRFile(locale, true); 1526 final MissingStatus expected = statusExpected; 1527 final MissingStatus status = 1528 VettingViewer.getMissingStatus(cldrFile, path, true /* latin */); 1529 if (status != expected) { 1530 errln( 1531 locale 1532 + " got getMissingStatus = " 1533 + status.toString() 1534 + "; expected " 1535 + expected.toString()); 1536 } 1537 Iterable<String> pathsToTest = Collections.singleton(path); 1538 Counter<org.unicode.cldr.util.Level> foundCounter = new Counter<>(); 1539 Counter<org.unicode.cldr.util.Level> unconfirmedCounter = new Counter<>(); 1540 Counter<org.unicode.cldr.util.Level> missingCounter = new Counter<>(); 1541 Relation<MissingStatus, String> missingPaths = 1542 new Relation( 1543 new TreeMap<MissingStatus, String>(), TreeSet.class, Ordering.natural()); 1544 Set<String> unconfirmedPaths = new TreeSet<>(); 1545 VettingViewer.getStatus( 1546 pathsToTest, 1547 cldrFile, 1548 PathHeader.getFactory(), 1549 foundCounter, 1550 unconfirmedCounter, 1551 missingCounter, 1552 missingPaths, 1553 unconfirmedPaths); 1554 assertEquals(locale + " foundCounter (0)", sizes[0], foundCounter.getTotal()); 1555 assertEquals(locale + " unconfirmedCounter (1)", sizes[1], unconfirmedCounter.getTotal()); 1556 assertEquals(locale + " missingCounter (2)", sizes[2], missingCounter.getTotal()); 1557 assertEquals(locale + " missingPaths (3)", sizes[3], missingPaths.size()); 1558 assertEquals(locale + " unconfirmedPaths (4)", sizes[4], unconfirmedPaths.size()); 1559 showStatusResults( 1560 locale, 1561 foundCounter, 1562 unconfirmedCounter, 1563 missingCounter, 1564 missingPaths, 1565 unconfirmedPaths); 1566 if (debug) { 1567 foundCounter.clear(); 1568 unconfirmedCounter.clear(); 1569 missingCounter.clear(); 1570 missingPaths.clear(); 1571 unconfirmedPaths.clear(); 1572 pathsToTest = cldrFile.fullIterable(); 1573 VettingViewer.getStatus( 1574 pathsToTest, 1575 cldrFile, 1576 PathHeader.getFactory(), 1577 foundCounter, 1578 unconfirmedCounter, 1579 missingCounter, 1580 missingPaths, 1581 unconfirmedPaths); 1582 showStatusResults( 1583 locale, 1584 foundCounter, 1585 unconfirmedCounter, 1586 missingCounter, 1587 missingPaths, 1588 unconfirmedPaths); 1589 } 1590 } 1591 1592 public void showStatusResults( 1593 final String locale, 1594 Counter<org.unicode.cldr.util.Level> foundCounter, 1595 Counter<org.unicode.cldr.util.Level> unconfirmedCounter, 1596 Counter<org.unicode.cldr.util.Level> missingCounter, 1597 Relation<MissingStatus, String> missingPaths, 1598 Set<String> unconfirmedPaths) { 1599 warnln( 1600 "\n" 1601 + locale 1602 + " foundCounter:\t" 1603 + foundCounter 1604 + "\n" 1605 + locale 1606 + " unconfirmedCounter:\t" 1607 + unconfirmedCounter 1608 + "\n" 1609 + locale 1610 + " missingCounter:\t" 1611 + missingCounter 1612 + "\n" 1613 + locale 1614 + " unconfirmedPaths:\t" 1615 + unconfirmedPaths 1616 + "\n" 1617 + locale 1618 + " missing paths (modern):"); 1619 int count = 0; 1620 for (Entry<MissingStatus, String> entry : missingPaths.entrySet()) { 1621 final MissingStatus missingStatus = entry.getKey(); 1622 final String missingPath = entry.getValue(); 1623 warnln( 1624 ++count 1625 + "\t" 1626 + locale 1627 + "\t" 1628 + missingStatus 1629 + "\t" 1630 + missingPath 1631 + "\t" 1632 + SUPPLEMENTAL_DATA_INFO.getCoverageLevel(missingPath, locale)); 1633 } 1634 } 1635 1636 /** 1637 * Test the function VoteResolver.Level.canCreateOrSetLevelTo() 1638 * 1639 * <p>Compare org.unicode.cldr.unittest.web.TestUserRegistry.TestCanSetUserLevel() 1640 */ 1641 public void TestCanCreateOrSetLevelTo() { 1642 if (Level.vetter.canCreateOrSetLevelTo(Level.guest) 1643 || Level.anonymous.canCreateOrSetLevelTo(Level.guest) 1644 || Level.guest.canCreateOrSetLevelTo(Level.locked) 1645 || Level.locked.canCreateOrSetLevelTo(Level.locked)) { 1646 errln("Only managers and above can change levels at all"); 1647 } 1648 if (Level.manager.canCreateOrSetLevelTo(Level.tc) 1649 || Level.manager.canCreateOrSetLevelTo(Level.admin) 1650 || Level.tc.canCreateOrSetLevelTo(Level.admin)) { 1651 errln("Can’t change anyone to a more privileged level than you"); 1652 } 1653 } 1654 } 1655