content: package rabbitescape.engine.menu;
  
  import static rabbitescape.engine.menu.FakeLevelsList.*;
  import static rabbitescape.engine.util.Util.*;
  
  import static org.hamcrest.CoreMatchers.*;
  import static org.hamcrest.MatcherAssert.*;
  
  import java.util.ArrayList;
  import java.util.List;
  
  import org.hamcrest.BaseMatcher;
  import org.hamcrest.Description;
  import org.hamcrest.Matcher;
  import org.junit.Test;
  
  import rabbitescape.engine.config.Config;
  import rabbitescape.engine.config.TestConfig;
  
  import static rabbitescape.engine.menu.ByNameConfigBasedLevelsCompleted.canonicalName;
  
  public class TestByNameConfigBasedLevelsCompleted
  {
      @Test
      public void Canonical_name_of_simple_string_is_the_string()
      {
          assertThat( "abcxyz", isEqualToItsCanonicalVersion() );
          assertThat( "c",      isEqualToItsCanonicalVersion() );
          assertThat( "zya",    isEqualToItsCanonicalVersion() );
          assertThat( "1z2a3",  isEqualToItsCanonicalVersion() );
      }
  
      @Test( expected = ByNameConfigBasedLevelsCompleted.EmptyLevelName.class )
      public void Empty_name_is_not_allowed()
      {
          canonicalName( "" );
      }
  
      @Test
      public void Spaces_become_underscores()
      {
          assertThat( canonicalName( "abc xyz" ), equalTo( "abc_xyz" ) );
          assertThat( canonicalName( " a " ), equalTo( "_a_" ) );
      }
  
      @Test
      public void Punctuation_becomes_underscores()
      {
          assertThat( canonicalName( "abc,xyz?" ), equalTo( "abc_xyz_" ) );
      }
  
      @Test
      public void Upper_case_becomes_lower_case()
      {
          assertThat( canonicalName( "AbcxYZ" ), equalTo( "abcxyz" ) );
      }
  
      @Test
      public void Unicode_becomes_underscore()
      {
          assertThat(
              canonicalName( "Pile of poo \uD83D\uDCA9 is coo" ),
              equalTo( "pile_of_poo___is_coo" )
          );
  
          assertThat(
              canonicalName( "Go to \u5317\u4eac\u5e02" ),
              equalTo( "go_to____" )
          );
      }
  
      @Test
      public void Report_highest_level_from_config_where_some_completed()
      {
          FakeConfig fakeConfig = new FakeConfig(
              "level_01_foo_1",
              "level_01_foo_2",
              "level_01_foo_3"
          );
  
          LevelsList levelsList = new LevelsList(
              levelSet( "01_foo", 10 ),
              levelSet( "02_bar", 10 )
          );
  
          ByNameConfigBasedLevelsCompleted lc =
              new ByNameConfigBasedLevelsCompleted( fakeConfig, levelsList );
  
          assertThat( lc.highestLevelCompleted( "01_foo" ), equalTo( 3 ) );
      }
  
      @Test
      public void Report_highest_level_from_config_where_none_completed()
      {
          FakeConfig fakeConfig = new FakeConfig(
              "level_01_foo_1",
              "level_01_foo_2",
              "level_01_foo_3"
          );
  
          LevelsList levelsList = new LevelsList(
              levelSet( "01_foo", 10 ),
              levelSet( "02_bar", 10 )
          );
  
          ByNameConfigBasedLevelsCompleted lc =
              new ByNameConfigBasedLevelsCompleted( fakeConfig, levelsList );
  
          assertThat( lc.highestLevelCompleted( "02_bar" ), equalTo( 0 ) );
      }
  
      @Test
      public void Report_highest_level_from_config_where_all_completed()
      {
          FakeConfig fakeConfig = new FakeConfig(
              "level_foo_1",
              "level_foo_2",
              "level_foo_3",
              "level_foo_4",
              "level_foo_5",
              "level_foo_6",
              "level_foo_7",
              "level_foo_8",
              "level_foo_9",
              "level_foo_10"
          );
  
          LevelsList levelsList = new LevelsList(
              levelSet( "foo", 10 ),
              levelSet( "bar", 10 )
          );
  
          ByNameConfigBasedLevelsCompleted lc =
              new ByNameConfigBasedLevelsCompleted( fakeConfig, levelsList );
  
          assertThat( lc.highestLevelCompleted( "foo" ), equalTo( 10 ) );
      }
  
      @Test
      public void Save_changes_to_config_new_dir()
      {
          FakeConfig fakeConfig = new FakeConfig(
              "level_foo_1",
              "level_foo_2",
              "level_foo_3"
          );
  
          LevelsList levelsList = new LevelsList(
              levelSet( "foo", 10 ),
              levelSet( "bar", 10 )
          );
  
          ByNameConfigBasedLevelsCompleted lc =
              new ByNameConfigBasedLevelsCompleted( fakeConfig, levelsList );
  
          lc.setCompletedLevel( "bar", 1 );
  
          // We called set with the right config key and value
          assertThat(
              fakeConfig.log.get( 1 ),  // 0 was a get
              equalTo(
                  "set levels.completed [" +
                  "\"level_bar_1\"," +
                  "\"level_foo_1\"," +
                  "\"level_foo_2\"," +
                  "\"level_foo_3\"" +
                  "]"
              )
          );
          assertThat(
              fakeConfig.log.get( 2 ),
              equalTo( "save" )
          );
          assertThat( fakeConfig.log.size(), equalTo( 3 ) );
      }
  
      @Test
      public void Save_changes_to_config_existing_dir()
      {
          FakeConfig fakeConfig = new FakeConfig(
              "level_foo_1",
              "level_foo_2",
              "level_foo_3"
          );
  
          LevelsList levelsList = new LevelsList(
              levelSet( "foo", 10 ),
              levelSet( "bar", 10 )
          );
  
          ByNameConfigBasedLevelsCompleted lc =
              new ByNameConfigBasedLevelsCompleted( fakeConfig, levelsList );
  
          lc.setCompletedLevel( "foo", 4 );
  
          assertThat(
              fakeConfig.log.get( 1 ),  // 0 was a get
              equalTo(
                  "set levels.completed [" +
                  "\"level_foo_1\"," +
                  "\"level_foo_2\"," +
                  "\"level_foo_3\"," +
                  "\"level_foo_4\"" +
                  "]"
              )
          );
          assertThat(
              fakeConfig.log.get( 2 ),
              equalTo( "save" )
          );
          assertThat( fakeConfig.log.size(), equalTo( 3 ) );
      }
  
      @Test
      public void No_need_to_update_if_weve_already_completed_a_level()
      {
          FakeConfig fakeConfig = new FakeConfig(
              "level_foo_1",
              "level_foo_2",
              "level_foo_3"
          );
  
          LevelsList levelsList = new LevelsList(
              levelSet( "foo", 10 ),
              levelSet( "bar", 10 )
          );
  
          ByNameConfigBasedLevelsCompleted lc =
              new ByNameConfigBasedLevelsCompleted( fakeConfig, levelsList );
  
          // Two useless calls
          lc.setCompletedLevel( "foo", 3 );
          lc.setCompletedLevel( "foo", 1 );
  
          // Just gets - nothing saved
          assertThat(
              fakeConfig.log.get( 0 ),
              equalTo( "get levels.completed" )
          );
          assertThat(
              fakeConfig.log.get( 1 ),
              equalTo( "get levels.completed" )
          );
          assertThat( fakeConfig.log.size(), equalTo( 2 ) );
      }
  
      // ---
  
      private static class FakeConfig extends Config
      {
          private final String getAnswer;
          public final List<String> log;
  
          public FakeConfig( String... completedLevelNames )
          {
              super( null, new TestConfig.EmptyConfigStorage() );
  
              this.getAnswer = makeAnswer( completedLevelNames );
              this.log = new ArrayList<String>();
          }
  
          private String makeAnswer( String[] completedLevelNames )
          {
              return "["
                  + join( ",", map( quoted(), completedLevelNames ) )
                  + "]";
          }
  
          private static Function<String, String> quoted()
          {
              return new Function<String, String>()
              {
                  @Override
                  public String apply( String t )
                  {
                      return "\"" + t + "\"";
                  }
              };
          }
  
          @Override
          public void set( String key, String value )
          {
              log.add( "set " + key + " " + value );
          }
  
          @Override
          public String get( String key )
          {
              log.add( "get " + key );
              return getAnswer;
          }
  
          @Override
          public void save()
          {
              log.add( "save" );
          }
      }
  
      // ---
  
      private static Matcher<String> isEqualToItsCanonicalVersion()
      {
          return new BaseMatcher<String>()
          {
              String str;
  
              @Override
              public boolean matches( Object obj )
              {
                  if ( !( obj instanceof String ) )
                  {
                      return false;
                  }
  
                  str = (String)obj;
  
                  return str.equals( canonicalName( str ) );
              }
  
              @Override
              public void describeTo( Description description )
              {
                  description.appendText( canonicalName( str ) );
              }
          };
      }
  
  
  }
  