blob: d9b7433d196c8d9570cccc3747491e54e0caf9c9 [file] [log] [blame]
/*
* Copyright 2003-2009 the original author or authors.
*
* 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 groovy.util
import java.util.regex.Pattern
/**
* <code>JavadocAssertionTestBuilder</code> will dynamically create a test cases from Groovy assertions placed within
* the Javadoc comments of a source file. Assertions should be placed within an html tag with a <code>class="groovyTestCase"</code>
* attribute assignment. Example:
* <pre>&lt;pre class="groovyTestCase"&gt; assert "example".size() == 7 &lt;/pre&gt;</pre>
*
* @author Merlyn Albery-Speyer
*/
class JavadocAssertionTestBuilder {
private static Pattern javadocPattern =
Pattern.compile( /(?ims)\/\*\*.*?\*\// )
private static Pattern assertionPattern =
Pattern.compile( /(?ims)<([a-z]+)\s+class\s*=\s*['"]groovyTestCase['"]\s*>.*?<\s*\/\s*\1>/ )
Class buildTest(String filename, String code) {
Class test = null
List assertionTags = getAssertionTags(code);
if (assertionTags) {
String testName = getTestName(filename)
Map lineNumberToAssertions = getLineNumberToAssertionsMap(code, assertionTags)
List testMethods = getTestMethods(lineNumberToAssertions, filename)
String testCode = getTestCode(testName, testMethods)
test = createClass(testCode)
}
return test
}
private List getAssertionTags(String code) {
List assertions = new ArrayList()
code.eachMatch(javadocPattern) { javadoc ->
assertions.addAll(javadoc.findAll(assertionPattern))
}
return assertions
}
private String getTestName(String filename) {
String filenameWithoutPath = new File(filename).name
String testName = filenameWithoutPath.substring(0, filenameWithoutPath.lastIndexOf(".")) +
"JavadocAssertionTest"
return testName
}
private Map getLineNumberToAssertionsMap(String code, List assertionTags) {
Map lineNumberToAssertions = [:] as LinkedHashMap
int codeIndex = 0
assertionTags.each { tag ->
codeIndex = code.indexOf(tag, codeIndex)
int lineNumber = code.substring(0, codeIndex).findAll("(?m)^").size()
codeIndex += tag.size()
String assertion = getAssertion(tag)
lineNumberToAssertions.get(lineNumber, []) << assertion
}
return lineNumberToAssertions
}
private String getAssertion(String tag) {
String tagInner = tag.substring(tag.indexOf(">")+1, tag.lastIndexOf("<"))
String htmlAssertion = tagInner.replaceAll("(?m)^\\s*\\*", "")
String assertion = htmlAssertion
// TODO improve on this
[nbsp:' ', gt:'>', lt:'<', quot:'"', apos:"'", at:'@', ndash:'-', amp:'&'].each { key, value ->
assertion = assertion.replaceAll("(?i)&$key;", value)
}
return assertion
}
private List getTestMethods(Map lineNumberToAssertions, String filename) {
List testMethods = lineNumberToAssertions.collect { lineNumber, assertions ->
Character differentiator = 'a'
assertions.collect { assertion ->
String suffix = (assertions.size() > 1 ? "$lineNumber$differentiator" : lineNumber)
differentiator++
getTestMethodCodeForAssertion(suffix, assertion, basename(filename))
}
}.flatten()
return testMethods
}
private String basename(String fullPath) {
def path = new File(fullPath)
def fullName = path.name
fullName.substring(0, fullName.lastIndexOf("."))
}
private String getTestMethodCodeForAssertion(String suffix, String assertion, String basename) {
return """
public void testAssertionFrom${basename}Line$suffix() {
byte[] bytes = [ ${(assertion.getBytes("UTF-8") as List).join(", ")} ] as byte[]
Eval.me(new String(bytes, "UTF-8"))
}
"""
}
private String getTestCode(String testName, List testMethods) {
return """
class $testName extends junit.framework.TestCase {
"""+testMethods.join("\r\n")+"""
}
"""
}
private Class createClass(String testCode) {
return new GroovyClassLoader().parseClass(testCode)
}
}