Skip to content

Commit

Permalink
Merge io streams in tests (#707)
Browse files Browse the repository at this point in the history
* Format in LFTest

* Try merging stdout & stderr

Not sure this is a good way to do it, maybe
output will be torn...

* Minor fixes

* Fix compilation error

* Updated submodule

Co-authored-by: Marten Lohstroh <marten@berkeley.edu>
  • Loading branch information
oowekyala and lhstrh authored Nov 7, 2021
1 parent 8fcd6fc commit 11570eb
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 122 deletions.
215 changes: 95 additions & 120 deletions org.lflang.tests/src/org/lflang/tests/LFTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.nio.file.Path;
import java.nio.file.Paths;
Expand All @@ -18,126 +19,40 @@
*
*/
public class LFTest implements Comparable<LFTest> {

/**
* Inner class for capturing streams during execution of a test, capable of
* recording output streams up until the moment that a test is interrupted
* upon timing out.
*
* @author Marten Lohstroh <marten@berkeley.edu>
*
*/
public static class ExecutionLogger {

/**
* String buffer used to record the standard output stream.
*/
final StringBuffer std = new StringBuffer();

/**
* String builder used to record the standard error stream.
*/
final StringBuffer err = new StringBuffer();

/**
* Return a thread responsible for recording the standard output stream
* of the given process.
* A separate thread is used so that the activity can preempted.
*/
public Thread recordStdOut(Process process) {
return recordStream(std, process.getInputStream());
}

/**
* Return a thread responsible for recording the error stream of the
* given process.
* A separate thread is used so that the activity can preempted.
*/
public Thread recordStdErr(Process process) {
return recordStream(err, process.getErrorStream());
}

/**
* Return a thread responsible for recording the given stream.
*
* @param builder The builder to append to.
* @param inputStream The stream to read from.
*/
private Thread recordStream(StringBuffer builder, InputStream inputStream) {
Thread t = new Thread(() -> {
try (Reader reader = new InputStreamReader(inputStream)) {
int len;
char[] buf = new char[1024];
while ((len = reader.read(buf)) > 0) {
builder.append(buf, 0, len);
}
} catch (Exception e) {
builder.append("[truncated...]\n");
}
});
t.start();
return t;
}
}

/**
* The path to the test.
*/
/** The path to the test. */
public final Path srcFile;

/**
* The name of the test.
*/
/** The name of the test. */
public final String name;

/**
* The result of the test.
* @see Result
*/

/** The result of the test. */
public Result result = Result.UNKNOWN;

/**
* Object used to determine where the code generator puts files.
*/

/** Object used to determine where the code generator puts files. */
public FileConfig fileConfig;

/**
* Path of the test program relative to the the package root.
*/
private final Path relativePath;

/**
* Stream object for capturing standard output.
*/
public ByteArrayOutputStream out = new ByteArrayOutputStream();
/** Path of the test program relative to the package root. */
private final Path relativePath;

/**
* Stream object for capturing standard error output.
*/
public ByteArrayOutputStream err = new ByteArrayOutputStream();
/** Records compilation stdout/stderr. */
private final ByteArrayOutputStream compilationLog = new ByteArrayOutputStream();

/**
* Specialized object for capturing output streams while executing the test.
*/
public ExecutionLogger execLog = new ExecutionLogger();
/** Specialized object for capturing output streams while executing the test. */
public final ExecutionLogger execLog = new ExecutionLogger();

/**
* String builder for collecting issues encountered during test executeion.
*/
public StringBuilder issues = new StringBuilder();
/** String builder for collecting issues encountered during test execution. */
public final StringBuilder issues = new StringBuilder();

/**
* The target of the test program.
*/
/** The target of the test program. */
public final Target target;

/**
* The path of the package root of the test program.
*/
/** The path of the package root of the test program. */
public final Path packageRoot;

/**
* Create a new test.
*
* @param target The target of the test program.
* @param srcFile The path to the file of the test program.
* @param packageRoot The path of the package root of the test program.
Expand All @@ -150,7 +65,12 @@ public LFTest(Target target, Path srcFile, Path packageRoot) {
this.name = packageRoot.relativize(srcFile).toString();
this.relativePath = Paths.get(name);
}


/** Stream object for capturing standard and error output. */
public OutputStream getOutputStream() {
return compilationLog;
}

/**
* Comparison implementation to allow for tests to be sorted (e.g., when added to a
* tree set) based on their path (relative to the root of the test directory).
Expand All @@ -166,10 +86,7 @@ public int compareTo(LFTest t) {
*/
@Override
public boolean equals(Object o) {
if (o instanceof LFTest && ((LFTest) o).name.equals(this.name)) {
return true;
}
return false;
return o instanceof LFTest && ((LFTest) o).name.equals(this.name);
}

/**
Expand All @@ -192,14 +109,11 @@ public int hashCode() {
}

/**
* Report whether or not this test has failed.
* Report whether this test has failed.
* @return True if the test has failed, false otherwise.
*/
public boolean hasFailed() {
if (result == Result.TEST_PASS) {
return false;
}
return true;
return result != Result.TEST_PASS;
}

/**
Expand All @@ -212,8 +126,8 @@ public StandaloneContext getContext() {
}

/**
*
* @return
* Compile a string that contains all collected errors and return it.
* @return A string that contains all collected errors.
*/
public String reportErrors() {
if (this.hasFailed()) {
Expand All @@ -223,10 +137,8 @@ public String reportErrors() {
sb.append("-----------------------------------------------------------------------------").append(System.lineSeparator());
sb.append("Reason: ").append(this.result.message).append(System.lineSeparator());
appendIfNotEmpty("Reported issues", this.issues.toString(), sb);
appendIfNotEmpty("Compilation error output", this.err.toString(), sb);
appendIfNotEmpty("Compilation standard output", this.out.toString(), sb);
appendIfNotEmpty("Execution error output", this.execLog.err.toString(), sb);
appendIfNotEmpty("Execution standard output", this.execLog.std.toString(), sb);
appendIfNotEmpty("Compilation output", this.compilationLog.toString(), sb);
appendIfNotEmpty("Execution output", this.execLog.toString(), sb);
sb.append("+---------------------------------------------------------------------------+\n");
return sb.toString();
} else {
Expand Down Expand Up @@ -271,8 +183,71 @@ public enum Result {
* Private constructor.
* @param message Description of the test outcome.
*/
private Result(String message) {
Result(String message) {
this.message = message;
}
}


/**
* Inner class for capturing streams during execution of a test, capable of
* recording output streams up until the moment that a test is interrupted
* upon timing out.
*
* @author Marten Lohstroh <marten@berkeley.edu>
*
*/
public static final class ExecutionLogger {

/**
* String buffer used to record the standard output and error
* streams from the input process.
*/
final StringBuffer buffer = new StringBuffer();

/**
* Return a thread responsible for recording the standard output stream
* of the given process.
* A separate thread is used so that the activity can be preempted.
*/
public Thread recordStdOut(Process process) {
return recordStream(buffer, process.getInputStream());
}

/**
* Return a thread responsible for recording the error stream of the
* given process.
* A separate thread is used so that the activity can be preempted.
*/
public Thread recordStdErr(Process process) {
return recordStream(buffer, process.getErrorStream());
}

/**
* Return a thread responsible for recording the given stream.
*
* @param builder The builder to append to.
* @param inputStream The stream to read from.
*/
private Thread recordStream(StringBuffer builder, InputStream inputStream) {
Thread t = new Thread(() -> {
try (Reader reader = new InputStreamReader(inputStream)) {
int len;
char[] buf = new char[1024];
while ((len = reader.read(buf)) > 0) {
builder.append(buf, 0, len);
}
} catch (Exception e) {
builder.append("[truncated...]\n");
}
});
t.start();
return t;
}

@Override
public String toString() {
return buffer.toString();
}
}
}
4 changes: 2 additions & 2 deletions org.lflang.tests/src/org/lflang/tests/TestBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,8 @@ private static void restoreOutputs() {
* @param test The test to redirect outputs to.
*/
private static void redirectOutputs(LFTest test) {
System.setOut(new PrintStream(test.out));
System.setErr(new PrintStream(test.err));
System.setOut(new PrintStream(test.getOutputStream()));
System.setErr(new PrintStream(test.getOutputStream()));
}


Expand Down

0 comments on commit 11570eb

Please sign in to comment.