001 package hw7.test;
002
003 import static org.junit.Assert.assertEquals;
004
005 import java.io.BufferedReader;
006 import java.io.File;
007 import java.io.FileFilter;
008 import java.io.FileNotFoundException;
009 import java.io.FileReader;
010 import java.io.IOException;
011 import java.net.URISyntaxException;
012 import java.util.ArrayList;
013 import java.util.LinkedList;
014 import java.util.List;
015
016 import org.junit.Test;
017 import org.junit.runner.RunWith;
018 import org.junit.runners.Parameterized.Parameters;
019
020 import utils.tests.LabelledParameterized;
021 import utils.tests.LabelledParameterized.Labels;
022
023 /**
024 * This class, along with HW7TestDriver, can be used to test your Campus Paths
025 * application. It is assumed that the files are located in the same directory.
026 *
027 * It works by parameterizing test methods over some data values, and then
028 * creating an instance for the cross-product of test methods and data values.
029 * In this case, it will create one HW7TestDriver instance per .expected file,
030 * and for each of those it will run the checkAgainstExpectedOutput() test.
031 * See the JUnit4 Javadocs for more information, or Google for more examples.
032 */
033 @RunWith(LabelledParameterized.class)
034 public class ScriptFileTests {
035
036 //static fields and methods used during setup of the parameterized runner
037 private static FileFilter testFileFilter = new FileFilter() {
038 public boolean accept(File file) {
039 return file.getName().endsWith(".test");
040 }
041 };
042 private static List<String> testScriptNames = null; // not yet calculated
043 private static List<File> testScriptFiles = null; // not yet calculated
044
045 //used by the actual test instance
046 private final File testScriptFile;
047
048 /**
049 * This method searches for and creates file handles for each script test.
050 * It only searches the immediate directory where the ScriptFileTests.class
051 * classfile is located.
052 */
053 public static void calculateTestFiles() {
054 if (ScriptFileTests.testScriptFiles != null
055 || ScriptFileTests.testScriptNames != null) {
056 //already initialized
057 return;
058 }
059
060 ScriptFileTests.testScriptNames = new LinkedList<String>();
061 ScriptFileTests.testScriptFiles = new LinkedList<File>();
062 try {
063 // getResource() cannot be null: this file itself is ScriptFileTests
064 // getParentFile() cannot be null: ScriptFileTests has a package
065 @SuppressWarnings("nullness")
066 File myDirectory = new File(ScriptFileTests.class.getResource("ScriptFileTests.class").toURI()).getParentFile();
067 for (File f : myDirectory.listFiles(ScriptFileTests.testFileFilter)) {
068 testScriptNames.add(f.getName());
069 testScriptFiles.add(f);
070 }
071
072 } catch (URISyntaxException e) {
073 throw new RuntimeException(e);
074 }
075 }
076
077 /**
078 * This method is called in the constructor of Parameterized.
079 *
080 * @return List of argument arrays that should be invoked on the ScriptFileTests constructor by the
081 * Parameterized test runner. Since that runner's constructor has one parameter, the
082 * array only has one element.
083 */
084 @Parameters
085 public static List<Object[]> getTestFiles() {
086 ScriptFileTests.calculateTestFiles();
087
088 if (ScriptFileTests.testScriptFiles == null)
089 throw new IllegalStateException("Did not initialise any files to test");
090
091 //we have to wrap testScriptFiles here so Parameterized.class receives a list of arg array.
092 List<Object[]> filesToTest = new ArrayList<Object[]>(testScriptFiles.size());
093 for (File f : ScriptFileTests.testScriptFiles) {
094 filesToTest.add(new Object[]{ f });
095 }
096
097 return filesToTest;
098 }
099
100 /**
101 * This method is called in the constructor of LabelledParameterized. Since
102 * getTestFiles (and thus calculateTestFiles()) should have already been
103 * called by the Parameterized constructor, the test script names should already have been computed.
104 *
105 * @return List of labels to be used as names for each of the parameterized tests. These names
106 * are the same as the script file used to run the test.
107 */
108 @Labels
109 public static List<String> getTestLabels() {
110 if (ScriptFileTests.testScriptNames == null)
111 throw new IllegalStateException("Must initialize list of test names before creating tests.");
112
113 return ScriptFileTests.testScriptNames;
114 }
115
116 /**
117 * This constructor is reflectively called by the Parameterized runner. It creates
118 * a script file test instance, representing one script file to be tested.
119 */
120 public ScriptFileTests(File testScriptFile) {
121 this.testScriptFile = testScriptFile;
122 }
123
124 /**
125 * Reads in the contents of a file
126 * @throws FileNotFoundException, IOException
127 * @requires that the specified File exists && File ends with a newline
128 * @returns the contents of that file
129 */
130 private String fileContents(File f) throws IOException {
131 if (f == null)
132 throw new IllegalArgumentException("No file specified");
133
134 BufferedReader br = new BufferedReader(new FileReader(f));
135
136 StringBuilder result = new StringBuilder();
137 String line = null;
138
139 //read line reads up to *any* newline character
140 while ( (line = br.readLine()) != null) {
141 result.append(line.trim());
142 result.append('\n');
143 }
144
145 br.close();
146 return result.toString();
147 }
148
149 /**
150 * @throws IOException
151 * @requires there exists a test file indicated by testScriptFile
152 *
153 * @effects runs the test in filename, and output its results to a file in
154 * the same directory with name filename+".actual"; if that file already
155 * exists, it will be overwritten.
156 * @returns the contents of the output file
157 */
158 private String runScriptFile() throws IOException {
159 if (testScriptFile == null)
160 throw new RuntimeException("No file specified");
161
162 File actual = fileWithSuffix("actual");
163
164 HW7TestDriver td = new HW7TestDriver(testScriptFile, actual);
165 td.runTests();
166
167 return fileContents(actual);
168 }
169
170 /**
171 * @param newSuffix
172 * @return a File with the same name as testScriptFile, except that the test
173 * suffix is replaced by the given suffix
174 */
175 private File fileWithSuffix(String newSuffix) {
176 File parent = testScriptFile.getParentFile();
177 String driverName = testScriptFile.getName();
178 String baseName = driverName.substring(0, driverName.length() - "test".length());
179
180 return new File(parent, baseName + newSuffix);
181 }
182
183 /**
184 * The only test that is run: run a script file and test its output.
185 * @throws IOException
186 */
187 @Test(timeout=30000)
188 public void checkAgainstExpectedOutput() throws IOException {
189 File expected = fileWithSuffix("expected");
190 assertEquals(testScriptFile.getName(), fileContents(expected), runScriptFile());
191 }
192 }