/**
* Catroid: An on-device visual programming system for Android devices
* Copyright (C) 2010-2013 The Catrobat Team
* (<http://developer.catrobat.org/credits>)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* An additional term exception under section 7 of the GNU Affero
* General Public License, version 3, is available at
* http://developer.catrobat.org/license_additional_term
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.catrobat.musicdroid.test.code;
import junit.framework.TestCase;
import org.catrobat.musicdroid.test.utils.Utils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class AssertionErrorMessageTest extends TestCase {
private static final String[] DIRECTORIES = { ".", "../Musicdroid", "../MusicdroidTest", "../MusicdroidUiTest" };
private static final String OPENING_BRACKET = "\\(";
private static final String CLOSING_BRACKET = "\\)";
private static final String WHITESPACES = "(\\s)*";
private static final String ANYTHING = ".*";
private static final String STRING_LITERAL = "\"[^\"]*\"";
private static final String STRING_LITERAL_NOT_EMPTY = "\"[^\"]+\"";
private static final String COMMENT = "/\\*[^\\*/]\\*/";
private static final String NON_STRING_NON_COMMA = "[^\",]";
private static final String METHOD_CALL = "[a-zA-Z0-9_]+" + OPENING_BRACKET + ".*" + CLOSING_BRACKET;
private static final String PARAMETER = "(" + STRING_LITERAL + "|" + METHOD_CALL + "|" + NON_STRING_NON_COMMA
+ ")+";
private static final String ASSERT_MESSAGE = PARAMETER.replace(STRING_LITERAL, STRING_LITERAL_NOT_EMPTY);
private static final String NOT_A_NUMBER = "([^(\\d\\.\\-)])";
private static final String COMMA = ",";
private static final String LINE_COMMENT = "//.*";
private class AssertMethod {
private String commandName;
private int numberOfParameters;
public AssertMethod(String commandName, int numberOfParameters) {
this.commandName = commandName;
this.numberOfParameters = numberOfParameters;
}
public String getCommandName() {
return commandName;
}
public int getNumberOfParameters() {
return numberOfParameters;
}
}
private List<AssertMethod> assertMethods;
private String regexIsAssertMethod;
private String regexAssertContainsErrorMessage;
private String regexAssertDoesntStartWithNumber;
private String regexIsCompleteCommand;
private String errorMessages;
private boolean errorFound;
public AssertionErrorMessageTest() {
/*
* All JUnit assert commands, along with the number of parameters IF an error message is included,
* taken from http://www.junit.org/apidocs/org/junit/Assert.html and
* http://www.junit.org/apidocs/junit/framework/Assert.html as of JUnit version 4.9b2
*/
assertMethods = new ArrayList<AssertionErrorMessageTest.AssertMethod>();
assertMethods.add(new AssertMethod("assertArrayEquals", 3));
assertMethods.add(new AssertMethod("assertEquals", 3));
assertMethods.add(new AssertMethod("assertEquals", 4));
assertMethods.add(new AssertMethod("assertFalse", 2));
assertMethods.add(new AssertMethod("assertNotNull", 2));
assertMethods.add(new AssertMethod("assertNotSame", 3));
assertMethods.add(new AssertMethod("assertNull", 2));
assertMethods.add(new AssertMethod("assertSame", 3));
assertMethods.add(new AssertMethod("assertThat", 3));
assertMethods.add(new AssertMethod("assertTrue", 2));
assertMethods.add(new AssertMethod("fail", 1));
// Build regular expressions to check if a String is an assert method
regexIsAssertMethod = "";
for (int i = 0; i < assertMethods.size(); i++) {
regexIsAssertMethod += "(" + WHITESPACES + assertMethods.get(i).getCommandName() + ANYTHING + ")";
if (i < assertMethods.size() - 1) {
regexIsAssertMethod += "|";
}
}
// Build regular expression to check if an assert method contains an valid error message
regexAssertContainsErrorMessage = "";
for (int i = 0; i < assertMethods.size(); i++) {
regexAssertContainsErrorMessage += "(" + WHITESPACES + assertMethods.get(i).getCommandName()
+ OPENING_BRACKET;
regexAssertContainsErrorMessage += ASSERT_MESSAGE;
for (int parameterCount = 1; parameterCount < assertMethods.get(i).getNumberOfParameters(); parameterCount++) {
regexAssertContainsErrorMessage += COMMA + PARAMETER;
}
regexAssertContainsErrorMessage += CLOSING_BRACKET + ANYTHING + ")";
if (i < assertMethods.size() - 1) {
regexAssertContainsErrorMessage += "|";
}
}
// Build regular expression to check if an assert message starts with a number
regexAssertDoesntStartWithNumber = "";
for (int i = 0; i < assertMethods.size(); i++) {
regexAssertDoesntStartWithNumber += "(" + WHITESPACES + assertMethods.get(i).getCommandName()
+ OPENING_BRACKET + NOT_A_NUMBER + ANYTHING + ")";
if (i < assertMethods.size() - 1) {
regexAssertDoesntStartWithNumber += "|";
}
}
// Build regular expression to check if a command is complete (i.e. not one line of a multi-line command)
regexIsCompleteCommand = "(" + STRING_LITERAL + "|" + COMMENT + "|[^;]" + ")*;" + WHITESPACES + "("
+ LINE_COMMENT + ")?";
}
/** Test that tests the regular expressions used in the actual test (meta-test :)) */
public void testRegex() {
List<String> matchingAsserts = new ArrayList<String>();
matchingAsserts.add("assertTrue(\"message\", parameter);");
matchingAsserts.add("assertTrue(iAmAString, parameter);");
matchingAsserts.add("assertFalse(\"message\", parameter); // comment");
matchingAsserts.add("assertEquals(\"message\", a, b);");
matchingAsserts.add("assertTrue(\"message, with a comma\", value);");
matchingAsserts.add("assertTrue(name + \" has wrong value, but...\", value);");
matchingAsserts.add("fail(\"epic fail\");");
matchingAsserts.add("assertTrue(getErrorMessage(a, b, c), value);");
matchingAsserts.add("assertEquals(\"Foo!\", bar, baz(), 1e-3);");
for (String matchingAssert : matchingAsserts) {
assertTrue(
"Regex didn't match expression " + matchingAssert,
matchingAssert.matches(regexAssertContainsErrorMessage)
&& matchingAssert.matches(regexAssertDoesntStartWithNumber));
}
List<String> notMatchingAsserts = new ArrayList<String>();
notMatchingAsserts.add("assertTrue(parameter);");
notMatchingAsserts.add("assertTrue((name + \"text\").equals(value));");
notMatchingAsserts.add("assertTrue(text.equals(\"a,b\"));");
notMatchingAsserts.add("assertTrue(/* Comment; evil */ value);");
notMatchingAsserts.add("assertTrue(\"\", true)");
notMatchingAsserts.add("assertEquals(\"a\", \"b\");");
notMatchingAsserts.add("assertEquals(a, b)");
notMatchingAsserts.add("assertEquals(1.0, 1.0, 1.0);");
notMatchingAsserts.add("fail();");
for (String notMatchingAssert : notMatchingAsserts) {
assertFalse(
"Expression was matched even though it shouldn't: " + notMatchingAssert,
notMatchingAssert.matches(regexAssertContainsErrorMessage)
&& notMatchingAssert.matches(regexAssertDoesntStartWithNumber));
}
List<String> completeCommands = new ArrayList<String>();
completeCommands.add("method();");
completeCommands.add("method(param);");
completeCommands.add("method(\"text;\");");
for (String completeCommand : completeCommands) {
assertTrue("Regex for complete commands did not match command: " + completeCommand,
completeCommand.matches(regexIsCompleteCommand));
}
List<String> incompleteCommands = new ArrayList<String>();
incompleteCommands.add("method()");
incompleteCommands.add("method(");
incompleteCommands.add("method(\";\"");
incompleteCommands.add("method(/*;*/");
// TODO: Incorporate this one; don't know how to exclude "//" from regex
// incompleteCommands.add("method(//;");
for (String incompleteCommand : incompleteCommands) {
assertFalse("Regex for complete commands matched incomplete command: " + incompleteCommand,
incompleteCommand.matches(regexIsCompleteCommand));
}
}
public void assertionErrorMessagesPresentInFile(File file) throws IOException {
assertTrue("Could not read file " + file.getAbsolutePath(), file.exists() && file.canRead());
BufferedReader reader = new BufferedReader(new FileReader(file));
String currentLine = "";
int lineNumber = 0;
while ((currentLine = reader.readLine()) != null) {
lineNumber++;
if (currentLine.matches(regexIsAssertMethod)) {
while (!currentLine.matches(regexIsCompleteCommand)) {
currentLine += reader.readLine();
lineNumber++;
}
currentLine.replace("\n", "");
if (!currentLine.matches(regexAssertContainsErrorMessage)
|| !currentLine.matches(regexAssertDoesntStartWithNumber)) {
errorFound = true;
errorMessages += (file.getCanonicalPath() + " in line " + lineNumber + "\n");
}
}
}
reader.close();
}
public void testAssertionErrorMessagesPresent() throws IOException {
errorMessages = "";
errorFound = false;
for (String directoryName : DIRECTORIES) {
File directory = new File(directoryName);
assertTrue("Couldn't find directory: " + directoryName, directory.exists() && directory.isDirectory());
assertTrue("Couldn't read directory: " + directoryName, directory.canRead());
List<File> filesToCheck = Utils.getFilesFromDirectoryByExtension(directory, ".java");
for (File file : filesToCheck) {
assertionErrorMessagesPresentInFile(file);
}
}
assertFalse("Assert statements without error messages have been found in the following files: \n"
+ errorMessages, errorFound);
}
}