code: package morfologik.fsa.builders;
  
  import static morfologik.fsa.FSAFlags.*;
  import static org.junit.Assert.*;
  
  import java.io.ByteArrayInputStream;
  import java.io.ByteArrayOutputStream;
  import java.io.IOException;
  import java.nio.ByteBuffer;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.HashSet;
  
  import morfologik.fsa.FSA;
  import morfologik.fsa.FSAFlags;
  
  import org.junit.Test;
  
  import static com.carrotsearch.randomizedtesting.RandomizedTest.*;
  
  public abstract class SerializerTestBase extends TestBase {
    @Test
    public void testA() throws IOException {
      byte[][] input = new byte[][] { { 'a' }, };
  
      Arrays.sort(input, FSABuilder.LEXICAL_ORDERING);
      FSA s = FSABuilder.build(input);
  
      checkSerialization(input, s);
    }
  
    @Test
    public void testArcsSharing() throws IOException {
      byte[][] input = new byte[][] { 
        { 'a', 'c', 'f' },
        { 'a', 'd', 'g' },
        { 'a', 'e', 'h' },
        { 'b', 'd', 'g' },
        { 'b', 'e', 'h' },
      };
  
      Arrays.sort(input, FSABuilder.LEXICAL_ORDERING);
      FSA s = FSABuilder.build(input);
  
      checkSerialization(input, s);
    }
  
    @Test
    public void testFSA5SerializerSimple() throws IOException {
      byte[][] input = new byte[][] { 
        { 'a' }, 
        { 'a', 'b', 'a' },
        { 'a', 'c' }, 
        { 'b' }, 
        { 'b', 'a' }, 
        { 'c' },
      };
  
      Arrays.sort(input, FSABuilder.LEXICAL_ORDERING);
      FSA s = FSABuilder.build(input);
  
      checkSerialization(input, s);
    }
  
    @Test
    public void testNotMinimal() throws IOException {
      byte[][] input = new byte[][] { 
        { 'a', 'b', 'a' }, 
        { 'b' },
        { 'b', 'a' }
      };
  
      Arrays.sort(input, FSABuilder.LEXICAL_ORDERING);
      FSA s = FSABuilder.build(input);
  
      checkSerialization(input, s);
    }
  
    @Test
    public void testFSA5Bug0() throws IOException {
      checkCorrect(new String[] { 
        "3-D+A+JJ", 
        "3-D+A+NN", 
        "4-F+A+NN",
        "z+A+NN", });
    }
  
    @Test
    public void testFSA5Bug1() throws IOException {
      checkCorrect(new String[] { "+NP", "n+N", "n+NP", });
    }
  
    private void checkCorrect(String[] strings) throws IOException {
      byte[][] input = new byte[strings.length][];
      for (int i = 0; i < strings.length; i++) {
          input[i] = strings[i].getBytes("ISO8859-1");
      }
  
      Arrays.sort(input, FSABuilder.LEXICAL_ORDERING);
      FSA s = FSABuilder.build(input);
  
      checkSerialization(input, s);
    }
  
    @Test
    public void testEmptyInput() throws IOException {
      byte[][] input = new byte[][] {};
      FSA s = FSABuilder.build(input);
  
      checkSerialization(input, s);
    }
  
    @Test
    public void test_abc() throws IOException {
      testBuiltIn(FSA.read(FSA5Test.class.getResourceAsStream("abc.fsa")));
    }
  
    @Test
    public void test_minimal() throws IOException {
      testBuiltIn(FSA.read(FSA5Test.class.getResourceAsStream("minimal.fsa")));
    }
  
    @Test
    public void test_minimal2() throws IOException {
      testBuiltIn(FSA.read(FSA5Test.class.getResourceAsStream("minimal2.fsa")));
    }
  
    @Test
    public void test_en_tst() throws IOException {
      testBuiltIn(FSA.read(FSA5Test.class.getResourceAsStream("en_tst.dict")));
    }
  
    private void testBuiltIn(FSA fsa) throws IOException {
      final ArrayList<byte[]> sequences = new ArrayList<byte[]>();
  
      sequences.clear();
      for (ByteBuffer bb : fsa) {
        sequences.add(Arrays.copyOf(bb.array(), bb.remaining()));
      }
  
      Collections.sort(sequences, FSABuilder.LEXICAL_ORDERING);
  
      final byte[][] in = sequences.toArray(new byte[sequences.size()][]);
      FSA root = FSABuilder.build(in);
  
      // Check if the DFSA is correct first.
      FSATestUtils.checkCorrect(in, root);
  
      // Check serialization.
      checkSerialization(in, root);
    }
  
    private void checkSerialization(byte[][] input, FSA root) throws IOException {
      checkSerialization0(createSerializer(), input, root);
      if (createSerializer().getFlags().contains(FSAFlags.NUMBERS)) {
        checkSerialization0(createSerializer().withNumbers(), input, root);
      }
    }
  
    private void checkSerialization0(FSASerializer serializer, final byte[][] in, FSA root) throws IOException {
      final byte[] fsaData = serializer.serialize(root, new ByteArrayOutputStream()).toByteArray();
  
      FSA fsa = FSA.read(new ByteArrayInputStream(fsaData));
      checkCorrect(in, fsa);
    }
  
    /*
     * Check if the FSA is correct with respect to the given input.
     */
    protected void checkCorrect(byte[][] input, FSA fsa) {
      // (1) All input sequences are in the right language.
      HashSet<ByteBuffer> rl = new HashSet<ByteBuffer>();
      for (ByteBuffer bb : fsa) {
        byte[] array = bb.array();
        int length = bb.remaining();
        rl.add(ByteBuffer.wrap(Arrays.copyOf(array, length)));
      }
  
      HashSet<ByteBuffer> uniqueInput = new HashSet<ByteBuffer>();
      for (byte[] sequence : input) {
        uniqueInput.add(ByteBuffer.wrap(sequence));
      }
  
      for (ByteBuffer sequence : uniqueInput) {
        if (!rl.remove(sequence)) {
          fail("Not present in the right language: " + toString(sequence));
        }
      }
  
      // (2) No other sequence _other_ than the input is in the right
      // language.
      assertEquals(0, rl.size());
    }
  
    @Test
    public void testAutomatonWithNodeNumbers() throws IOException {
      assumeTrue(createSerializer().getFlags().contains(FSAFlags.NUMBERS));
  
      byte[][] input = new byte[][] { 
        { 'a' }, 
        { 'a', 'b', 'a' },
        { 'a', 'c' }, 
        { 'b' }, 
        { 'b', 'a' }, 
        { 'c' }, };
  
      Arrays.sort(input, FSABuilder.LEXICAL_ORDERING);
      FSA s = FSABuilder.build(input);
  
      final byte[] fsaData = 
          createSerializer().withNumbers()
                            .serialize(s, new ByteArrayOutputStream())
                            .toByteArray();
  
      FSA fsa = FSA.read(new ByteArrayInputStream(fsaData));
  
      // Ensure we have the NUMBERS flag set.
      assertTrue(fsa.getFlags().contains(NUMBERS));
  
      // Get all numbers from nodes.
      byte[] buffer = new byte[128];
      final ArrayList<String> result = new ArrayList<String>();
      FSA5Test.walkNode(buffer, 0, fsa, fsa.getRootNode(), 0, result);
  
      Collections.sort(result);
      assertEquals(
              Arrays.asList("0 a", "1 aba", "2 ac", "3 b", "4 ba", "5 c"),
              result);
    }
  
    protected abstract FSASerializer createSerializer();
  
    /*
     * Drain bytes from a byte buffer to a string.
     */
    public static String toString(ByteBuffer sequence) {
        byte [] bytes = new byte [sequence.remaining()];
        sequence.get(bytes);
        return Arrays.toString(bytes);
    }    
  }
  