code: package org.gridkit.jvmtool.stacktrace;
  
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.FileNotFoundException;
  import java.io.FileOutputStream;
  import java.io.IOException;
  
  import org.junit.Assert;
  import org.junit.Rule;
  import org.junit.Test;
  import org.junit.rules.TestName;
  
  public class StackTraceReaderTest {
  
      @Rule
      public TestName testName = new TestName();
  
      @Test
      public void read_dump_v1() throws FileNotFoundException, IOException {
  
          StackTraceReader reader = StackTraceCodec.newReader(new FileInputStream("src/test/resources/dump_v1.std"));
  
          if (!reader.isLoaded()) {
              reader.loadNext();
          }
  
          int n = 0;
          while(reader.isLoaded()) {
              reader.loadNext();
              ++n;
          }
  
          System.out.println("Read " + n + " traces from file");
      }
  
      @Test
      public void read_dump_v2() throws FileNotFoundException, IOException {
  
          StackTraceReader reader = StackTraceCodec.newReader(new FileInputStream("src/test/resources/dump_v2.std"));
  
          if (!reader.isLoaded()) {
              reader.loadNext();
          }
  
          int n = 0;
          while(reader.isLoaded()) {
  //            System.out.println(reader.getCounters() + " - " + reader.getThreadName());
              reader.loadNext();
              ++n;
          }
  
          System.out.println("Read " + n + " traces from file");
      }
  
      @Test
      public void read_dump_v1_rewrite_and_compare() throws FileNotFoundException, IOException {
  
          File file = new File("target/tmp/" + testName.getMethodName() + "-" + System.currentTimeMillis() + ".std");
          file.getParentFile().mkdirs();
          file.delete();
  
          FileOutputStream fow = new FileOutputStream(file);
          StackTraceWriter writer = StackTraceCodec.newWriter(fow);
  
          StackTraceReader reader = StackTraceCodec.newReader(new FileInputStream("src/test/resources/dump_v1.std"));
  
          copyAllTraces(reader, writer);
          writer.close();
  
          reader = StackTraceCodec.newReader(new FileInputStream(file));
          StackTraceReader origReader = StackTraceCodec.newReader(new FileInputStream("src/test/resources/dump_v1.std"));
  
          assertEqual(origReader, reader);
      }
  
      @Test
      public void read_dump_v2_rewrite_and_compare() throws FileNotFoundException, IOException {
  
          File file = new File("target/tmp/" + testName.getMethodName() + "-" + System.currentTimeMillis() + ".std");
          file.getParentFile().mkdirs();
          file.delete();
  
          FileOutputStream fow = new FileOutputStream(file);
          StackTraceWriter writer = StackTraceCodec.newWriter(fow);
  
          StackTraceReader reader = StackTraceCodec.newReader(new FileInputStream("src/test/resources/dump_v2.std"));
  
          copyAllTraces(reader, writer);
          writer.close();
  
          reader = StackTraceCodec.newReader(new FileInputStream(file));
          StackTraceReader origReader = StackTraceCodec.newReader(new FileInputStream("src/test/resources/dump_v2.std"));
  
          assertEqual(origReader, reader);
      }
  
      void assertEqual(StackTraceReader r1, StackTraceReader r2) throws IOException {
          if (!r1.isLoaded()) {
              r1.loadNext();
          }
          if (!r2.isLoaded()) {
              r2.loadNext();
          }
  
          int n = 0;
          while(r1.isLoaded() && r2.isLoaded()) {
              assertSameState(r1, r2);
              r1.loadNext();
              r2.loadNext();
              ++n;
          }
  
          if (r1.isLoaded()) {
              Assert.fail("Loaded " + n + " traces, expected more");
          }
          if (r2.isLoaded()) {
              Assert.fail("Loaded excepted " + n + " traces, but more is available");
          }
      }
  
      private void assertSameState(StackTraceReader r1, StackTraceReader r2) {
          Assert.assertEquals("ThreadID", r1.getThreadId(), r2.getThreadId());
          Assert.assertEquals("ThreadName", r1.getThreadName(), r2.getThreadName());
          Assert.assertEquals("Timestamp", r1.getTimestamp(), r2.getTimestamp());
          Assert.assertEquals("State", r1.getThreadState(), r2.getThreadState());
          CounterCollection c1 = r1.getCounters();
          CounterCollection c2 = r2.getCounters();
          for(String key: c1) {
              Assert.assertEquals("Counter #" + key, c1.getValue(key), c2.getValue(key));
          }
          for(String key: c2) {
              Assert.assertEquals("Counter #" + key, c1.getValue(key), c2.getValue(key));
          }
          assertEqualTraces(r1.getStackTrace(), r2.getStackTrace());
      }
  
      private void copyAllTraces(StackTraceReader reader, StackTraceWriter writer) throws IOException {
          if (!reader.isLoaded()) {
              reader.loadNext();
          }
          while(reader.isLoaded()) {
              copyTrace(reader, writer);
              reader.loadNext();
          }
      }
  
      private void copyTrace(StackTraceReader reader, StackTraceWriter writer) throws IOException {
          ThreadCapture snap = new ThreadCapture();
          snap.threadId = reader.getThreadId();
          snap.threadName = reader.getThreadName();
          snap.state = reader.getThreadState();
          snap.timestamp = reader.getTimestamp();
          snap.counters.copyFrom(reader.getCounters());
          snap.elements = reader.getTrace();
          writer.write(snap);
      }
  
      private void assertEqualTraces(StackFrameList s1, StackFrameList s2) {
          StringBuilder sb1 = new StringBuilder();
          for(StackFrame f: s1) {
              sb1.append(f.toString());
              sb1.append('\n');
          }
          StringBuilder sb2 = new StringBuilder();
          for(StackFrame f: s2) {
              sb2.append(f.toString());
              sb2.append('\n');
          }
          Assert.assertEquals("Stack trace", sb1.toString(), sb2.toString());
      }
  }
  