diff options
author | Rogan Creswick <creswick@gmail.com> | 2012-09-02 14:10:01 -0700 |
---|---|---|
committer | Rogan Creswick <creswick@gmail.com> | 2012-09-02 14:10:01 -0700 |
commit | a8664102395dc9c899428ea6e31a64ff6f22046e (patch) | |
tree | a1d764b3983ff385d59d2d2fad8c9ccda4e78495 /rsTester/src | |
parent | 172992b50a44417ec758ba79b25026c700961e4b (diff) |
refactored the java-based apps: testrunner, rstester, batch-executor
Diffstat (limited to 'rsTester/src')
26 files changed, 1555 insertions, 0 deletions
diff --git a/rsTester/src/main/java/com/galois/fiveui/BatchRunner.java b/rsTester/src/main/java/com/galois/fiveui/BatchRunner.java new file mode 100644 index 0000000..a6996ab --- /dev/null +++ b/rsTester/src/main/java/com/galois/fiveui/BatchRunner.java @@ -0,0 +1,161 @@ +/** + * Module : BatchRunner.java Copyright : (c) 2011-2012, Galois, Inc. + * + * Maintainer : Stability : Provisional Portability: Portable + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.galois.fiveui; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.WebDriver; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableList.Builder; +import com.google.common.collect.Lists; + +public class BatchRunner { + + private final WebDriver _driver; + private final JavascriptExecutor _exe; + + // Relative to the batch directory. + private static final String DATA_DIR = "../data/"; + private static final String J_QUERY_JS = DATA_DIR + + "lib/jquery/jquery-1.7.1.min.js"; + private static final String PRELUDE_JS = DATA_DIR + + "fiveui/injected/prelude.js"; + private static final String SEL_INJECTED_COMPUTE_JS = DATA_DIR + + "/fiveui/selenium/selenium-injected-compute.js"; + + private static final String INJECTED_COMPUTE_JS = DATA_DIR + + "/fiveui/injected/fiveui-injected-compute.js"; + + public BatchRunner(WebDriver driver) { + _driver = driver; + _exe = (JavascriptExecutor) _driver; + } + + public ImmutableList<Result> runTests(ImmutableList<RuleTest> build) { + Builder<Result> resBuilder = ImmutableList.builder(); + for (RuleTest test : build) { + resBuilder.addAll(runTest(test)); + } + return resBuilder.build(); + } + + /** + * Run a URITest, returning the result (success, failure details, or + * indicator of exceptional conditions.) + * + * @param test + */ + public ImmutableList<Result> runTest(final RuleTest test) { + RuleSet rule = test.getRule(); + + ImmutableList<Result> rawResults; + Builder<Result> builder = ImmutableList.builder(); + try { + _driver.get(test.getUri().toString()); + rawResults = runRule(rule); + + List<ResType> oracle = Lists.newArrayList(test.getOracle()); + for (Result result : rawResults) { + Result res; + if ( oracle.remove(result.getType()) ) { + res = Result.pass(_driver, + test.getRuleId() + ": Got expected result: "+result.getType()); + } else { + res = Result.error(_driver, + test.getRuleId() + ": Unexpected Result: "+result); + } + builder.add(res); + } + } catch (Exception e) { + String errStr = "Could not run rule: " + rule.getName() + "\n"; + errStr += e.toString(); + rawResults = ImmutableList.of( + Result.exception(_driver, "Could not run rule: "+errStr)); + + e.printStackTrace(); + } + + return builder.build(); + } + + private ImmutableList<Result> runRule(final RuleSet ruleSet) throws IOException { + String contentScript = wrapRule(ruleSet); + Builder<Result> builder = ImmutableList.builder(); + + _exe.executeScript(contentScript); + + try { + Thread.sleep(1000); + } catch (InterruptedException e1) { + e1.printStackTrace(); + } + + Object res = _exe.executeScript("return fiveui.selPort.query()"); + + if (res.getClass() == String.class) { + // we received an error via the expected mechanisms: + System.err.println("Exception running rule: " + res); + builder.add(Result.exception(_driver, (String) res)); + return builder.build(); + } else { + + try { + @SuppressWarnings({ "unchecked", "rawtypes" }) + List<Map<String, Map<String, String>>> results = (List) res; + + if (0 == results.size()) { + builder.add(Result.pass(_driver, "passed")); + } + + for (Map<String, Map<String, String>> r : results) { + Map<String, String> problem = r.get("payload"); + + builder.add(Result.error(_driver, problem.get("descr"))); + } + + } catch (ClassCastException e) { + // An unexpected error happened: + builder.add(Result.exception(_driver, "Unexpected object returned: " + + res)); + e.printStackTrace(); + } + } + return builder.build(); + } + + /** + * Build up the complete content script needed to run the rule + * + * @throws IOException + */ + private String wrapRule(RuleSet ruleSet) throws IOException { + String injected = ""; + injected += Utils.readFile(SEL_INJECTED_COMPUTE_JS); + injected += Utils.readFile(J_QUERY_JS); + injected += Utils.readFile(PRELUDE_JS); + injected += Utils.readFile(INJECTED_COMPUTE_JS); + + injected += "return fiveui.selPort.send('SetRules', " + ruleSet + ");"; + + return injected; + } +} diff --git a/rsTester/src/main/java/com/galois/fiveui/Drivers.java b/rsTester/src/main/java/com/galois/fiveui/Drivers.java new file mode 100644 index 0000000..c965b4c --- /dev/null +++ b/rsTester/src/main/java/com/galois/fiveui/Drivers.java @@ -0,0 +1,100 @@ +/** + * Module : Drivers.java Copyright : (c) 2011-2012, Galois, Inc. + * + * Maintainer : Stability : Provisional Portability: Portable + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.galois.fiveui; + +import java.io.File; + +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.chrome.ChromeOptions; +import org.openqa.selenium.firefox.FirefoxDriver; +import org.openqa.selenium.firefox.FirefoxProfile; + +/** + * TODO refactor into a shared package that can be used by the testrunner as w + * well as the batch execution apps. + * + * + * @author creswick + * + */ +public class Drivers { + private static final String CD_BINARY_NAME = "chromedriver"; + private static final String CD_BASE_PATH = mkPath("..", "..", "tools", + "seleniumChromeDrivers"); + + private static final String CHROME_PROFILE = mkPath("..", "..", "profiles", + "chrome"); + + private static final String FF_PROFILE = mkPath("..", "..", "profiles", + "firefox"); + + public static WebDriver buildFFDriver() { + // Extracted into a method so we can set up profiles + + File profileDir = new File(FF_PROFILE); + FirefoxProfile profile = new FirefoxProfile(profileDir); + FirefoxDriver driver = new FirefoxDriver(profile); + + return driver; + } + + public static WebDriver buildChromeDriver() { + // set the chrome driver path: + String chromeDriverPth = + mkPath(CD_BASE_PATH, osNameArch(), CD_BINARY_NAME); + System.setProperty("webdriver.chrome.driver", chromeDriverPth); + + ChromeOptions options = new ChromeOptions(); + options.addArguments("--user-data-dir="+CHROME_PROFILE); + + return new ChromeDriver(options); + } + + private static String mkPath(String... components) { + StringBuilder path = new StringBuilder(); + int remaining = components.length; + for (String c : components) { + path.append(c); + remaining--; + if (remaining != 0) { + path.append(File.separator); + } + } + + return path.toString(); + } + + /** + * Determine the name of the directory that the chromedriver is in, based on + * os.name and os.arch. + * + * @return The name of the directory containing 'chromedriver' + */ + private static String osNameArch() { + String rawOsName = System.getProperty("os.name").toLowerCase(); + String osName = rawOsName.substring(0, 3); + boolean is64bit = System.getProperty("os.arch").indexOf("64") >= 0; + + if (osName.equals("lin")) { + osName += is64bit ? "64" : "32"; + } + return osName; + } + +} diff --git a/rsTester/src/main/java/com/galois/fiveui/RSTestDescription.java b/rsTester/src/main/java/com/galois/fiveui/RSTestDescription.java new file mode 100644 index 0000000..a9a3f02 --- /dev/null +++ b/rsTester/src/main/java/com/galois/fiveui/RSTestDescription.java @@ -0,0 +1,296 @@ +package com.galois.fiveui; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.lang.reflect.Type; +import java.net.URI; +import java.util.List; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableList.Builder; +import com.google.common.collect.Lists; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; + +/** + * RSTestDescriptions represent a set of tests for a specific RuleSet. + * + * RSTestDescriptions are also GSON-serializiable (and deserializable). + * + * An example of the JSON is given below: + * + * <pre> + * {@code + * { 'ruleSet': '../../exampleData/ruleSets/headingGuidelines.json', + * 'tests': [ { 'url': 'http://localhost:8000/exampleData/basic/headings.html', + * 'oracle': [ { 'ruleId': 1 + * , 'results': ['Error', 'Error'] + * }, + * { 'ruleId': 2 + * , 'results': ['Error'] + * } + * ] + * } + * ] + * } + * } + * </pre> + * + * {@code tests} is as list that may contain a number of urls and sets of + * expected Problems for the specified rule set and url combination. + * + * {@code ruleSet} is a string file path that is relative to the rule set + * description json file. + * + * @author creswick + * + */ +public class RSTestDescription { + + /** + * Parse a JSON file into a RSTestDescription + * + * @param runDescFileName The file to load. + * @return A populated RunDescription object. + * @throws FileNotFoundException if runDescFile can't be found. + */ + public static RSTestDescription parse(String runDescFileName) + throws FileNotFoundException { + + GsonBuilder gsonBuilder = new GsonBuilder(); + gsonBuilder.registerTypeAdapter(RSTestDescription.class, + new RSTestDescription.Deserializer(runDescFileName)); + Gson gson = gsonBuilder.create(); + + Reader in = new InputStreamReader(new FileInputStream(runDescFileName)); + + return gson.fromJson(in, RSTestDescription.class); + } + + public static class Deserializer implements JsonDeserializer<RSTestDescription> { + + private final String _descFile; + + public Deserializer(String descFile) { + _descFile = descFile; + } + + public RSTestDescription deserialize(JsonElement json, Type typeOfT, + JsonDeserializationContext context) throws JsonParseException { + + JsonObject obj = json.getAsJsonObject(); + + String ruleSet = obj.get("ruleSet").getAsString(); + JsonArray testArray = obj.get("tests").getAsJsonArray(); + + List<URIMap> tests = Lists.newArrayList(); + for (JsonElement jsonElement : testArray) { + tests.add((URIMap)context.deserialize(jsonElement, URIMap.class)); + } + + String descDir = new File(_descFile).getParent(); + if (null == descDir) { + descDir = "."; + } + + // This probably is not portable, because the ruleSet path separator + // may not match that of the file system. + // TODO if File.separator is not "/", then replace "\" with File.separator. + String rsPath = descDir + File.separator + ruleSet; + + String ruleSetStr; + try { + ruleSetStr = Utils.readFile(rsPath); + } catch (IOException e) { + throw new JsonParseException("Could not read " + rsPath); + } + RuleSet parsed = RuleSet.parse(ruleSetStr); + + return new RSTestDescription(rsPath, tests, parsed); + } + } + + /** + * The path to the selected rule set. + */ + private final String _ruleSetLoc; + + /** + * A list of urls and oracles that define tests for the specified RuleSet. + */ + private final List<URIMap> _tests; + + private final RuleSet _ruleSet; + + public RSTestDescription(String ruleSetLoc, List<URIMap> tests, RuleSet ruleSet) { + _ruleSetLoc = ruleSetLoc; + _tests = tests; + _ruleSet = ruleSet; + } + + public RuleSet getRuleSet() { + return _ruleSet; + } + + public String getRuleSetLoc() { + return _ruleSetLoc; + } + + public ImmutableList<RuleTest> getTests() throws IOException { + Builder<RuleTest> builder = ImmutableList.builder(); + for (URIMap uriMap : _tests) { + for (RuleMap rMap : uriMap.getOracle()) { + builder.add( + new RuleTest(uriMap.getUrl(), getRuleSet(), rMap.getRuleId(), rMap.getResults())); + } + } + return builder.build(); + } + + public String toString() { + Gson gson = new Gson(); + return gson.toJson(this); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = + prime * result + ((_ruleSet == null) ? 0 : _ruleSet.hashCode()); + result = + prime * result + + ((_ruleSetLoc == null) ? 0 : _ruleSetLoc.hashCode()); + result = prime * result + ((_tests == null) ? 0 : _tests.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + RSTestDescription other = (RSTestDescription) obj; + if (_ruleSet == null) { + if (other._ruleSet != null) + return false; + } else if (!_ruleSet.equals(other._ruleSet)) + return false; + if (_ruleSetLoc == null) { + if (other._ruleSetLoc != null) + return false; + } else if (!_ruleSetLoc.equals(other._ruleSetLoc)) + return false; + if (_tests == null) { + if (other._tests != null) + return false; + } else if (!_tests.equals(other._tests)) + return false; + return true; + } + + public static class URIMap { + private URI url; + private List<RuleMap> oracle; + + @SuppressWarnings("unused") + URIMap(){} + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = + prime * result + ((getOracle() == null) ? 0 : getOracle().hashCode()); + result = prime * result + ((getUrl() == null) ? 0 : getUrl().hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + URIMap other = (URIMap) obj; + if (getOracle() == null) { + if (other.getOracle() != null) + return false; + } else if (!getOracle().equals(other.getOracle())) + return false; + if (getUrl() == null) { + if (other.getUrl() != null) + return false; + } else if (!getUrl().equals(other.getUrl())) + return false; + return true; + } + + public URI getUrl() { + return url; + } + + public List<RuleMap> getOracle() { + return oracle; + } + } + + public static class RuleMap { + private int ruleId; + private List<ResType> results; + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = + prime * result + + ((getResults() == null) ? 0 : getResults().hashCode()); + result = prime * result + getRuleId(); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + RuleMap other = (RuleMap) obj; + if (getResults() == null) { + if (other.getResults() != null) + return false; + } else if (!getResults().equals(other.getResults())) + return false; + if (getRuleId() != other.getRuleId()) + return false; + return true; + } + + public int getRuleId() { + return ruleId; + } + + public List<ResType> getResults() { + return results; + } + } + +} diff --git a/rsTester/src/main/java/com/galois/fiveui/ResType.java b/rsTester/src/main/java/com/galois/fiveui/ResType.java new file mode 100644 index 0000000..671d74c --- /dev/null +++ b/rsTester/src/main/java/com/galois/fiveui/ResType.java @@ -0,0 +1,23 @@ +/** + * Module : ResType.java Copyright : (c) 2011-2012, Galois, Inc. + * + * Maintainer : Stability : Provisional Portability: Portable + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.galois.fiveui; + +public enum ResType { + Pass, Error, Warning, Exception +}
\ No newline at end of file diff --git a/rsTester/src/main/java/com/galois/fiveui/Result.java b/rsTester/src/main/java/com/galois/fiveui/Result.java new file mode 100644 index 0000000..2f80aca --- /dev/null +++ b/rsTester/src/main/java/com/galois/fiveui/Result.java @@ -0,0 +1,67 @@ +/** + * Module : Result.java Copyright : (c) 2011-2012, Galois, Inc. + * + * Maintainer : Stability : Provisional Portability: Portable + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.galois.fiveui; + +import org.openqa.selenium.WebDriver; + +public class Result { + + public static Result exception(WebDriver driver, String res) { + return new Result(ResType.Exception, driver, res); + } + + public static Result pass(WebDriver driver, String res) { + return new Result(ResType.Pass, driver, res); + } + + public static Result error(WebDriver driver, String res) { + return new Result(ResType.Error, driver, res); + } + + public static Result warning(WebDriver driver, String res) { + return new Result(ResType.Warning, driver, res); + } + + private ResType _type; + private String _desc; + private WebDriver _driver; + + private Result(ResType type, WebDriver driver, String desc) { + super(); + _type = type; + _desc = desc; + _driver = driver; + } + + public ResType getType() { + return _type; + } + + public String getDesc() { + return _desc; + } + + public WebDriver getDriver() { + return _driver; + } + + @Override + public String toString() { + return getType() + " - " + _driver + ": " + getDesc(); + } +} diff --git a/rsTester/src/main/java/com/galois/fiveui/Rule.java b/rsTester/src/main/java/com/galois/fiveui/Rule.java new file mode 100644 index 0000000..838fafb --- /dev/null +++ b/rsTester/src/main/java/com/galois/fiveui/Rule.java @@ -0,0 +1,130 @@ +/** + * Module : Rule.java Copyright : (c) 2011-2012, Galois, Inc. + * + * Maintainer : Stability : Provisional Portability: Portable + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.galois.fiveui; + +import java.util.HashMap; + +import com.google.common.base.Function; +import com.google.gson.Gson; + +public class Rule { + + /** + * Function wrapper around `RuleSet.parse` for use in `transform` and other + * functional combinators. + */ + public static final Function<HashMap<String, Object>, Rule> PARSE = + new Function<HashMap<String, Object>, Rule>() { + public Rule apply(final HashMap<String, Object> input) { + return Rule.parse(input); + } + }; + + /** + * Parse a string representation of a Rule into a Java POJO. + * + * @param str + * @return + */ + public static final Rule parse(final HashMap<String, Object> res) { + String name = (String) res.get("name"); + String desc = (String) res.get("description"); + String rule = res.get("rule").toString(); + int id = ((Long)res.get("id")).intValue(); + return new Rule(name, desc, rule, id); + } + + private final String _name; + private final String _desc; + private final String _rule; + private final int _id; + + public Rule(final String name, final String desc, final String rule, final int id) { + this._name = name; + this._desc = desc; + this._rule = rule; + this._id = id; + } + + public String getName() { + return _name; + } + + public String getDescription() { + return _desc; + } + + public String getRule() { + return _rule; + } + + public int getId() { + return _id; + } + + @Override + public String toString() { + Gson gson = new Gson(); + + return "{ 'id': " + gson.toJson(getId()) + ",\n " + + " 'name': " + gson.toJson(getName()) + ",\n" + + " 'description': " + gson.toJson(getDescription()) + ",\n" + + " 'ruleStr': " + gson.toJson(getRule()) + "\n" + + "}"; + } + + /** + * Equals and hashCode ignore the rule function text when performing comparisons. + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((_desc == null) ? 0 : _desc.hashCode()); + result = prime * result + _id; + result = prime * result + ((_name == null) ? 0 : _name.hashCode()); + return result; + } + + /** + * Equals and hashCode ignore the rule function text when performing comparisons. + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Rule other = (Rule) obj; + if (_desc == null) { + if (other._desc != null) + return false; + } else if (!_desc.equals(other._desc)) + return false; + if (_id != other._id) + return false; + if (_name == null) { + if (other._name != null) + return false; + } else if (!_name.equals(other._name)) + return false; + return true; + } +} diff --git a/rsTester/src/main/java/com/galois/fiveui/RuleSet.java b/rsTester/src/main/java/com/galois/fiveui/RuleSet.java new file mode 100644 index 0000000..ee2638c --- /dev/null +++ b/rsTester/src/main/java/com/galois/fiveui/RuleSet.java @@ -0,0 +1,162 @@ +/** + * Module : RuleSet.java Copyright : (c) 2011-2012, Galois, Inc. + * + * Maintainer : Stability : Provisional Portability: Portable + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.galois.fiveui; + +import java.util.HashMap; +import java.util.List; + +import org.openqa.selenium.htmlunit.HtmlUnitDriver; + +import com.google.common.base.Function; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.gson.Gson; + +public class RuleSet { + + /** + * Function wrapper around `RuleSet.parse` for use in `transform` and other + * functional combinators. + */ + public static final Function<String, RuleSet> PARSE = + new Function<String, RuleSet>() { + public RuleSet apply(String input) { + return RuleSet.parse(input); + } + }; + + /** + * Parse a string representation of a Rule Set into a Java POJO. + * + * TODO Extract out a js evaluation env. + * + * @param str + * @return + */ + @SuppressWarnings("unchecked") + public static final RuleSet parse(String str) { + HtmlUnitDriver driver = new HtmlUnitDriver(true); + String name = ""; + String desc = ""; + List<Rule> rules = Lists.newArrayList(); + HashMap<String, Object> res = null; + try { + driver.get("http://localhost:8000/test.html"); + res = + (HashMap<String, Object>) driver.executeScript("var x = " + + str + "; return x;"); + name = (String) res.get("name"); + desc = (String) res.get("description"); + List<HashMap<String, Object>> rawRules = + (List<HashMap<String, Object>>) res.get("rules"); + + rules = Lists.transform(rawRules, Rule.PARSE); + } finally { + driver.quit(); + } + return new RuleSet(name, desc, ImmutableList.copyOf(rules)); + } + + private final String _name; + private final String _description; + private final ImmutableList<Rule> _rules; + + public RuleSet(String name, String description, + ImmutableList<Rule> immutableList) { + _name = name; + _description = description; + _rules = immutableList; + } + + public String getName() { + return _name; + } + + public String getDescription() { + return _description; + } + + public ImmutableList<Rule> getRules() { + return _rules; + } + + @Override + public String toString() { + StringBuilder rules = new StringBuilder(); + for (Rule r : getRules()) { + if (0 != rules.length()) { + rules.append(",\n"); + } + rules.append(r.toString()); + } + Gson gson = new Gson(); + return "{ 'name': " + gson.toJson(getName()) + ", " + + " 'description': " + gson.toJson(getDescription()) + ", " + + " 'rules': [" + rules.toString() + "]" + + "}"; + } + + public Rule getRule(int ruleId) { + for (Rule rule : getRules()) { + if ( ruleId == rule.getId()) { + return rule; + } + } + return null; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((_description == null) ? 0 : _description.hashCode()); + result = prime * result + ((_name == null) ? 0 : _name.hashCode()); + result = prime * result + ((_rules == null) ? 0 : _rules.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + RuleSet other = (RuleSet) obj; + if (_description == null) { + if (other._description != null) + return false; + } else if (!_description.equals(other._description)) + return false; + if (_name == null) { + if (other._name != null) + return false; + } else if (!_name.equals(other._name)) + return false; + if (_rules == null) { + if (other._rules != null) + return false; + } else if (!_rules.equals(other._rules)) + return false; + return true; + } + + +} diff --git a/rsTester/src/main/java/com/galois/fiveui/RuleSetTester.java b/rsTester/src/main/java/com/galois/fiveui/RuleSetTester.java new file mode 100644 index 0000000..beea351 --- /dev/null +++ b/rsTester/src/main/java/com/galois/fiveui/RuleSetTester.java @@ -0,0 +1,90 @@ +/** + * Module : BatchExecutor.java Copyright : (c) 2011-2012, Galois, Inc. + * + * Maintainer : Stability : Provisional Portability: Portable + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.galois.fiveui; + +import java.io.IOException; +import java.net.URISyntaxException; + +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.firefox.FirefoxDriver; + +import com.google.common.collect.ImmutableList; + +/** + * The main entry point for testing Rule Sets. + * + * + * @author creswick + * + */ +public class RuleSetTester { + + /** + * @param args + * @throws IOException + * @throws URISyntaxException + */ + public static void main(final String[] args) throws IOException, + URISyntaxException { + System.out.println(args.length); + + if (0 == args.length) { + printHelp(); + System.exit(1); + } + + for (int i = 0; i < args.length; i++) { + String runDescFileName = args[i]; + RSTestDescription descr = RSTestDescription.parse(runDescFileName); + + for (WebDriver driver : getDrivers()) { + try { + ImmutableList<Result> results = invokeTest(descr, driver); + + for (Result result : results) { + System.out.println(result); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + driver.quit(); + } + } + } + } + + private static ImmutableList<WebDriver> getDrivers() { + return ImmutableList.of(Drivers.buildFFDriver() + // , Drivers.buildChromeDriver() + ); + } + + private static void printHelp() { + System.out + .println("Usage: RuleSetTester [<ruleSetTestDescirption.json>]"); + } + + private static ImmutableList<Result> invokeTest(RSTestDescription descr, + WebDriver driver) throws IOException { + BatchRunner runner = new BatchRunner(driver); + + return runner.runTests(descr.getTests()); + + } +} diff --git a/rsTester/src/main/java/com/galois/fiveui/RuleTest.java b/rsTester/src/main/java/com/galois/fiveui/RuleTest.java new file mode 100644 index 0000000..020f787 --- /dev/null +++ b/rsTester/src/main/java/com/galois/fiveui/RuleTest.java @@ -0,0 +1,70 @@ +/** + * Module : RuleTest.java + * Copyright : (c) 2011-2012, Galois, Inc. + * + * Maintainer : + * Stability : Provisional + * Portability: Portable + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.galois.fiveui; + +import java.net.URI; +import java.util.Collection; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMultiset; + +/** + * @author creswick + * + */ +public class RuleTest { + + private final URI _uri; + private final RuleSet _ruleSet; + private final int _ruleId; + private final ImmutableMultiset<ResType> _oracle; + + public RuleTest(URI uri, RuleSet ruleSet, int ruleId, Collection<ResType> oracle) { + _uri = uri; + _ruleSet = ruleSet; + _ruleId = ruleId; + _oracle = ImmutableMultiset.copyOf(oracle); + } + + public RuleSet getRule() { + RuleSet newRS = new RuleSet(_ruleSet.getName(), + _ruleSet.getDescription(), ImmutableList.of(_ruleSet.getRule(_ruleId))); + return newRS; + } + + public URI getUri() { + return _uri; + } + + public RuleSet getRuelSet() { + return _ruleSet; + } + + public int getRuleId() { + return _ruleId; + } + + public ImmutableMultiset<ResType> getOracle() { + return _oracle; + } + + +} diff --git a/rsTester/src/main/java/com/galois/fiveui/URITest.java b/rsTester/src/main/java/com/galois/fiveui/URITest.java new file mode 100644 index 0000000..5a0f920 --- /dev/null +++ b/rsTester/src/main/java/com/galois/fiveui/URITest.java @@ -0,0 +1,55 @@ +/** + * Module : URITest.java Copyright : (c) 2011-2012, Galois, Inc. + * + * Maintainer : Stability : Provisional Portability: Portable + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.galois.fiveui; + +import java.net.URI; +import java.util.List; + +import com.google.common.collect.ImmutableList; + +public class URITest { + + private URI uri; + private int id; + private List<ResType> oracle; + + /** + * Deserialization constructor. + */ + URITest() {} + +// public URITest(URI uri, RuleSet ruleSet, ResType oracle) { +// assert(1 == ruleSet.getRules().size()); +// +// this._uri = uri; +// this._ruleSet = ruleSet; +// this._oracle = oracle; +// } + + public URI getUri() { + return uri; + } + + public int getId() { + return id; + } + + public ImmutableList<ResType> getOracle() { + return ImmutableList.copyOf(oracle); + } +} diff --git a/rsTester/src/main/java/com/galois/fiveui/Utils.java b/rsTester/src/main/java/com/galois/fiveui/Utils.java new file mode 100644 index 0000000..b9e231c --- /dev/null +++ b/rsTester/src/main/java/com/galois/fiveui/Utils.java @@ -0,0 +1,39 @@ +/** + * Module : Utils.java Copyright : (c) 2011-2012, Galois, Inc. + * + * Maintainer : Stability : Provisional Portability: Portable + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.galois.fiveui; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import com.google.common.io.CharStreams; + +public class Utils { + + public static String readFile(String fileName) throws IOException { + InputStream in = new FileInputStream(new File(fileName)); + return readStream(in); + } + + public static String readStream(final InputStream stream) + throws IOException { + return CharStreams.toString(new InputStreamReader(stream)); + } +} diff --git a/rsTester/src/main/resources/javascript/ruleEval.js b/rsTester/src/main/resources/javascript/ruleEval.js new file mode 100644 index 0000000..04e81ba --- /dev/null +++ b/rsTester/src/main/resources/javascript/ruleEval.js @@ -0,0 +1,74 @@ +/** + * Module : ruleEval.js + * Copyright : (c) 2011-2012, Galois, Inc. + * + * Maintainer : + * Stability : Provisional + * Portability: Portable + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* Evaluate a rule. + * + * @param {!string} ruleStr The string representation of the rule (as + * a JavaScript Object literal). + * @return {?Array<Problem>} Empty if no problems were found or a string + * with an error if an exception occurred. + */ +var evaluate = function(ruleName, description, ruleStr) { + var theRule = null; + var results = []; + +// return [{ 'name': 'some rule', +// 'descr': 'some description', +// 'url': 'http:\/\/localhost:8000', +// 'severity': '1' +// }]; +//}; + + var report = function(name, node) { + var prob = { + 'name': name, + 'descr': description, + 'url': window.location.href, + 'severity': 1 + }; + + results.push(prob); + }; + + try { + eval('var theRule = '+ruleStr); + return theRule.toString(); + } catch (x) { +// console.log('could not load rule: '+ruleStr); +// console.log(x); + return "Error: "+x; + } + + var scope = { + name : ruleName, + description : description + }; + + if (theRule) { + try { + theRule.apply(scope); + } catch (x) { +// console.log('exception running rule: '+theRule.name); +// console.log(x); + return "Error: "+x; + } + } + return results; +}; diff --git a/rsTester/src/main/resources/seleniumDrivers/linux64/chromedriver b/rsTester/src/main/resources/seleniumDrivers/linux64/chromedriver Binary files differnew file mode 100755 index 0000000..b0a0e4a --- /dev/null +++ b/rsTester/src/main/resources/seleniumDrivers/linux64/chromedriver diff --git a/rsTester/src/test/java/com/galois/fiveui/BatchExecutorTest.java b/rsTester/src/test/java/com/galois/fiveui/BatchExecutorTest.java new file mode 100644 index 0000000..bb4dd31 --- /dev/null +++ b/rsTester/src/test/java/com/galois/fiveui/BatchExecutorTest.java @@ -0,0 +1,38 @@ +/** + * Module : BatchExecutorTest.java + * Copyright : (c) 2011-2012, Galois, Inc. + * + * Maintainer : + * Stability : Provisional + * Portability: Portable + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.galois.fiveui; + +import org.junit.Test; + +import junit.framework.Assert; + + +/** + * @author creswick + * + */ +public class BatchExecutorTest { + + @Test + public void simpleTest() { + Assert.assertEquals("Booleans are not equal.", true, true); + } +} diff --git a/rsTester/src/test/java/com/galois/fiveui/RuleSetTest.java b/rsTester/src/test/java/com/galois/fiveui/RuleSetTest.java new file mode 100644 index 0000000..1cd4dae --- /dev/null +++ b/rsTester/src/test/java/com/galois/fiveui/RuleSetTest.java @@ -0,0 +1,57 @@ +/** + * Module : RuleSetTest.java + * Copyright : (c) 2011-2012, Galois, Inc. + * + * Maintainer : + * Stability : Provisional + * Portability: Portable + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.galois.fiveui; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; + +import com.google.common.collect.ImmutableList; + +public class RuleSetTest { + @Test + public void testParseNoFns() { + testParse("the name", "a descr", "[]", new ArrayList<String>()); + } + + @Ignore + @Test + public void testParseOneFn() { + List<String> rules = ImmutableList.of("\n function () {\n }\n"); + testParse("the name", "a descr", "[function () {}]", rules); + } + + private void testParse(String name, String desc, String rules, List<String> rulesOracle) { + RuleSet rs = RuleSet.parse("{ 'name': '" +name+"'" + + ", 'description': '"+desc+"'" + + ", 'rules': " + rules + + "};"); + + assertEquals("", name, rs.getName()); + assertEquals("", desc, rs.getDescription()); + Assert.assertArrayEquals("", rulesOracle.toArray(), rs.getRules().toArray()); + } +} diff --git a/rsTester/src/test/java/com/galois/fiveui/RunDescriptionTest.java b/rsTester/src/test/java/com/galois/fiveui/RunDescriptionTest.java new file mode 100644 index 0000000..4c58f74 --- /dev/null +++ b/rsTester/src/test/java/com/galois/fiveui/RunDescriptionTest.java @@ -0,0 +1,82 @@ +/** + * RunDescriptionTest.java + * + * Copyright (c) 2012 Galois, Inc. + */ +package com.galois.fiveui; + +import static org.junit.Assert.fail; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.ArrayList; + +import junit.framework.Assert; + +import org.junit.Test; + +import com.google.common.collect.ImmutableList; + +/** + * @author creswick + * + */ +public class RunDescriptionTest { + + private static final String RUN_DESCRIPTION_DIR = "src/test/resources/runDescriptions/"; + + /** + * Test method for {@link com.galois.fiveui.RSTestDescription#RunDescription(java.lang.String, java.util.List, com.galois.fiveui.RuleSet)}. + * @throws FileNotFoundException + */ + @Test + public final void testDeserialize_sample0() throws FileNotFoundException { + + String jsonFileName = RUN_DESCRIPTION_DIR + "sample0.json"; + String ruleSetLoc = + RUN_DESCRIPTION_DIR + "../ruleSets/emptyRuleSet.json"; + + ImmutableList<Rule> emptyRuleList = ImmutableList.of(); + RuleSet rsOracle = + new RuleSet("emptyRuleSet", "", emptyRuleList); + + RSTestDescription oracle = + new RSTestDescription(ruleSetLoc, + new ArrayList<RSTestDescription.URIMap>(), rsOracle); + + + RSTestDescription actual = RSTestDescription.parse(jsonFileName); + assertObjEqual("Object deserialized incorrectly.", oracle, actual); + } + + /** + * Test method for {@link com.galois.fiveui.RSTestDescription#RunDescription(java.lang.String, java.util.List, com.galois.fiveui.RuleSet)}. + * @throws FileNotFoundException + */ + @Test + public final void testDeserialize_sample1() throws FileNotFoundException { + + String jsonFileName = RUN_DESCRIPTION_DIR + "sample1.json"; + String ruleSetLoc = + RUN_DESCRIPTION_DIR + "../ruleSets/simpleRuleSet1.json"; + + RuleSet rsOracle = + new RuleSet("simpleRuleSet1", "", ImmutableList.of( + new Rule("trivial check", "test desc", "", 42))); + + RSTestDescription oracle = + new RSTestDescription(ruleSetLoc, + new ArrayList<RSTestDescription.URIMap>(), rsOracle); + + + RSTestDescription actual = RSTestDescription.parse(jsonFileName); + assertObjEqual("Object deserialized incorrectly.", oracle, actual); + } + + private void assertObjEqual(String msg, Object oracle, Object actual) { + Assert.assertTrue(msg + "; expected: "+oracle+" actual: "+actual, + oracle.equals(actual)); + } + + +} diff --git a/rsTester/src/test/resources/ruleSets/emptyRuleSet.json b/rsTester/src/test/resources/ruleSets/emptyRuleSet.json new file mode 100644 index 0000000..a01bc68 --- /dev/null +++ b/rsTester/src/test/resources/ruleSets/emptyRuleSet.json @@ -0,0 +1,4 @@ +{ "name": "emptyRuleSet" +, "description": "" +, "rules": [] +} diff --git a/rsTester/src/test/resources/ruleSets/headingGuidelines.json b/rsTester/src/test/resources/ruleSets/headingGuidelines.json new file mode 100644 index 0000000..be69fac --- /dev/null +++ b/rsTester/src/test/resources/ruleSets/headingGuidelines.json @@ -0,0 +1,37 @@ +{ "name": "Heading Guidelines" +, "description": "Guidelines pertaining to the formatting and content of headings." +, "rules": [ { "id": 1 + , "name": "Headings are capitalized" + , "description": "Check to see if all headings use leading capital letters." + , "rule": + function() { + var badHeadings = + fiveui.query(':header').filter( + function(idx) { + var ch = $(this).text()[0]; + if (ch) { + return (ch == ch.toLowerCase() ); + } else { + return false; + } + }); + $(badHeadings).map(function(idx, elt){ + report('Heading does not start with a capitol letter.', elt); + }); + + } + }, + { "id": 2 + , "name": "Disallow Empty Headers" + , "description": "Heading elements should contain text." + , "rule": function() { + fiveui.query(':header').each( + function(ix, elt) { + if($(elt).text() == '') { + report('Heading does not contain text', elt); + } + }); + } + } + ] +}
\ No newline at end of file diff --git a/rsTester/src/test/resources/ruleSets/simpleRuleSet1.json b/rsTester/src/test/resources/ruleSets/simpleRuleSet1.json new file mode 100644 index 0000000..8365d1d --- /dev/null +++ b/rsTester/src/test/resources/ruleSets/simpleRuleSet1.json @@ -0,0 +1,9 @@ +{ "name": "simpleRuleSet1" +, "description": "" +, "rules": [ { "id": 42 + , "name": "trivial check" + , "description": "test desc" + , "rule": + function() { } + }] +} diff --git a/rsTester/src/test/resources/runDescriptions/headingSample.json b/rsTester/src/test/resources/runDescriptions/headingSample.json new file mode 100644 index 0000000..e8228ca --- /dev/null +++ b/rsTester/src/test/resources/runDescriptions/headingSample.json @@ -0,0 +1,13 @@ +{ + 'ruleSet': '../ruleSets/headingGuidelines.json', + 'tests': [ { 'url': 'http://localhost:8000/exampleData/basic/headings.html', + 'oracle': [ { 'ruleId': 1 + , 'results': ['Error', 'Error'] + }, + { 'ruleId': 2 + , 'results': ['Error'] + } + ] + } + ] +} diff --git a/rsTester/src/test/resources/runDescriptions/sample0.json b/rsTester/src/test/resources/runDescriptions/sample0.json new file mode 100644 index 0000000..0c2bbb7 --- /dev/null +++ b/rsTester/src/test/resources/runDescriptions/sample0.json @@ -0,0 +1,4 @@ +{ + 'ruleSet': '../ruleSets/emptyRuleSet.json', + 'tests': [] +} diff --git a/rsTester/src/test/resources/runDescriptions/sample1.json b/rsTester/src/test/resources/runDescriptions/sample1.json new file mode 100644 index 0000000..91f8f34 --- /dev/null +++ b/rsTester/src/test/resources/runDescriptions/sample1.json @@ -0,0 +1,4 @@ +{ + 'ruleSet': '../ruleSets/simpleRuleSet1.json', + 'tests': [] +} diff --git a/rsTester/src/test/resources/runDescriptions/sample2.json b/rsTester/src/test/resources/runDescriptions/sample2.json new file mode 100644 index 0000000..a8ea9ec --- /dev/null +++ b/rsTester/src/test/resources/runDescriptions/sample2.json @@ -0,0 +1,10 @@ +{ + 'ruleSet': '../ruleSets/simpleRuleSet1.json', + 'tests': [ { 'url': 'http://localhost:8000/', + 'oracle': [ { 'ruleId': 42 + , 'results': ['Error', 'Error'] + } + ] + } + ] +} diff --git a/rsTester/src/test/resources/runDescriptions/sample3.json b/rsTester/src/test/resources/runDescriptions/sample3.json new file mode 100644 index 0000000..e4d1cea --- /dev/null +++ b/rsTester/src/test/resources/runDescriptions/sample3.json @@ -0,0 +1,7 @@ +{ + 'ruleSet': '../ruleSets/simpleRuleSet1.json', + 'tests': [ { 'url': 'http://localhost:8000/', + 'oracle': [ ] + } + ] +} diff --git a/rsTester/src/test/resources/runDescriptions/sample4.json b/rsTester/src/test/resources/runDescriptions/sample4.json new file mode 100644 index 0000000..c4f46cd --- /dev/null +++ b/rsTester/src/test/resources/runDescriptions/sample4.json @@ -0,0 +1,10 @@ +{ + 'ruleSet': '../ruleSets/simpleRuleSet1.json', + 'tests': [ { 'url': 'http://localhost:8000/', + 'oracle': [ ] + }, + { 'url': 'http://localhost:8000/', + 'oracle': [ ] + } + ] +} diff --git a/rsTester/src/test/resources/runDescriptions/sample5.json b/rsTester/src/test/resources/runDescriptions/sample5.json new file mode 100644 index 0000000..11ca957 --- /dev/null +++ b/rsTester/src/test/resources/runDescriptions/sample5.json @@ -0,0 +1,13 @@ +{ + 'ruleSet': '../ruleSets/simpleRuleSet1.json', + 'tests': [ { 'url': 'http://localhost:8000/', + 'oracle': [ { 'ruleId': 42 + , 'results': ['Error'] + } + ] + }, + { 'url': 'http://localhost:8000/', + 'oracle': [ ] + } + ] +} |