From 4079a163b3a663948f0257465299db99bcf27563 Mon Sep 17 00:00:00 2001 From: Krishnan Mahadevan Date: Tue, 23 Jan 2024 10:48:02 +0530 Subject: [PATCH] Documentation for IExecutionVisualiser Closes #57 --- .../asciidoc/docs/execution_visualiser.adoc | 150 ++++++++++++++++++ src/main/asciidoc/docs/testng_listeners.adoc | 2 +- 2 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 src/main/asciidoc/docs/execution_visualiser.adoc diff --git a/src/main/asciidoc/docs/execution_visualiser.adoc b/src/main/asciidoc/docs/execution_visualiser.adoc new file mode 100644 index 0000000..f4f123a --- /dev/null +++ b/src/main/asciidoc/docs/execution_visualiser.adoc @@ -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] + +---- + + com.squareup + gifencoder + 0.10.1 + +---- + +==== 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 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 xmlSuites, List 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); + } + } +} +---- \ No newline at end of file diff --git a/src/main/asciidoc/docs/testng_listeners.adoc b/src/main/asciidoc/docs/testng_listeners.adoc index ddb3021..07149d6 100644 --- a/src/main/asciidoc/docs/testng_listeners.adoc +++ b/src/main/asciidoc/docs/testng_listeners.adoc @@ -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