44
55package com .urbanairship .datacube ;
66
7+ import com .google .common .base .Optional ;
78import com .google .common .math .LongMath ;
9+ import com .urbanairship .datacube .idservices .CachingIdService ;
810import org .junit .Assert ;
11+ import org .junit .Test ;
912
13+ import java .io .IOException ;
1014import java .util .HashSet ;
1115import java .util .Set ;
1216import java .util .concurrent .TimeUnit ;
17+ import java .util .concurrent .atomic .AtomicInteger ;
1318
1419public class IdServiceTests {
1520 public static void basicTest (IdService idService ) throws Exception {
1621 final int numFieldBytes = 5 ;
17-
22+
1823 // Different inputs should always produce different outputs (non-repeating ids)
1924 Set <BoxedByteArray > idsSeen = new HashSet <BoxedByteArray >();
2025 for (int i =0 ; i <500 ; i ++) {
@@ -23,46 +28,102 @@ public static void basicTest(IdService idService) throws Exception {
2328 BoxedByteArray newBox = new BoxedByteArray (newId );
2429 Assert .assertTrue ("ID was repeated: " + newBox , idsSeen .add (newBox ));
2530 }
26-
31+
2732 // The same input should produce the same output
2833 byte [] id1 = idService .getOrCreateId (1 , Util .longToBytes (10 ), numFieldBytes );
2934 byte [] id2 = idService .getOrCreateId (1 , Util .longToBytes (10 ), numFieldBytes );
3035 Assert .assertEquals (numFieldBytes , id1 .length );
3136 Assert .assertArrayEquals (id1 , id2 );
3237 }
33-
38+
3439 /**
3540 * Generating 2^(fieldbits) unique IDs should work, then generating one more should raise an
3641 * exception because no more IDs were available.
37- *
42+ *
3843 */
39- public static void testExhaustion (IdService idService , int numFieldBytes , int dimensionNum )
44+ public static void testExhaustion (IdService idService , int numFieldBytes , int dimensionNum )
4045 throws Exception {
4146 int numFieldBits = numFieldBytes * 8 ;
42-
47+
4348 long numToGenerate = LongMath .pow (2 , numFieldBits );
4449 long i =0 ;
4550 for (; i <numToGenerate ; i ++) {
4651 byte [] id = idService .getOrCreateId (dimensionNum , Util .longToBytes (i ), numFieldBytes );
4752 Assert .assertEquals (numFieldBytes , id .length );
4853 }
49-
54+
5055 try {
5156 idService .getOrCreateId (dimensionNum , Util .longToBytes (i ), numFieldBytes );
5257 Assert .fail ("getOrCreateId call should have thrown an exception" );
5358 } catch (RuntimeException e ) {
5459 // Happy success
5560 }
56-
61+
5762 // Subsequent calls for the same input should fail quickly (and not block for long)
5863 long startTimeNanos = System .nanoTime ();
5964 try {
6065 idService .getOrCreateId (dimensionNum , Util .longToBytes (i ), numFieldBytes );
6166 Assert .fail ("ID allocation should have failed" );
62- } catch (RuntimeException e ) {
63- if (System .nanoTime () - startTimeNanos > TimeUnit .SECONDS .toNanos (5 )) {
67+ } catch (RuntimeException e ) {
68+ if (System .nanoTime () - startTimeNanos > TimeUnit .SECONDS .toNanos (5 )) {
6469 Assert .fail ("Took too long to fail" );
6570 }
6671 }
6772 }
73+
74+ @ Test
75+ public void testCacheMissingOff () throws IOException , InterruptedException {
76+ final CountingIdService wrappedIdService = new CountingIdService ();
77+ final CachingIdService lol = new CachingIdService (2 , wrappedIdService , "lol" , false );
78+ lol .getId (-1 , CountingIdService .unknown , 0 );
79+ lol .getId (-1 , CountingIdService .unknown , 0 );
80+ lol .getId (-1 , CountingIdService .known , 0 );
81+ lol .getId (-1 , CountingIdService .known , 0 );
82+ // should not have been cached, so accessed backing store twice
83+ Assert .assertEquals (2 , wrappedIdService .unKnownCount .get ());
84+ // should have been cached, so accessed backing store once
85+ Assert .assertEquals (1 , wrappedIdService .knownCount .get ());
86+ }
87+
88+ @ Test
89+ public void testCacheMissingOn () throws IOException , InterruptedException {
90+ final CountingIdService wrappedIdService = new CountingIdService ();
91+ final CachingIdService lol = new CachingIdService (2 , wrappedIdService , "lol" , true );
92+ lol .getId (-1 , CountingIdService .unknown , 0 );
93+ lol .getId (-1 , CountingIdService .unknown , 0 );
94+ lol .getId (-1 , CountingIdService .known , 0 );
95+ lol .getId (-1 , CountingIdService .known , 0 );
96+ // should have been cached, so accessed backing store once
97+ Assert .assertEquals (1 , wrappedIdService .unKnownCount .get ());
98+ // should have been cached, so accessed backing store once
99+ Assert .assertEquals (1 , wrappedIdService .knownCount .get ());
100+ }
101+
102+ private static final class CountingIdService implements IdService {
103+
104+ public static final byte [] known = "known" .getBytes ();
105+ public static final byte [] unknown = "unknown" .getBytes ();
106+
107+ public final AtomicInteger knownCount = new AtomicInteger (0 );
108+ public final AtomicInteger unKnownCount = new AtomicInteger (0 );
109+
110+ @ Override
111+ public byte [] getOrCreateId (int dimensionNum , byte [] input , int numIdBytes ) throws IOException , InterruptedException {
112+ throw new RuntimeException ("the feature this class tests doesn't make sense with getOrCreateId" );
113+ }
114+
115+ @ Override
116+ public Optional <byte []> getId (int dimensionNum , byte [] input , int numIdBytes ) throws IOException , InterruptedException {
117+ if (input == known ) {
118+ knownCount .incrementAndGet ();
119+ return Optional .of (known );
120+ } else if (input == unknown ) {
121+ unKnownCount .incrementAndGet ();
122+ return Optional .absent ();
123+ } else {
124+ throw new RuntimeException ("only use the two values for testing plz" );
125+ }
126+ }
127+
128+ }
68129}
0 commit comments