content: package rabbitescape.render;
  
  import static org.hamcrest.MatcherAssert.*;
  import static org.hamcrest.CoreMatchers.*;
  import static rabbitescape.engine.Tools.*;
  
  import java.util.ArrayList;
  import java.util.List;
  
  import rabbitescape.render.androidlike.Bitmap;
  
  import org.junit.Test;
  
  public class TestBitmapCache
  {
      @Test
      public void Cache_and_scale_calls_load_if_not_loaded_before()
      {
          TrackingBitmapLoader loader = new TrackingBitmapLoader();
          BitmapCache<FakeBitmap> cache = new BitmapCache<>( loader, null, 5 );
  
          // This is what we are testing - get from the cache and load a bitmap
          cache.get( "a/b/foo.png", 32 );
  
          // Load was called once
          assertThat(
              loader.loadCalls,
              equalToList( new String[] { "a/b/foo.png" } )
          );
      }
  
      @Test
      public void Getting_a_bitmap_from_the_cache_returns_the_same_object()
      {
          TrackingBitmapLoader loader = new TrackingBitmapLoader();
          BitmapCache<FakeBitmap> cache = new BitmapCache<>( loader, null, 5 );
  
          FakeBitmap gotBitmap1 = cache.get( "a/b/foo01.png", 32 );
          FakeBitmap gotBitmap2 = cache.get( "a/b/foo02.png", 32 );
  
          // Sanity
          assertThat( gotBitmap1, not( sameInstance( gotBitmap2 ) ) );
  
          // This is what we are testing - get same instance when call again
          assertThat(
              gotBitmap1,
              sameInstance( cache.get( "a/b/foo01.png", 32 ) )
          );
  
          assertThat(
              gotBitmap1,
              sameInstance( cache.get( "a/b/foo01.png", 32 ) )
          );
  
          assertThat(
              gotBitmap2,
              sameInstance( cache.get( "a/b/foo02.png", 32 ) )
          );
  
          assertThat(
              gotBitmap2,
              sameInstance( cache.get( "a/b/foo02.png", 32 ) )
          );
      }
  
      @Test
      public void Load_only_called_once_even_if_get_called_multiple_times()
      {
          TrackingBitmapLoader loader = new TrackingBitmapLoader();
          TrackingBitmapScaler scaler = new TrackingBitmapScaler();
          BitmapCache<FakeBitmap> cache = new BitmapCache<>( loader, scaler, 5 );
  
          // This is what we are testing - call get 3 times
          FakeBitmap bitmap = cache.get( "a/b/foo.png", 30 );
          cache.get( "a/b/foo.png", 30 );
          cache.get( "a/b/foo.png", 30 );
  
          // Load was still only called once
          assertThat(
              loader.loadCalls,
              equalToList( new String[] { "a/b/foo.png" } )
          );
  
          // Scale was only called once
          assertThat( scaler.scaleCalls.size(), equalTo( 1 ) );
  
          // The bitmap was not recycled
          assertThat( bitmap.recycled, is( false ) );
      }
  
      @Test
      public void Scale_is_not_called_when_we_ask_for_the_loaded_size()
      {
          TrackingBitmapLoader loader = new TrackingBitmapLoader();
          TrackingBitmapScaler scaler = new TrackingBitmapScaler();
          BitmapCache<FakeBitmap> cache = new BitmapCache<>( loader, scaler, 5 );
  
          // This is what we are testing - get it and scale 3 times to
          // the same size as the loader uses.
          cache.get( "a/b/foo.png", 32 );
          cache.get( "a/b/foo.png", 32 );
          cache.get( "a/b/foo.png", 32 );
  
          // Scale was only called once
          assertThat( scaler.scaleCalls.size(), equalTo( 0 ) );
      }
  
      @Test
      public void Scale_is_only_called_the_first_time_we_ask_for_a_size()
      {
          TrackingBitmapLoader loader = new TrackingBitmapLoader();
          TrackingBitmapScaler scaler = new TrackingBitmapScaler();
          BitmapCache<FakeBitmap> cache = new BitmapCache<>( loader, scaler, 5 );
  
          // This is what we are testing - get it and scale 3 times to
          // the same size, but not the default loader size
          cache.get( "a/b/foo.png", 19 );
  
          // Scale was called
          assertThat( scaler.scaleCalls.size(), equalTo( 1 ) );
  
          cache.get( "a/b/foo.png", 19 );
          cache.get( "a/b/foo.png", 19 );
  
          // Scale was not called again
          assertThat( scaler.scaleCalls.size(), equalTo( 1 ) );
      }
  
      @Test
      public void Recently_accessed_items_are_not_purged_before_older()
      {
          TrackingBitmapLoader loader = new TrackingBitmapLoader();
          BitmapCache<FakeBitmap> cache = new BitmapCache<>( loader, null, 5 );
  
          // Get enough to fill the cache
          FakeBitmap b1 = cache.get( "a/b/foo01.png", 32 );
          FakeBitmap b2 = cache.get( "a/b/foo02.png", 32 );
          FakeBitmap b3 = cache.get( "a/b/foo03.png", 32 );
          FakeBitmap b4 = cache.get( "a/b/foo04.png", 32 );
          FakeBitmap b5 = cache.get( "a/b/foo05.png", 32 );
  
          // This is what we are testing: re-access foo02 and it should avoid
          // being purged
          cache.get( "a/b/foo02.png", 32 );
  
          // Sanity
          assertThat( loader.loadCalls.size(), equalTo( 5 ) );
  
          // Get 3 new things - causing 3 things to be purged
          cache.get( "a/b/foo06.png", 32 );
          cache.get( "a/b/foo07.png", 32 );
          cache.get( "a/b/foo08.png", 32 );
  
          assertThat( b1.recycled, is( true ) );
          assertThat( b2.recycled, is( false ) );
          assertThat( b3.recycled, is( true ) );
          assertThat( b4.recycled, is( true ) );
          assertThat( b5.recycled, is( false ) );
  
          // Sanity
          assertThat( loader.loadCalls.size(), equalTo( 8 ) );
  
          // Get foo02 - should not need to re-load it
          cache.get( "a/b/foo02.png", 32 );
  
          // Load was not called again
          assertThat( loader.loadCalls.size(), equalTo( 8 ) );
  
          // Get foo01 - for this we should need to re-load it
          cache.get( "a/b/foo01.png", 32 );
  
          // Load was called
          assertThat( loader.loadCalls.size(), equalTo( 9 ) );
      }
  
      @Test
      public void Bitmaps_are_purged_in_order_when_cache_is_full()
      {
          TrackingBitmapLoader loader = new TrackingBitmapLoader();
          BitmapCache<FakeBitmap> cache = new BitmapCache<>( loader, null, 5 );
  
          // Get enough to fill the cache
          FakeBitmap b1 = cache.get( "a/b/foo01.png", 32 );
          FakeBitmap b2 = cache.get( "a/b/foo02.png", 32 );
          FakeBitmap b3 = cache.get( "a/b/foo03.png", 32 );
          FakeBitmap b4 = cache.get( "a/b/foo04.png", 32 );
          FakeBitmap b5 = cache.get( "a/b/foo05.png", 32 );
  
          // Not recycled yet
          assertThat( b1.recycled, is( false ) );
  
          // This is what we are testing: get another and the first bitmap
          // should get purged
          cache.get( "a/b/foo06.png", 32 );
  
          // Sanity
          assertThat( loader.loadCalls.size(), equalTo( 6 ) );
          assertThat( loader.loadCalls.get( 5 ), equalTo( "a/b/foo06.png" ) );
  
          // The first was recycled, others weren't
          assertThat( b1.recycled, is( true ) );
          assertThat( b2.recycled, is( false ) );
          assertThat( b3.recycled, is( false ) );
          assertThat( b4.recycled, is( false ) );
          assertThat( b5.recycled, is( false ) );
  
          // Try and get the first - it should have to be reloaded
          cache.get( "a/b/foo01.png", 32 );
  
          // Load was called again for foo01
          assertThat( loader.loadCalls.size(), equalTo( 7 ) );
          assertThat( loader.loadCalls.get( 6 ), equalTo( "a/b/foo01.png" ) );
      }
  
      // --
  
      private static class FakeBitmap implements Bitmap
      {
          public boolean recycled = false;
  
          @Override
          public String name()
          {
              return "fake";
          }
  
          @Override
          public int width()
          {
              return 32;
          }
  
          @Override
          public int height()
          {
              return 32;
          }
  
          @Override
          public void recycle()
          {
              assertThat( recycled, is( false ) );
  
              recycled = true;
          }
  
          @Override
          public long getByteCount()
          {
              return 1;
          }
      }
  
      private static class TrackingBitmapLoader
          implements BitmapLoader<FakeBitmap>
      {
          public List<String> loadCalls = new ArrayList<String>();
  
          @Override
          public FakeBitmap load( String fileName, int tileSize )
          {
              loadCalls.add( fileName );
              return new FakeBitmap();
          }
  
          @Override
          public int sizeFor( int tileSize )
          {
              return 32;
          }
      }
  
      private static class TrackingBitmapScaler
      implements BitmapScaler<FakeBitmap>
      {
          public List<String> scaleCalls = new ArrayList<String>();
  
          @Override
          public FakeBitmap scale( FakeBitmap originalBitmap, double scale )
          {
              scaleCalls.add( String.valueOf( scale ) );
              return new FakeBitmap();
          }
      }
  }
  