001package daikon.test;
002
003import static java.nio.charset.StandardCharsets.UTF_8;
004import static java.util.logging.Level.INFO;
005import static org.junit.Assert.fail;
006
007import daikon.FileIO;
008import java.io.BufferedReader;
009import java.io.ByteArrayOutputStream;
010import java.io.IOException;
011import java.io.InputStream;
012import java.io.InputStreamReader;
013import java.io.PrintStream;
014import java.io.UncheckedIOException;
015import java.util.ArrayList;
016import java.util.List;
017import java.util.StringTokenizer;
018import junit.framework.*;
019import org.junit.BeforeClass;
020import org.junit.Test;
021
022/**
023 * This tests various aspects of VarInfoName's and transforming VarInfoName's. This calls
024 * VarInfoNameDriver after parsing all input files of the name "varInfoNameTest.<em>foo</em>".
025 * VarInfoNameDriver does transform tests, and its output is compared to the
026 * "varInfoNameTest.<em>foo</em>.goal" file by this.
027 *
028 * <p>To add a new test case, add a line to the <em>foo</em> file and a line to the goal file with
029 * intended output. Format of the <em>foo</em> file is output method, followed by a variable name.
030 * Output methods are defined in VarInfoNameDriver. To add a new transformation method (which can
031 * then be tested in test cases) add a static Handler implementation to VarInfoNameDriver modeled
032 * after one of the ones already present and add a static {} line after to add the handler to the
033 * list of handlers.
034 */
035public class VarInfoNameTest {
036
037  private static final String lineSep = daikon.Global.lineSep;
038
039  /** prepare for tests */
040  @BeforeClass
041  public static void setUpClass() {
042    daikon.LogHelper.setupLogs(INFO);
043    FileIO.new_decl_format = true;
044  }
045
046  @Test
047  public void testParse() {
048    run("testParse");
049  }
050
051  // Fails mysteriously, only when run from a cron job.  The failure is in
052  // obsolescent code (VarInfoNameTest), so comment it out rather than
053  // debugging it.
054  // @Test public void testEscForall() { run("testEscForall"); }
055  @Test
056  public void testSubscript() {
057    run("testSubscript");
058  }
059
060  @Test
061  public void testJML() {
062    run("testJML");
063  }
064
065  private void run(String name) {
066    String file = "varInfoNameTest." + name;
067    try (InputStream input_stream = VarInfoNameTest.class.getResourceAsStream(file);
068        InputStream goal_stream = VarInfoNameTest.class.getResourceAsStream(file + ".goal")) {
069
070      if (input_stream == null) {
071        throw new Error("couldn't find " + file);
072      }
073      if (goal_stream == null) {
074        throw new Error("couldn't find " + file + ".goal");
075      }
076
077      // run the tests
078      ByteArrayOutputStream out = new ByteArrayOutputStream();
079      VarInfoNameDriver.run(input_stream, new PrintStream(out));
080
081      // put output into actual
082      List<String> _actual = new ArrayList<>();
083      @SuppressWarnings("DefaultCharset") // toString(Charset) was introduced in Java 10
084      StringTokenizer tok = new StringTokenizer(out.toString(), lineSep);
085      while (tok.hasMoreTokens()) {
086        _actual.add(tok.nextToken());
087      }
088      String[] actual = _actual.toArray(new String[0]);
089
090      // put desired into goal
091      List<String> _goal = new ArrayList<>();
092      try {
093        BufferedReader buf = new BufferedReader(new InputStreamReader(goal_stream, UTF_8));
094        while (buf.ready()) {
095          String line = buf.readLine();
096          _goal.add(line);
097        }
098        buf.close();
099      } catch (IOException e) {
100        throw new RuntimeException(e.toString());
101      }
102      String[] goal = _goal.toArray(new String[0]);
103
104      // diff desired and output
105      diff(goal, actual);
106    } catch (IOException e) {
107      throw new UncheckedIOException(e);
108    }
109  }
110
111  private void diff(String[] goal, String[] actual) {
112    for (int i = 0; i < goal.length; i++) {
113      String goal_line = goal[i];
114      if (i >= actual.length) {
115        fail(
116            "Diff error:"
117                + lineSep
118                + "Actual had too few lines, starting with goal line:"
119                + lineSep
120                + "\t"
121                + goal_line);
122      }
123      String actual_line = actual[i];
124      if (!goal_line.equals(actual_line)) {
125        String goals = "";
126        String actuals = "";
127        int low = Math.max(0, i - 3);
128        int high = Math.min(Math.min(i + 3, actual.length - 1), goal.length - 1);
129        for (int j = low; j <= high; j++) {
130          if (!goal[j].equals(actual[j])) {
131            goals += ">";
132            actuals += ">";
133          }
134          goals += "\t" + goal[j] + lineSep;
135          actuals += "\t" + actual[j] + lineSep;
136        }
137        fail(
138            "Diff error:"
139                + lineSep
140                + "Different output encountered.  Expected:"
141                + lineSep
142                + goals
143                + "Received:"
144                + lineSep
145                + actuals
146                + " on line: "
147                + i);
148      }
149    }
150    if (actual.length > goal.length) {
151      StringBuilder extra = new StringBuilder();
152      for (int i = goal.length; i < actual.length; i++) {
153        extra.append("\t");
154        extra.append(actual[i]);
155        extra.append(lineSep);
156      }
157      fail("Diff error:" + lineSep + "Actual had extra lines:" + lineSep + extra.toString());
158    }
159  }
160
161  // parsing
162  // interning
163  // *name()
164  // object methods
165
166  // Simple
167  // Size
168  // Function
169  // TypeOf
170  // Prestate
171  // Poststate
172  // Add
173  // Elements
174  // Subscript
175  // Slice
176
177  // ElementsFinder
178  // Replacer
179  // InorderFlattener
180  // QuantifierVisitor
181  // QuantHelper.format_esc
182}