blob: 8a6eab447ccde2feb0884b0489971713e428c01b [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* 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 util.build;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Scanner;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Helper base class for code generators.
*/
public abstract class BuildUtilBase {
public static boolean DEBUG = true;
public static class MethodData {
String methodBody, constraint, title;
}
public interface TestHandler {
public void handleTest(String fqcn, List<String> methods);
}
public void run(TestHandler handler) {
System.out.println("Collecting all junit tests...");
JUnitTestCollector tests = new JUnitTestCollector(getClass().getClassLoader());
handleTests(tests, handler);
}
protected void handleTests(JUnitTestCollector tests, TestHandler handler) {
System.out.println("collected " + tests.testMethodsCnt + " test methods in " +
tests.testClassCnt + " junit test classes");
for (Entry<String, List<String>> entry : tests.map.entrySet()) {
handler.handleTest(entry.getKey(), entry.getValue());
}
}
private static String readURL(URL in) {
// Use common scanner idiom to read a complete InputStream into a string.
try (Scanner scanner = new Scanner(in.openStream(), StandardCharsets.UTF_8.toString())) {
scanner.useDelimiter("\\A"); // This delimits by "start of content," of which there is
// only one.
return scanner.hasNext() ? scanner.next() : null;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
protected MethodData parseTestMethod(String pname, String classOnlyName,
String method) {
String searchPath = "src/" + pname.replaceAll("\\.", "/") + "/" + classOnlyName + ".java";
String content;
{
URL resource = getClass().getClassLoader().getResource(searchPath);
if (resource == null) {
throw new RuntimeException("Could not find " + searchPath);
}
content = readURL(resource);
if (content == null) {
throw new RuntimeException("Could not retrieve content for " + searchPath);
}
}
final String methodPattern = "public\\s+void\\s+" + method + "[^\\{]+\\{";
int methodSkip;
try (Scanner scanner = new Scanner(content)) {
String token = scanner.findWithinHorizon(methodPattern, content.length());
if (token == null) {
throw new RuntimeException("cannot find method source of 'public void " + method +
"' in file '" + searchPath + "'");
}
MatchResult result = scanner.match();
result.start();
methodSkip = result.end();
}
StringBuilder builder = new StringBuilder();
try {
StringReader reader = new StringReader(content);
reader.skip(methodSkip);
int readResult;
int blocks = 1;
while ((readResult = reader.read()) != -1 && blocks > 0) {
char currentChar = (char) readResult;
switch (currentChar) {
case '}': {
blocks--;
builder.append(currentChar);
break;
}
case '{': {
blocks++;
builder.append(currentChar);
break;
}
default: {
builder.append(currentChar);
break;
}
}
}
if (reader != null) {
reader.close();
}
} catch (Exception e) {
throw new RuntimeException("failed to parse", e);
}
// find the @title/@constraint in javadoc comment for this method
// using platform's default charset
// System.out.println("grepping javadoc found for method " + method +
// " in " + pname + "," + classOnlyName);
String commentPattern = "/\\*\\*([^{]*)\\*/\\s*" + methodPattern;
Pattern p = Pattern.compile(commentPattern, Pattern.DOTALL);
Matcher m = p.matcher(content);
String title = null, constraint = null;
if (m.find()) {
String res = m.group(1);
// System.out.println("res: " + res);
// now grep @title and @constraint
Matcher titleM = Pattern.compile("@title (.*)", Pattern.DOTALL)
.matcher(res);
if (titleM.find()) {
title = titleM.group(1).replaceAll("\\n \\*", "");
title = title.replaceAll("\\n", " ");
title = title.trim();
// System.out.println("title: " + title);
} else {
System.err.println("warning: no @title found for method " + method + " in " + pname +
"," + classOnlyName);
}
// constraint can be one line only
Matcher constraintM = Pattern.compile("@constraint (.*)").matcher(
res);
if (constraintM.find()) {
constraint = constraintM.group(1);
constraint = constraint.trim();
// System.out.println("constraint: " + constraint);
} else if (method.contains("VFE")) {
System.err
.println("warning: no @constraint for for a VFE method:" + method + " in " +
pname + "," + classOnlyName);
}
} else {
System.err.println("warning: no javadoc found for method " + method + " in " + pname +
"," + classOnlyName);
}
MethodData md = new MethodData();
md.methodBody = builder.toString();
md.constraint = constraint;
md.title = title;
return md;
}
/**
* @param pName
* @param classOnlyName
* @param methodSource
* @return testclass names
*/
protected static List<String> parseTestClassName(String pName, String classOnlyName,
String methodSource) {
List<String> entries = new ArrayList<String>(2);
String opcodeName = classOnlyName.substring(5);
try (Scanner scanner = new Scanner(methodSource)) {
String[] patterns = new String[] { "new\\s(T_" + opcodeName + "\\w*)",
"(T_" + opcodeName + "\\w*)", "new\\s(T\\w*)" };
String token = null;
for (String pattern : patterns) {
token = scanner.findWithinHorizon(pattern, methodSource.length());
if (token != null) {
break;
}
}
if (token == null) {
System.err.println("warning: failed to find dependent test class name: " + pName
+ ", " + classOnlyName + " in methodSource:\n" + methodSource);
return entries;
}
MatchResult result = scanner.match();
entries.add((pName + ".d." + result.group(1)).trim());
// search additional @uses directives
Pattern p = Pattern.compile("@uses\\s+(.*)\\s+", Pattern.MULTILINE);
Matcher m = p.matcher(methodSource);
while (m.find()) {
String res = m.group(1);
entries.add(0, res.trim());
}
// search for " load(\"...\" " and add as dependency
Pattern loadPattern = Pattern.compile("load\\(\"([^\"]*)\"", Pattern.MULTILINE);
Matcher loadMatcher = loadPattern.matcher(methodSource);
while (loadMatcher.find()) {
String res = loadMatcher.group(1);
entries.add(res.trim());
}
// search for " loadAndRun(\"...\" " and add as dependency
Pattern loadAndRunPattern = Pattern.compile("loadAndRun\\(\"([^\"]*)\"",
Pattern.MULTILINE);
Matcher loadAndRunMatcher = loadAndRunPattern.matcher(methodSource);
while (loadAndRunMatcher.find()) {
String res = loadAndRunMatcher.group(1);
entries.add(res.trim());
}
// lines with the form @uses
// dot.junit.opcodes.add_double.jm.T_add_double_2
// one dependency per one @uses
// TODO
return entries;
}
}
public static void writeToFileMkdir(File file, String content) {
File parent = file.getParentFile();
if (!parent.exists() && !parent.mkdirs()) {
throw new RuntimeException("failed to create directory: " + parent.getAbsolutePath());
}
writeToFile(file, content);
}
public static void writeToFile(File file, String content) {
try {
if (file.exists() && file.length() == content.length()) {
FileReader reader = new FileReader(file);
char[] charContents = new char[(int) file.length()];
reader.read(charContents);
reader.close();
String contents = new String(charContents);
if (contents.equals(content)) {
// System.out.println("skipping identical: "
// + file.getAbsolutePath());
return;
}
}
//System.out.println("writing file " + file.getAbsolutePath());
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(file), "utf-8"));
bw.write(content);
bw.close();
} catch (Exception e) {
throw new RuntimeException("error while writing to file: " + e.getClass().getName() +
", msg:" + e.getMessage());
}
}
}