/*
* 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.google.devtools.j2objc.regression;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.devtools.j2objc.GenerationTest;
import com.google.devtools.j2objc.J2ObjC;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.List;
/**
* Basic framework for working with Eclipse regression tests. Eclipse has built a very large set of
* user driven test data, which if we leverage for new features allows us to quickly identify areas
* where we have bugs, features we are not supporting, and, if we look back from the tests to the
* original bug descriptions, an idea of how users are using tested features. A good example of this
* is intersection types, which are currently hard to track down examples for, but are covered well
* in LambdaExpressionsTest.
* <p>
* For testing current features this will probably not be as useful, as it will be easy to get lost
* fixing bug minutia rather than implementing used features, and the model of j2objc has been to
* let our users be our bumpers, and point out which features they need, allowing us to only focus
* on the used parts of the language, rather than trying to match the JLS directly for completeness.
* <p>
* We can find Eclipse's repository of regression tests here:
* https://github.com/eclipse/eclipse.jdt.core/tree/master/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression
*
* @author Seth Kirby
*/
public abstract class AbstractRegressionTest extends GenerationTest {
String methodName = null;
public static int testCount = 0;
static final String J2OBJCC_LOCATION = System.getProperty("j2objcc.path", "j2objcc");
public String writeFileFromString(String filename, String content) {
PrintWriter out;
File file = new File(tempDir, filename);
try {
new File(file.getParent()).mkdirs();
out = new PrintWriter(file, "UTF-8");
out.print(content);
out.close();
} catch (IOException e) {
fail("Unable to write file " + filename);
}
return file.getAbsolutePath();
}
public List<String> writeFiles(String[] ls) {
List<String> fileArgs = Lists.newArrayListWithCapacity(ls.length / 2);
for (int i = 0; i < ls.length; i += 2) {
fileArgs.add(writeFileFromString(ls[i], ls[i + 1]));
}
return fileArgs;
}
/**
* Takes a list of .java files and returns a list with the .java extension replaced with .m.
*/
public List<String> getImplementationFileList(List<String> fileArgs) {
List<String> mFileArgs = Lists.newArrayListWithCapacity(fileArgs.size());
for (String s : fileArgs) {
String newString = s.substring(0, s.lastIndexOf('/'));
newString += s.substring(s.lastIndexOf('/'), s.lastIndexOf('.')) + ".m";
mFileArgs.add(newString);
}
return mFileArgs;
}
/**
* Takes a .java file name and possibly path and returns the name of the represented class.
*/
public String className(String path) {
int pathIndex = path.lastIndexOf('/');
int extensionIndex = path.lastIndexOf('.');
return path.substring(Math.max(0, pathIndex), extensionIndex);
}
public String runCommand(String command) {
Runtime rt = Runtime.getRuntime();
Process process;
try {
process = rt.exec(command);
BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream()));
BufferedReader stdError = new BufferedReader(new InputStreamReader(process.getErrorStream()));
StringBuilder buffer = new StringBuilder();
int c;
while ((c = stdInput.read()) != -1) {
buffer.append((char) c);
}
while ((c = stdError.read()) != -1) {
buffer.append((char) c);
}
stdInput.close();
stdError.close();
return buffer.toString();
} catch (IOException e) {
return null;
}
}
public void regressionFail(String[] ls, String res, String output, List<String> mFileArgs) {
StringBuilder buffer = new StringBuilder();
buffer.append('\n' + methodName);
buffer.append("\nExpected:\n");
buffer.append(res);
buffer.append("\nReturned:\n");
if (output == null) {
buffer.append("***Failed to compile***");
} else {
buffer.append(output);
}
buffer.append("\n\n");
for (int i = 0; i < ls.length; i += 2) {
buffer.append("FILE:\n");
buffer.append(ls[i]);
buffer.append('\n');
buffer.append(ls[i + 1]);
buffer.append('\n');
}
System.out.println("FAIL");
System.out.println(buffer.toString());
fail(buffer.toString());
}
public void checkMatch(String[] ls, String res, String output, List<String> mFileArgs) {
res = res.trim();
if (output != null) {
output = output.trim();
}
if (!res.equals(output)) {
regressionFail(ls, res, output, mFileArgs);
}
}
public void runConformTest(String[] ls) {
runConformTest(ls, null);
}
public void runConformTest(String[] ls, String res) {
StackTraceElement[] st = Thread.currentThread().getStackTrace();
methodName = st[2].getMethodName();
List<String> fileArgs = writeFiles(ls);
J2ObjC.run(fileArgs, options);
List<String> mFileArgs = getImplementationFileList(fileArgs);
String command = J2OBJCC_LOCATION + " -g -I. -ObjC -o " + tempDir + "/regressiontesting "
+ Joiner.on(' ').join(mFileArgs);
String compileOutput = runCommand(command);
if (compileOutput.indexOf("error: ") != -1) {
regressionFail(ls, res, compileOutput, mFileArgs);
}
if (res != null) {
checkMatch(ls, res, runCommand(tempDir + "/regressiontesting " + className(ls[0])),
mFileArgs);
runCommand("rm " + tempDir + "/regressiontesting");
}
}
void runNegativeTest(String[] ls, String res) {
fail("Negative tests not supported (or needed)");
}
}