Home | History | Annotate | Download | only in util
      1 //  2016 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html#License
      3 /**
      4  *******************************************************************************
      5  * Copyright (C) 2001-2013, International Business Machines Corporation and    *
      6  * others. All Rights Reserved.                                                *
      7  *******************************************************************************
      8  */
      9 package com.ibm.icu.dev.test.util;
     10 
     11 import java.text.Collator;
     12 import java.util.ArrayList;
     13 import java.util.Arrays;
     14 import java.util.Collection;
     15 import java.util.Comparator;
     16 import java.util.HashSet;
     17 import java.util.Iterator;
     18 import java.util.List;
     19 import java.util.Locale;
     20 import java.util.Map;
     21 import java.util.Map.Entry;
     22 import java.util.MissingResourceException;
     23 import java.util.Random;
     24 import java.util.Set;
     25 import java.util.SortedMap;
     26 
     27 import org.junit.Test;
     28 import org.junit.runner.RunWith;
     29 import org.junit.runners.JUnit4;
     30 
     31 import com.ibm.icu.dev.test.TestFmwk;
     32 import com.ibm.icu.impl.ICULocaleService;
     33 import com.ibm.icu.impl.ICUService;
     34 import com.ibm.icu.impl.ICUService.Factory;
     35 import com.ibm.icu.impl.ICUService.SimpleFactory;
     36 import com.ibm.icu.util.ULocale;
     37 
     38 @RunWith(JUnit4.class)
     39 public class ICUServiceThreadTest extends TestFmwk
     40 {
     41     private static final boolean PRINTSTATS = false;
     42 
     43     private static final String[] countries = {
     44         "ab", "bc", "cd", "de", "ef", "fg", "gh", "ji", "ij", "jk"
     45     };
     46     private static final String[] languages = {
     47         "", "ZY", "YX", "XW", "WV", "VU", "UT", "TS", "SR", "RQ", "QP"
     48     };
     49     private static final String[] variants = {
     50         "", "", "", "GOLD", "SILVER", "BRONZE"
     51     };
     52 
     53     private static class TestFactory extends SimpleFactory {
     54         TestFactory(String id) {
     55             super(new ULocale(id), id, true);
     56         }
     57 
     58         @Override
     59         public String getDisplayName(String idForDisplay, ULocale locale) {
     60             return (visible && idForDisplay.equals(this.id)) ? "(" + locale.toString() + ") " + idForDisplay : null;
     61         }
     62 
     63         @Override
     64         public String toString() {
     65             return "Factory_" + id;
     66         }
     67     }
     68     /**
     69      * Convenience override of getDisplayNames(ULocale, Comparator, String) that
     70      * uses the default collator for the locale as the comparator to
     71      * sort the display names, and null for the matchID.
     72      */
     73     public static SortedMap getDisplayNames(ICUService service, ULocale locale) {
     74         Collator col;
     75         try {
     76             col = Collator.getInstance(locale.toLocale());
     77         }
     78         catch (MissingResourceException e) {
     79             // if no collator resources, we can't collate
     80             col = null;
     81         }
     82         return service.getDisplayNames(locale, col, null);
     83     }
     84     private static final Random r = new Random(); // this is a multi thread test, can't 'unrandomize'
     85 
     86     private static String getCLV() {
     87         String c = countries[r.nextInt(countries.length)];
     88         String l = languages[r.nextInt(languages.length)];
     89         String v = variants[r.nextInt(variants.length)];
     90         return new Locale(c, l, v).toString();
     91     }
     92 
     93     private static boolean WAIT = true;
     94     private static boolean GO = false;
     95     private static long TIME = 5000;
     96 
     97     public static void runThreads() {
     98         runThreads(TIME);
     99     }
    100 
    101     public static void runThreads(long time) {
    102         try {
    103             GO = true;
    104             WAIT = false;
    105 
    106             Thread.sleep(time);
    107 
    108             WAIT = true;
    109             GO = false;
    110 
    111             Thread.sleep(300);
    112         }
    113         catch (InterruptedException e) {
    114         }
    115     }
    116 
    117     static class TestThread extends Thread {
    118         //private final String name;
    119         protected ICUService service;
    120         private final long delay;
    121 
    122         public TestThread(String name, ICUService service, long delay) {
    123             //this.name = name + " ";
    124             this.service = service;
    125             this.delay = delay;
    126             this.setDaemon(true);
    127         }
    128 
    129         @Override
    130         public void run() {
    131             while (WAIT) {
    132                 Thread.yield();
    133             }
    134 
    135             try {
    136                 while (GO) {
    137                     iterate();
    138                     if (delay > 0) {
    139                         Thread.sleep(delay);
    140                     }
    141                 }
    142             }
    143             catch (InterruptedException e) {
    144             }
    145         }
    146 
    147         protected void iterate() {
    148         }
    149 
    150         /*
    151           public boolean logging() {
    152           return log != null;
    153           }
    154 
    155           public void log(String msg) {
    156           if (logging()) {
    157           log.log(name + msg);
    158           }
    159           }
    160 
    161           public void logln(String msg) {
    162           if (logging()) {
    163           log.logln(name + msg);
    164           }
    165           }
    166 
    167           public void err(String msg) {
    168           if (logging()) {
    169           log.err(name + msg);
    170           }
    171           }
    172 
    173           public void errln(String msg) {
    174           if (logging()) {
    175           log.errln(name + msg);
    176           }
    177           }
    178 
    179           public void warn(String msg) {
    180           if (logging()) {
    181           log.info(name + msg);
    182           }
    183           }
    184 
    185           public void warnln(String msg) {
    186           if (logging()) {
    187           log.infoln(name + msg);
    188           }
    189           }
    190         */
    191     }
    192 
    193     static class RegisterFactoryThread extends TestThread {
    194         RegisterFactoryThread(String name, ICUService service, long delay) {
    195             super("REG " + name, service, delay);
    196         }
    197 
    198         @Override
    199         protected void iterate() {
    200             Factory f = new TestFactory(getCLV());
    201             service.registerFactory(f);
    202             TestFmwk.logln(f.toString());
    203         }
    204     }
    205 
    206     static class UnregisterFactoryThread extends TestThread {
    207         private Random r;
    208         List factories;
    209 
    210         UnregisterFactoryThread(String name, ICUService service, long delay) {
    211             super("UNREG " + name, service, delay);
    212 
    213             r = new Random();
    214             factories = service.factories();
    215         }
    216 
    217         @Override
    218         public void iterate() {
    219             int s = factories.size();
    220             if (s == 0) {
    221                 factories = service.factories();
    222             } else {
    223                 int n = r.nextInt(s);
    224                 Factory f = (Factory)factories.remove(n);
    225                 boolean success = service.unregisterFactory(f);
    226                 TestFmwk.logln("factory: " + f + (success ? " succeeded." : " *** failed."));
    227             }
    228         }
    229     }
    230 
    231     static class UnregisterFactoryListThread extends TestThread {
    232         Factory[] factories;
    233         int n;
    234 
    235         UnregisterFactoryListThread(String name, ICUService service, long delay, Factory[] factories) {
    236             super("UNREG " + name, service, delay);
    237 
    238             this.factories = factories;
    239         }
    240 
    241         @Override
    242         public void iterate() {
    243             if (n < factories.length) {
    244                 Factory f = factories[n++];
    245                 boolean success = service.unregisterFactory(f);
    246                 TestFmwk.logln("factory: " + f + (success ? " succeeded." : " *** failed."));
    247             }
    248         }
    249     }
    250 
    251 
    252     static class GetVisibleThread extends TestThread {
    253         GetVisibleThread(String name, ICUService service, long delay) {
    254             super("VIS " + name, service, delay);
    255         }
    256 
    257         @Override
    258         protected void iterate() {
    259             Set ids = service.getVisibleIDs();
    260             Iterator iter = ids.iterator();
    261             int n = 10;
    262             while (--n >= 0 && iter.hasNext()) {
    263                 String id = (String)iter.next();
    264                 Object result = service.get(id);
    265                 TestFmwk.logln("iter: " + n + " id: " + id + " result: " + result);
    266             }
    267         }
    268     }
    269 
    270     static class GetDisplayThread extends TestThread {
    271         ULocale locale;
    272 
    273         GetDisplayThread(String name, ICUService service, long delay, ULocale locale) {
    274             super("DIS " + name, service, delay);
    275 
    276             this.locale = locale;
    277         }
    278 
    279         @Override
    280         protected void iterate() {
    281             Map names = getDisplayNames(service,locale);
    282             Iterator iter = names.entrySet().iterator();
    283             int n = 10;
    284             while (--n >= 0 && iter.hasNext()) {
    285                 Entry e = (Entry)iter.next();
    286                 String dname = (String)e.getKey();
    287                 String id = (String)e.getValue();
    288                 Object result = service.get(id);
    289 
    290                 // Note: IllegalMonitorStateException is thrown by the code
    291                 // below on IBM JRE5 for AIX 64bit.  For some reason, converting
    292                 // int to String out of this statement resolves the issue.
    293 
    294                 String num = Integer.toString(n);
    295                 TestFmwk.logln(" iter: " + num +
    296                         " dname: " + dname +
    297                         " id: " + id +
    298                         " result: " + result);
    299             }
    300         }
    301     }
    302 
    303     static class GetThread extends TestThread {
    304         private String[] actualID;
    305 
    306         GetThread(String name, ICUService service, long delay) {
    307             super("GET " + name, service, delay);
    308 
    309             actualID = new String[1];
    310         }
    311 
    312         @Override
    313         protected void iterate() {
    314             String id = getCLV();
    315             Object o = service.get(id, actualID);
    316             if (o != null) {
    317                 TestFmwk.logln(" id: " + id + " actual: " + actualID[0] + " result: " + o);
    318             }
    319         }
    320     }
    321 
    322     static class GetListThread extends TestThread {
    323         private final String[] list;
    324         private int n;
    325 
    326         GetListThread(String name, ICUService service, long delay, String[] list) {
    327             super("GETL " + name, service, delay);
    328 
    329             this.list = list;
    330         }
    331 
    332         @Override
    333         protected void iterate() {
    334             if (--n < 0) {
    335                 n = list.length - 1;
    336             }
    337             String id = list[n];
    338             Object o = service.get(id);
    339             TestFmwk.logln(" id: " + id + " result: " + o);
    340         }
    341     }
    342 
    343     // return a collection of unique factories, might be fewer than requested
    344     Collection getFactoryCollection(int requested) {
    345         Set locales = new HashSet();
    346         for (int i = 0; i < requested; ++i) {
    347             locales.add(getCLV());
    348         }
    349         List factories = new ArrayList(locales.size());
    350         Iterator iter = locales.iterator();
    351         while (iter.hasNext()) {
    352             factories.add(new TestFactory((String)iter.next()));
    353         }
    354         return factories;
    355     }
    356 
    357     void registerFactories(ICUService service, Collection c) {
    358         Iterator iter = c.iterator();
    359         while (iter.hasNext()) {
    360             service.registerFactory((Factory)iter.next());
    361         }
    362     }
    363 
    364     ICUService stableService() {
    365         if (stableService == null) {
    366             stableService = new ICULocaleService();
    367             registerFactories(stableService, getFactoryCollection(50));
    368         }
    369         if (PRINTSTATS) stableService.stats();  // Enable the stats collection
    370         return stableService;
    371     }
    372     private ICUService stableService;
    373 
    374     // run multiple get on a stable service
    375     @Test
    376     public void Test00_ConcurrentGet() {
    377         for(int i = 0; i < 10; ++i) {
    378             new GetThread("[" + Integer.toString(i) + "]",  stableService(), 0).start();
    379         }
    380         runThreads();
    381         if (PRINTSTATS) System.out.println(stableService.stats());
    382     }
    383 
    384     // run multiple getVisibleID on a stable service
    385     @Test
    386     public void Test01_ConcurrentGetVisible() {
    387         for(int i = 0; i < 10; ++i) {
    388             new GetVisibleThread("[" + Integer.toString(i) + "]",  stableService(), 0).start();
    389         }
    390         runThreads();
    391         if (PRINTSTATS) System.out.println(stableService.stats());
    392     }
    393 
    394     // run multiple getDisplayName on a stable service
    395     @Test
    396     public void Test02_ConcurrentGetDisplay() {
    397         String[] localeNames = {
    398             "en", "es", "de", "fr", "zh", "it", "no", "sv"
    399         };
    400         for(int i = 0; i < localeNames.length; ++i) {
    401             String locale = localeNames[i];
    402             new GetDisplayThread("[" + locale + "]",
    403                                  stableService(),
    404                                  0,
    405                                  new ULocale(locale)).start();
    406         }
    407         runThreads();
    408         if (PRINTSTATS) System.out.println(stableService.stats());
    409     }
    410 
    411     // run register/unregister on a service
    412     @Test
    413     public void Test03_ConcurrentRegUnreg() {
    414         ICUService service = new ICULocaleService();
    415         if (PRINTSTATS) service.stats();    // Enable the stats collection
    416         for (int i = 0; i < 5; ++i) {
    417             new RegisterFactoryThread("[" + i + "]", service, 0).start();
    418         }
    419         for (int i = 0; i < 5; ++i) {
    420             new UnregisterFactoryThread("[" + i + "]", service, 0).start();
    421         }
    422         runThreads();
    423         if (PRINTSTATS) System.out.println(service.stats());
    424     }
    425 
    426     @Test
    427     public void Test04_WitheringService() {
    428         ICUService service = new ICULocaleService();
    429         if (PRINTSTATS) service.stats();    // Enable the stats collection
    430 
    431         Collection fc = getFactoryCollection(50);
    432         registerFactories(service, fc);
    433 
    434         Factory[] factories = (Factory[])fc.toArray(new Factory[fc.size()]);
    435         Comparator comp = new Comparator() {
    436                 @Override
    437                 public int compare(Object lhs, Object rhs) {
    438                     return lhs.toString().compareTo(rhs.toString());
    439                 }
    440             };
    441         Arrays.sort(factories, comp);
    442 
    443         new GetThread("", service, 0).start();
    444         new UnregisterFactoryListThread("", service, 3, factories).start();
    445 
    446         runThreads(2000);
    447         if (PRINTSTATS) System.out.println(service.stats());
    448     }
    449 
    450     // "all hell breaks loose"
    451     // one register and one unregister thread, delay 500ms
    452     // two display threads with different locales, delay 500ms;
    453     // one visible id thread, delay 50ms
    454     // fifteen get threads, delay 0
    455     // run for ten seconds
    456     @Test
    457     public void Test05_ConcurrentEverything() {
    458         ICUService service = new ICULocaleService();
    459         if (PRINTSTATS) service.stats();    // Enable the stats collection
    460 
    461         new RegisterFactoryThread("", service, 500).start();
    462 
    463         for(int i = 0; i < 15; ++i) {
    464             new GetThread("[" + Integer.toString(i) + "]", service, 0).start();
    465         }
    466 
    467         new GetVisibleThread("",  service, 50).start();
    468 
    469         String[] localeNames = {
    470             "en", "de"
    471         };
    472         for(int i = 0; i < localeNames.length; ++i) {
    473             String locale = localeNames[i];
    474             new GetDisplayThread("[" + locale + "]",
    475                                  stableService(),
    476                                  500,
    477                                  new ULocale(locale)).start();
    478         }
    479 
    480         new UnregisterFactoryThread("", service, 500).start();
    481 
    482         // yoweee!!!
    483         runThreads(9500);
    484         if (PRINTSTATS) System.out.println(service.stats());
    485     }
    486 }
    487