code: /*
  * Copyright 2014 Basis Technology Corp.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  * http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
  package com.basistech.tclre;
  
  import com.google.common.collect.Lists;
  import com.google.common.collect.Range;
  import org.junit.Test;
  import org.junit.runner.RunWith;
  import org.mockito.InOrder;
  import org.mockito.Mock;
  import org.mockito.Mockito;
  import org.mockito.invocation.InvocationOnMock;
  import org.mockito.junit.MockitoJUnitRunner;
  import org.mockito.stubbing.Answer;
  
  import java.util.List;
  import java.util.Map;
  
  import static org.junit.Assert.assertEquals;
  import static org.mockito.ArgumentMatchers.anyInt;
  import static org.mockito.ArgumentMatchers.any;
  import static org.mockito.ArgumentMatchers.anyShort;
  import static org.mockito.Mockito.doAnswer;
  import static org.mockito.Mockito.inOrder;
  import static org.mockito.Mockito.never;
  import static org.mockito.Mockito.times;
  import static org.mockito.Mockito.verify;
  import static org.mockito.Mockito.verifyNoMoreInteractions;
  import static org.mockito.Mockito.when;
  
  /**
   * Unit testing the color map.
   */
  @RunWith(MockitoJUnitRunner.class)
  public class ColorMapTest {
  
      boolean traceNewarc;
  
      @Mock
      Compiler compiler;
  
      @Mock
      Nfa nfa;
  
      private RuntimeColorMap runtime(ColorMap cm) {
          return new RuntimeColorMap(cm.getMap());
      }
  
      @Test
      public void testEmpty() throws Exception {
          ColorMap cm = new ColorMap(compiler);
          cm.okcolors(nfa);
          assertEquals(Constants.WHITE, runtime(cm).getcolor('a'));
          verify(compiler, never()).getNfa();
          verify(nfa, never()).newarc(anyInt(), anyShort(), any(State.class), any(State.class));
      }
  
      @Test
      public void testVerySimple() throws Exception {
          ColorMap cm = new ColorMap(compiler);
          cm.subcolor('a');
          cm.okcolors(nfa);
          assertEquals(1, cm.subcolor('a'));
          verify(compiler, never()).getNfa();
          verify(nfa, never()).newarc(anyInt(), anyShort(), any(State.class), any(State.class));
      }
  
      @Test
        public void testSimpleRange() throws Exception {
          when(compiler.getNfa()).thenReturn(nfa); // supply the NFA
          ColorMap cm = new ColorMap(compiler);
          State from = Mockito.mock(State.class);
          State to = Mockito.mock(State.class);
          cm.subrange('a', 'z', from, to);
  
          // we no longer make 26 identical arcs, we make one.
          verify(nfa, times(1)).newarc(Compiler.PLAIN, (short) 1, from, to);
  
          cm.okcolors(nfa);
  
          RuntimeColorMap runtime = runtime(cm);
          assertEquals(1, runtime.getcolor('a'));
          assertEquals(1, runtime.getcolor('z'));
          assertEquals(Constants.WHITE, runtime.getcolor('A'));
          verifyNoMoreInteractions(nfa);
      }
  
      @Test
      public void testSubout() throws Exception {
          when(compiler.getNfa()).thenReturn(nfa); // supply the NFA
          ColorMap cm = new ColorMap(compiler);
          State from = Mockito.mock(State.class);
          State to = Mockito.mock(State.class);
          cm.subrange('a', 'z', from, to);
          InOrder inOrder = inOrder(nfa);
  
          inOrder.verify(nfa, times(1)).newarc(Compiler.PLAIN, (short) 1, from, to);
          cm.okcolors(nfa);
  
          cm.subcolor('b');
          cm.okcolors(nfa);
          inOrder.verifyNoMoreInteractions(); // this does not result in more arcs?
  
          RuntimeColorMap runtime = runtime(cm);
          assertEquals(1, runtime.getcolor('a'));
          assertEquals(2, runtime.getcolor('b'));
          assertEquals(1, runtime.getcolor('z'));
          assertEquals(Constants.WHITE, runtime.getcolor('A'));
  
      }
  
      @Test
      public void testSubRange() throws Exception {
          when(compiler.getNfa()).thenReturn(nfa); // supply the NFA
          ColorMap cm = new ColorMap(compiler);
          State from = Mockito.mock(State.class);
          State to = Mockito.mock(State.class);
          cm.subrange('a', 'z', from, to);
          InOrder inOrder = inOrder(nfa);
  
          inOrder.verify(nfa, times(1)).newarc(Compiler.PLAIN, (short) 1, from, to);
          cm.okcolors(nfa);
  
          cm.subrange('b', 'c', from, to);
          cm.okcolors(nfa);
          inOrder.verify(nfa, times(1)).newarc(Compiler.PLAIN, (short) 2, from, to);
  
          RuntimeColorMap runtime = runtime(cm);
          assertEquals(1, runtime.getcolor('a'));
          assertEquals(2, runtime.getcolor('b'));
          assertEquals(1, runtime.getcolor('z'));
          assertEquals(Constants.WHITE, runtime.getcolor('A'));
      }
  
      @Test
      public void testMoreComplexSubrange() throws Exception {
          when(compiler.getNfa()).thenReturn(nfa); // supply the NFA
          ColorMap cm = new ColorMap(compiler);
          State from = Mockito.mock(State.class);
          State to = Mockito.mock(State.class);
          cm.subrange('a', 'f', from, to);
          cm.okcolors(nfa);
          from = Mockito.mock(State.class);
          to = Mockito.mock(State.class);
          cm.subrange('g', 'z', from, to);
          cm.okcolors(nfa);
          from = Mockito.mock(State.class);
          to = Mockito.mock(State.class);
          cm.subrange('e', 'h', from, to);
          cm.okcolors(nfa);
          Map<Range<Integer>, Short> ranges = cm.getMap().asMapOfRanges();
          assertEquals(6, ranges.size());
          List<Range<Integer>> keys = Lists.newArrayList(ranges.keySet());
          assertEquals(Range.closedOpen(0, (int)'a'), keys.get(0));
          assertEquals(Range.closedOpen((int)'a', (int)'e'), keys.get(1));
          assertEquals(Range.closedOpen((int)'e', (int)'g'), keys.get(2));
          assertEquals(Range.closedOpen((int)'g', (int)'i'), keys.get(3));
          assertEquals(Range.closedOpen((int)'i', (int)'z' + 1), keys.get(4));
      }
  
      // This proves that, once the dust settles, the colors are what you would expect.
      // It does leave some questions unanswered about the arcs. It seems as if
      // it makes redundant calls to newarc. In the new version, it's not clear how hard
      // to work to avoid this.
  
      @Test
      public void testBigRange() throws Exception {
          when(compiler.getNfa()).thenReturn(nfa); // supply the NFA
  
          if (traceNewarc) {
              doAnswer(new Answer() {
                  public Object answer(InvocationOnMock invocation) {
                      System.out.printf("%s %s %s %s\n",
                              invocation.getArguments()[0].toString(),
                              invocation.getArguments()[1].toString(),
                              invocation.getArguments()[2],
                              invocation.getArguments()[3]
                      );
                      return null;
                  }
              }).when(nfa).newarc(anyInt(), anyShort(), any(State.class), any(State.class));
          }
  
          ColorMap cm = new ColorMap(compiler);
          State from = Mockito.mock(State.class);
          State to = Mockito.mock(State.class);
          cm.subrange('\uc000', '\ue000', from, to);
          cm.okcolors(nfa);
  
          cm.subrange('\uc001', '\uc101', from, to);
          cm.okcolors(nfa);
  
          RuntimeColorMap runtime = runtime(cm);
          assertEquals(1, runtime.getcolor('\uc000'));
          assertEquals(2, runtime.getcolor('\uc001'));
          assertEquals(1, runtime.getcolor('\udfff'));
          assertEquals(Constants.WHITE, runtime.getcolor('A'));
      }
  }
  