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 }