View Javadoc
1   package org.djutils.traceverifier;
2   
3   import java.io.BufferedReader;
4   import java.io.BufferedWriter;
5   import java.io.Closeable;
6   import java.io.File;
7   import java.io.FileReader;
8   import java.io.FileWriter;
9   import java.io.IOException;
10  
11  /**
12   * Create or verify identity of a trace of states of a system. <br>
13   * E.g., A simulation that is run with the same initial conditions (including random seed) should behave in a 100% predictable
14   * manner. If it does not, there is some source of randomness that probably should be eliminated. This package can help locate
15   * where the various runs of the simulation start to deviate from one another. <br>
16   * The simulation must be instrumented with calls to the <code>sample</code> method. The sample method either records these
17   * samples in a file, or compares these samples with the values that were stored in the file in a previous run. When a
18   * difference occurs, the sample method throws an exception. <br>
19   * <br>
20   * Copyright (c) 2020-2024 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
21   * for project information <a href="https://djutils.org" target="_blank"> https://djutils.org</a>. The DJUTILS project is
22   * distributed under a three-clause BSD-style license, which can be found at
23   * <a href="https://djutils.org/docs/license.html" target="_blank"> https://djutils.org/docs/license.html</a>. <br>
24   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
25   * @author <a href="https://www.tudelft.nl/pknoppers">Peter Knoppers</a>
26   */
27  public class TraceVerifier implements Closeable
28  {
29      /** Name of the output file (when in writing mode). */
30      private final String outputFileName;
31  
32      /** Reader for existing trace file. */
33      private final BufferedReader reader;
34  
35      /**
36       * Create a new TraceVerifier.
37       * @param fileName String; name of the file for the trace
38       * @throws IOException when reading or writing fails
39       */
40      public TraceVerifier(final String fileName) throws IOException
41      {
42          // System.out.println("Creating TraceVerifier; file name is \"" + fileName + "\"");
43          File traceFile = new File(fileName);
44          if (traceFile.exists())
45          {
46              // Verify mode
47              this.reader = new BufferedReader(new FileReader(fileName));
48              this.outputFileName = null;
49          }
50          else
51          {
52              // Record mode
53              this.outputFileName = fileName;
54              BufferedWriter writer = new BufferedWriter(new FileWriter(fileName));
55              writer.close();
56              this.reader = null;
57          }
58      }
59  
60      /**
61       * Add or compare one sample.
62       * @param description String; some kind of description of the sample (usually some kind of time stamp).
63       * @param state String; summary of the state of the process that is sampled.
64       * @throws IOException when reading or writing fails
65       * @throws TraceVerifierException on detection of a sample discrepancy
66       */
67      public void sample(final String description, final String state) throws IOException
68      {
69          String got = String.format("%s: %s", description, state);
70          if (this.reader != null)
71          {
72              String expected = this.reader.readLine();
73              if (expected.equals(got))
74              {
75                  return;
76              }
77              int indexOfFirstDifference = 0;
78              while (got.charAt(indexOfFirstDifference) == expected.charAt(indexOfFirstDifference))
79              {
80                  indexOfFirstDifference++;
81              }
82              String format =
83                      indexOfFirstDifference == 0 ? "Discrepancy found.\n%%-8.8s: \"%%s\"\n%%-8.8s: \"%%s\"\n%%-8.8s:  %%s^"
84                              : String.format("Discrepancy found.\n%%-8.8s: \"%%s\"\n%%-8.8s: \"%%s\"\n%%-8.8s:  %%%d.%ds^",
85                                      indexOfFirstDifference, indexOfFirstDifference);
86              String error = String.format(format, "Got", got, "Expected", expected, "1st diff", "");
87              throw new TraceVerifierException(error);
88          }
89          BufferedWriter writer = new BufferedWriter(new FileWriter(this.outputFileName, true));
90          writer.append(got);
91          writer.append('\n');
92          writer.close();
93      }
94  
95      @Override
96      public void close() throws IOException
97      {
98          if (null != this.reader)
99          {
100             this.reader.close();
101         }
102     }
103 
104     @Override
105     public String toString()
106     {
107         return "TraceVerifier [reader=" + this.reader + ", outputFileName=" + this.outputFileName + "]";
108     }
109 
110 }