## Sunday, July 22, 2012

### Visual testing of a triangle filling graphic method

Some time ago I was invited by a big company for a technical interview where I was asked by a test engineer - "how to write test for a method filling a triangle area?".. I didn't find the answer for the provided time (some inappropriate ideas like to use a neural network chased each other in my brain), but the problem was very interesting for me and an idea dawned me on the next day..
Idea: A Triangle is a very easy shape but it is very hard to distinguish the shape in an image by a computer, but a rectangle is much easier to be processed by a computer, thus we should just make a rectangle from two triangles and then we will check that the rectangle area is presented and artifacts-free, also we can check that there are not any points outside of the area..
I have written some Java code to check the idea. There is not a method in the java.awt.Graphics object to fill a triangle thus I have written such one:
```public class TriangleFiller {
/**
* The method fills a triangle area and we check that the method fills a triangle
* @param graphics the graphics context
* @param xcoords an array contains the x coordinates for vertices (must have 3 positions)
* @param ycoords an array contains the y coordinates for vertices (must have 3 positions)
*/
public static void fillTriangle(final Graphics graphics, int[] xcoords, int[] ycoords) {
if (xcoords.length != 3 || ycoords.length != 3) {
throw new IllegalArgumentException("Triangle must have 3 points");
}
final Polygon polygon = new Polygon(xcoords, ycoords, 3);
// we need use draw+fill because the fill operation fills the inside area
graphics.drawPolygon(polygon);
graphics.fillPolygon(polygon);
}
}
```

Then I wrote a unit test to make the "visual check" of the method just on an image and it works well:
```package com.igormaznitsa.testtriangle;

import java.awt.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.ImageIO;
import static org.junit.Assert.*;
import org.junit.Test;

public class TriangleFillerTest {

private boolean checkTriangleCornerPoints(final BufferedImage baseRGBImage, final int pointColor, final int[] xVerticies, final int[] yVerticies) {
for (int pointIndex = 0; pointIndex < 3; pointIndex++) {
final int x = xVerticies[pointIndex];
final int y = yVerticies[pointIndex];

if ((baseRGBImage.getRGB(x, y) & 0xFFFFFF) != pointColor) {
return false;
}
}
return true;
}

private boolean checkRectangleAreaHasBeenFilledOnly(final BufferedImage baseRGBImage, final int fillColor, final int backgroundColor, final int areaLeftX, final int areaTopY, final int areaWidth, final int areaHeight) {
final int areaRightX = areaLeftX + areaWidth;
final int areaBottomY = areaTopY + areaHeight;

final int imagewidth = baseRGBImage.getWidth();
final int imageheight = baseRGBImage.getHeight();

for (int y = 0; y < imageheight; y++) {
for (int x = 0; x < imagewidth; x++) {
final int rgb = baseRGBImage.getRGB(x, y) & 0xFFFFFF;
final boolean notInArea = x < areaLeftX || x > areaRightX || y < areaTopY || y > areaBottomY;
if (notInArea) {
assertEquals("Must be background color "+backgroundColor, backgroundColor, rgb);
} else {
assertEquals("Must be fill color " + fillColor, fillColor, rgb);
}
}
}
return true;
}

@Test
public void testFillTriangle_VisualTest() throws Exception {
// the graphic log image file
final File gfxLogFile = new File("./testimage.png");

// create a RGB memory rendered image which will be our base for the test
final BufferedImage baseTestImage = new BufferedImage(200, 200, BufferedImage.TYPE_INT_RGB);
final Graphics2D gfx = (Graphics2D) baseTestImage.getGraphics();

// we must disable antialasing for the graphics to avoid artefacts
((Graphics2D) gfx).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);

final int backColor = 0x000000;
final int shapeColor = 0xFFFFFF;

// fill the image by our background color
gfx.setColor(new Color(backColor));
gfx.clearRect(0, 0, 200, 200);

// set the shape color
gfx.setColor(new Color(shapeColor));

// our triangles, we use non-equaterial triangles, to make a rectangle, not a square
final int[] firstTriangleX = new int[]{10, 10, 50};
final int[] firstTriangleY = new int[]{10, 150, 150};
final int[] secondTriangleX = new int[]{10, 50, 50};
final int[] secondTriangleY = new int[]{10, 10, 150};

// draw the first triangle
TriangleFiller.fillTriangle(gfx, firstTriangleX, firstTriangleY);

// save the current graphic state as an image (like log)
ImageIO.write(baseTestImage, "png", gfxLogFile);

assertTrue("The image must have set the points of vertices", checkTriangleCornerPoints(baseTestImage, shapeColor, firstTriangleX, firstTriangleY));

// draw the second triangle, to get a filled rectangular area on the image
TriangleFiller.fillTriangle(gfx, secondTriangleX, secondTriangleY);

gfx.dispose();

// save the current graphic state as an image (like log)
ImageIO.write(baseTestImage, "png", gfxLogFile);

assertTrue("Only the rectangle area must be filled, without artefacts",checkRectangleAreaHasBeenFilledOnly(baseTestImage, shapeColor, backColor, 10, 10, 40, 140));
}
}
```
{"ft_lang_label":"__label__en","ft_lang_prob":0.6107544,"math_prob":0.90965414,"size":4667,"snap":"2021-04-2021-17","text_gpt3_token_len":1132,"char_repetition_ratio":0.15312031,"word_repetition_ratio":0.043410853,"special_character_ratio":0.24705379,"punctuation_ratio":0.20050125,"nsfw_num_words":0,"has_unicode_error":false,"math_prob_llama3":0.96838284,"pos_list":[0],"im_url_duplicate_count":[null],"WARC_HEADER":"{\"WARC-Type\":\"response\",\"WARC-Date\":\"2021-01-20T19:39:39Z\",\"WARC-Record-ID\":\"<urn:uuid:8aa2bd3e-0bf3-4c6c-8793-36f4e69c17f6>\",\"Content-Length\":\"57783\",\"Content-Type\":\"application/http; msgtype=response\",\"WARC-Warcinfo-ID\":\"<urn:uuid:35f7f67c-4f4e-4811-95ac-e95e3eba38f5>\",\"WARC-Concurrent-To\":\"<urn:uuid:8a4bbbcf-cec7-467c-ba71-778ddc1b9bd6>\",\"WARC-IP-Address\":\"172.217.164.161\",\"WARC-Target-URI\":\"http://raydac.blogspot.com/2012/07/\",\"WARC-Payload-Digest\":\"sha1:YV43CP5R62NPAWXMONFOQRVOJANQQTIG\",\"WARC-Block-Digest\":\"sha1:CBQGE3UK2VWW6A6XROKJAQ4QEYSUJ7DI\",\"WARC-Identified-Payload-Type\":\"text/html\",\"warc_filename\":\"/cc_download/warc_2021/CC-MAIN-2021-04/CC-MAIN-2021-04_segments_1610703521987.71_warc_CC-MAIN-20210120182259-20210120212259-00239.warc.gz\"}"}