Skip to content

Commit

Permalink
Documentation for IExecutionVisualiser
Browse files Browse the repository at this point in the history
Closes #57
  • Loading branch information
krmahadevan committed Jan 23, 2024
1 parent bf972db commit 4079a16
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 1 deletion.
150 changes: 150 additions & 0 deletions src/main/asciidoc/docs/execution_visualiser.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
=== Visualising Executions

:url: https://javadoc.io/static/org.testng/testng/{version-label}

TestNG allows you to visualise the test method executions via the interface {url}/org/testng/IExecutionVisualiser.html[IExecutionVisualiser].

TestNG passes metadata about the current execution state in https://graphviz.org/doc/info/lang.html[dot] representation.

Color coding is done as below:

* tests that are yet to run are colored in *yellow*.
* tests that are currently running are colored in *green*.
* tests that have completed execution are colored in *grey*.
This listener should be declared, as explained in the section about xref:testng_listeners.adoc[TestNG listeners].

Let's a sample.

==== Setup

* Follow the instructions detailed in https://graphviz.org/download/[graphviz] website to install `graphviz`. This binary will help us convert the `dot` format into `png` files.
* Add a dependency to a library such as https://github.com/square/gifencoder/[gifencoder] so that we can convert a bunch of `png` files into a `gif` file.

[source, xml]

----
<dependency>
<groupId>com.squareup</groupId>
<artifactId>gifencoder</artifactId>
<version>0.10.1</version>
</dependency>
----

==== Code sample

Here's how a sample implementation could look like:

[source, java]

----
import com.squareup.gifencoder.FloydSteinbergDitherer;
import com.squareup.gifencoder.GifEncoder;
import com.squareup.gifencoder.ImageOptions;
import org.testng.IExecutionVisualiser;
import org.testng.IReporter;
import org.testng.ISuite;
import org.testng.xml.XmlSuite;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class SampleVisualiser implements IExecutionVisualiser, IReporter {
private final AtomicInteger counter = new AtomicInteger(1);
private final Path tmpdir;
private final List<String> pngFiles = new ArrayList<>();
public SampleVisualiser() throws IOException {
tmpdir = Files.createTempDirectory(Paths.get("target"), "dot-");
}
@Override
public void consumeDotDefinition(String dotDefinition) {
String filePrefix = counter.getAndIncrement() + "-" + UUID.randomUUID();
Path input = Path.of(tmpdir.toFile().getAbsolutePath(), filePrefix + "-input.dot");
try {
Files.writeString(input, dotDefinition);
Path output = Path.of(tmpdir.toFile().getAbsolutePath(), filePrefix + "-output.png");
if (generatePngFileFromDotContent(input, output)) {
pngFiles.add(output.toFile().getAbsolutePath());
}
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
}
}
@Override
public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites, String outputDirectory) {
//The GIF image will be created with file name "animation.gif"
File writeTo = new File(tmpdir.toFile().getAbsolutePath(), "animation.gif");
try (FileOutputStream outputStream = new FileOutputStream(writeTo)) {
ImageOptions options = new ImageOptions();
//Set 500ms between each frame
options.setDelay(500, TimeUnit.MILLISECONDS);
//Use Floyd Steinberg dithering as it yields the best quality
options.setDitherer(FloydSteinbergDitherer.INSTANCE);
GifEncoder encoder = new GifEncoder(outputStream, 1600, 1200, 0);
for (String pngFile : pngFiles) {
encoder.addImage(convertImageToArray(new File(pngFile)), options);
}
encoder.finishEncoding();
} catch (IOException e) {
throw new RuntimeException(e);
}
System.err.println("The gif has been generated in :" + writeTo.getAbsolutePath());
}
private int[][] convertImageToArray(File file) throws IOException {
BufferedImage bufferedImage = ImageIO.read(file);
int[][] rgbArray = new int[bufferedImage.getHeight()][bufferedImage.getWidth()];
for (int i = 0; i < bufferedImage.getHeight(); i++) {
for (int j = 0; j < bufferedImage.getWidth(); j++) {
rgbArray[i][j] = bufferedImage.getRGB(j, i);
}
}
return rgbArray;
}
private static boolean generatePngFileFromDotContent(Path input, Path output) throws IOException, InterruptedException {
String[] cmds = {
"dot",
"-Tpng",
input.toFile().getAbsolutePath(),
"-o",
output.toFile().getAbsolutePath()
};
Process process = new ProcessBuilder().command(cmds).start();
handleInputErrorFrom(process);
int exitCode = process.waitFor();
System.err.println("PNG file written to " + output.toFile().getAbsolutePath());
return exitCode == 0;
}
private static void handleInputErrorFrom(Process process) throws IOException {
processStream(process.getInputStream());
processStream(process.getErrorStream());
}
private static void processStream(InputStream stream) throws IOException {
if (stream == null) {
return;
}
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
}
----
2 changes: 1 addition & 1 deletion src/main/asciidoc/docs/testng_listeners.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Javadocs are available {javadocs-base-url}/org/testng/IExecutionListener.html[he

==== IExecutionVisualiser

Documentation To be included.
Refer xref:execution_visualiser.adoc[here] to learn more.
Javadocs are available {javadocs-base-url}/org/testng/IExecutionVisualiser.html[here]

==== IHookable
Expand Down

0 comments on commit 4079a16

Please sign in to comment.