Files
sousa-gecko/image/test/unit/test_imgtools.js
T
Lee Salzman 92d220f44b Bug 2045201 - Re-enable legacy shader path in Skia. r=gfx-reviewers,tnikkel
In the Skia m149 update, upstream changed it so that the legacy shader blitting path
is no longer enabled by default. Instead, the raster pipeline blitter gets used, but it
is both slower and gives divergent results, so we don't want to use it. This just re-enables
the legacy shader path via setting the appropriate definition for now.

Differential Revision: https://phabricator.services.mozilla.com/D305021
2026-06-06 16:14:48 +00:00

857 lines
25 KiB
JavaScript

/*
* Tests for imgITools
*/
const { NetUtil } = ChromeUtils.importESModule(
"resource://gre/modules/NetUtil.sys.mjs"
);
const { AppConstants } = ChromeUtils.importESModule(
"resource://gre/modules/AppConstants.sys.mjs"
);
/*
* dumpToFile()
*
* For test development, dumps the specified array to a file.
* Call |dumpToFile(outData);| in a test to file to a file.
*/
// eslint-disable-next-line no-unused-vars
function dumpToFile(aData) {
var outputFile = do_get_cwd();
outputFile.append("testdump.webp");
var outputStream = Cc[
"@mozilla.org/network/file-output-stream;1"
].createInstance(Ci.nsIFileOutputStream);
// WR_ONLY|CREATE|TRUNC
outputStream.init(outputFile, 0x02 | 0x08 | 0x20, 0o644, null);
var bos = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(
Ci.nsIBinaryOutputStream
);
bos.setOutputStream(outputStream);
bos.writeByteArray(aData);
outputStream.close();
}
/*
* getFileInputStream()
*
* Returns an input stream for the specified file.
*/
function getFileInputStream(aFile) {
var inputStream = Cc[
"@mozilla.org/network/file-input-stream;1"
].createInstance(Ci.nsIFileInputStream);
// init the stream as RD_ONLY, -1 == default permissions.
inputStream.init(aFile, 0x01, -1, null);
// Blah. The image decoders use ReadSegments, which isn't implemented on
// file input streams. Use a buffered stream to make it work.
var bis = Cc["@mozilla.org/network/buffered-input-stream;1"].createInstance(
Ci.nsIBufferedInputStream
);
bis.init(inputStream, 1024);
return bis;
}
/*
* streamToArray()
*
* Consumes an input stream, and returns its bytes as an array.
*/
function streamToArray(aStream) {
var size = aStream.available();
// use a binary input stream to grab the bytes.
var bis = Cc["@mozilla.org/binaryinputstream;1"].createInstance(
Ci.nsIBinaryInputStream
);
bis.setInputStream(aStream);
var bytes = bis.readByteArray(size);
if (size != bytes.length) {
throw new Error("Didn't read expected number of bytes");
}
return bytes;
}
/*
* compareArrays
*
* Compares two arrays, and throws if there's a difference.
*/
function compareArrays(aArray1, aArray2) {
Assert.equal(aArray1.length, aArray2.length);
for (var i = 0; i < aArray1.length; i++) {
if (aArray1[i] != aArray2[i]) {
throw new Error("arrays differ at index " + i);
}
}
}
function run_test() {
try {
/* ========== 0 ========== */
var testnum = 0;
var testdesc = "imgITools setup";
var imgTools = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools);
if (!imgTools) {
throw new Error("Couldn't get imgITools service");
}
// Ugh, this is an ugly hack. The pixel values we get in Windows are sometimes
// +/- 1 value compared to other platforms, so we need to compare against a
// different set of reference images. nsIXULRuntime.OS doesn't seem to be
// available in xpcshell, so we'll use this as a kludgy way to figure out if
// we're running on Windows.
var isWindows = mozinfo.os == "win";
/* ========== 1 ========== */
testnum++;
testdesc = "test decoding a PNG";
// 64x64 png, 8415 bytes.
var imgName = "image1.png";
var inMimeType = "image/png";
var imgFile = do_get_file(imgName);
var istream = getFileInputStream(imgFile);
Assert.equal(istream.available(), 8415);
var buffer = NetUtil.readInputStreamToString(istream, istream.available());
var container = imgTools.decodeImageFromBuffer(
buffer,
buffer.length,
inMimeType
);
// It's not easy to look at the pixel values from JS, so just
// check the container's size.
Assert.equal(container.width, 64);
Assert.equal(container.height, 64);
/* ========== 2 ========== */
testnum++;
testdesc = "test encoding a scaled JPEG";
// we'll reuse the container from the previous test
istream = imgTools.encodeScaledImage(container, "image/jpeg", 16, 16);
var encodedBytes = streamToArray(istream);
// Get bytes for expected result
var refName = "image1png16x16.jpg";
var refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
Assert.equal(istream.available(), 1051);
var referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 3 ========== */
testnum++;
testdesc = "test encoding an unscaled JPEG";
// we'll reuse the container from the previous test
istream = imgTools.encodeImage(container, "image/jpeg");
encodedBytes = streamToArray(istream);
// Get bytes for expected result
refName = "image1png64x64.jpg";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
Assert.equal(istream.available(), 4507);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 4 ========== */
testnum++;
testdesc = "test decoding a JPEG";
// 32x32 jpeg, 3494 bytes.
imgName = "image2.jpg";
inMimeType = "image/jpeg";
imgFile = do_get_file(imgName);
istream = getFileInputStream(imgFile);
Assert.equal(istream.available(), 3494);
buffer = NetUtil.readInputStreamToString(istream, istream.available());
container = imgTools.decodeImageFromBuffer(
buffer,
buffer.length,
inMimeType
);
// It's not easy to look at the pixel values from JS, so just
// check the container's size.
Assert.equal(container.width, 32);
Assert.equal(container.height, 32);
/* ========== 5 ========== */
testnum++;
testdesc = "test encoding a scaled PNG";
if (!isWindows) {
// we'll reuse the container from the previous test
istream = imgTools.encodeScaledImage(container, "image/png", 16, 16);
encodedBytes = streamToArray(istream);
// Get bytes for expected result
if (AppConstants.USE_LIBZ_RS) {
refName = "image2jpg16x16.libz-rs.png";
} else {
refName = isWindows ? "image2jpg16x16-win.png" : "image2jpg16x16.png";
}
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
Assert.equal(istream.available(), AppConstants.USE_LIBZ_RS ? 938 : 955);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
}
/* ========== 6 ========== */
testnum++;
testdesc = "test encoding an unscaled PNG";
if (!isWindows) {
// we'll reuse the container from the previous test
istream = imgTools.encodeImage(container, "image/png");
encodedBytes = streamToArray(istream);
// Get bytes for expected result
if (AppConstants.USE_LIBZ_RS) {
refName = "image2jpg32x32.libz-rs.png";
} else {
refName = isWindows ? "image2jpg32x32-win.png" : "image2jpg32x32.png";
}
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
Assert.equal(istream.available(), AppConstants.USE_LIBZ_RS ? 2916 : 3026);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
}
/* ========== 7 ========== */
testnum++;
testdesc = "test decoding a ICO";
// 16x16 ico, 1406 bytes.
imgName = "image3.ico";
inMimeType = "image/x-icon";
imgFile = do_get_file(imgName);
istream = getFileInputStream(imgFile);
Assert.equal(istream.available(), 1406);
buffer = NetUtil.readInputStreamToString(istream, istream.available());
container = imgTools.decodeImageFromBuffer(
buffer,
buffer.length,
inMimeType
);
// It's not easy to look at the pixel values from JS, so just
// check the container's size.
Assert.equal(container.width, 16);
Assert.equal(container.height, 16);
/* ========== 8 ========== */
testnum++;
testdesc = "test encoding a scaled PNG"; // note that we're scaling UP
// we'll reuse the container from the previous test
istream = imgTools.encodeScaledImage(container, "image/png", 32, 32);
encodedBytes = streamToArray(istream);
// Get bytes for expected result
refName = AppConstants.USE_LIBZ_RS
? "image3ico32x32.libz-rs.png"
: "image3ico32x32.png";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
Assert.equal(istream.available(), AppConstants.USE_LIBZ_RS ? 2283 : 2280);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 9 ========== */
testnum++;
testdesc = "test encoding an unscaled PNG";
// we'll reuse the container from the previous test
istream = imgTools.encodeImage(container, "image/png");
encodedBytes = streamToArray(istream);
// Get bytes for expected result
refName = AppConstants.USE_LIBZ_RS
? "image3ico16x16.libz-rs.png"
: "image3ico16x16.png";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
Assert.equal(istream.available(), AppConstants.USE_LIBZ_RS ? 512 : 520);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 10 ========== */
testnum++;
testdesc = "test decoding a GIF";
// 32x32 gif, 1809 bytes.
imgName = "image4.gif";
inMimeType = "image/gif";
imgFile = do_get_file(imgName);
istream = getFileInputStream(imgFile);
Assert.equal(istream.available(), 1809);
buffer = NetUtil.readInputStreamToString(istream, istream.available());
container = imgTools.decodeImageFromBuffer(
buffer,
buffer.length,
inMimeType
);
// It's not easy to look at the pixel values from JS, so just
// check the container's size.
Assert.equal(container.width, 32);
Assert.equal(container.height, 32);
/* ========== 11 ========== */
testnum++;
testdesc =
"test encoding an unscaled ICO with format options " +
"(format=bmp;bpp=32)";
// we'll reuse the container from the previous test
istream = imgTools.encodeImage(
container,
"image/vnd.microsoft.icon",
"format=bmp;bpp=32"
);
encodedBytes = streamToArray(istream);
// Get bytes for expected result
refName = "image4gif32x32bmp32bpp.ico";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
Assert.equal(istream.available(), 4286);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 12 ========== */
testnum++;
testdesc =
// eslint-disable-next-line no-useless-concat
"test encoding a scaled ICO with format options " + "(format=bmp;bpp=32)";
// we'll reuse the container from the previous test
istream = imgTools.encodeScaledImage(
container,
"image/vnd.microsoft.icon",
16,
16,
"format=bmp;bpp=32"
);
encodedBytes = streamToArray(istream);
// Get bytes for expected result
refName = "image4gif16x16bmp32bpp.ico";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
Assert.equal(istream.available(), 1150);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 13 ========== */
testnum++;
testdesc =
"test encoding an unscaled ICO with format options " +
"(format=bmp;bpp=24)";
// we'll reuse the container from the previous test
istream = imgTools.encodeImage(
container,
"image/vnd.microsoft.icon",
"format=bmp;bpp=24"
);
encodedBytes = streamToArray(istream);
// Get bytes for expected result
refName = "image4gif32x32bmp24bpp.ico";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
Assert.equal(istream.available(), 3262);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 14 ========== */
testnum++;
testdesc =
// eslint-disable-next-line no-useless-concat
"test encoding a scaled ICO with format options " + "(format=bmp;bpp=24)";
// we'll reuse the container from the previous test
istream = imgTools.encodeScaledImage(
container,
"image/vnd.microsoft.icon",
16,
16,
"format=bmp;bpp=24"
);
encodedBytes = streamToArray(istream);
// Get bytes for expected result
refName = "image4gif16x16bmp24bpp.ico";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
Assert.equal(istream.available(), 894);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 15 ========== */
testnum++;
testdesc = "test cropping a JPG";
// 32x32 jpeg, 3494 bytes.
imgName = "image2.jpg";
inMimeType = "image/jpeg";
imgFile = do_get_file(imgName);
istream = getFileInputStream(imgFile);
Assert.equal(istream.available(), 3494);
buffer = NetUtil.readInputStreamToString(istream, istream.available());
container = imgTools.decodeImageFromBuffer(
buffer,
buffer.length,
inMimeType
);
// It's not easy to look at the pixel values from JS, so just
// check the container's size.
Assert.equal(container.width, 32);
Assert.equal(container.height, 32);
// encode a cropped image
istream = imgTools.encodeCroppedImage(
container,
"image/jpeg",
0,
0,
16,
16
);
encodedBytes = streamToArray(istream);
// Get bytes for expected result
refName = "image2jpg16x16cropped.jpg";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
Assert.equal(istream.available(), 879);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 16 ========== */
testnum++;
testdesc = "test cropping a JPG with an offset";
// we'll reuse the container from the previous test
istream = imgTools.encodeCroppedImage(
container,
"image/jpeg",
16,
16,
16,
16
);
encodedBytes = streamToArray(istream);
// Get bytes for expected result
refName = "image2jpg16x16cropped2.jpg";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
Assert.equal(istream.available(), 878);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 17 ========== */
testnum++;
testdesc = "test cropping a JPG without a given height";
// we'll reuse the container from the previous test
istream = imgTools.encodeCroppedImage(container, "image/jpeg", 0, 0, 16, 0);
encodedBytes = streamToArray(istream);
// Get bytes for expected result
refName = "image2jpg16x32cropped3.jpg";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
Assert.equal(istream.available(), 1127);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 18 ========== */
testnum++;
testdesc = "test cropping a JPG without a given width";
// we'll reuse the container from the previous test
istream = imgTools.encodeCroppedImage(container, "image/jpeg", 0, 0, 0, 16);
encodedBytes = streamToArray(istream);
// Get bytes for expected result
refName = "image2jpg32x16cropped4.jpg";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
Assert.equal(istream.available(), 1135);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 19 ========== */
testnum++;
testdesc = "test cropping a JPG without a given width and height";
// we'll reuse the container from the previous test
istream = imgTools.encodeCroppedImage(container, "image/jpeg", 0, 0, 0, 0);
encodedBytes = streamToArray(istream);
// Get bytes for expected result
refName = "image2jpg32x32.jpg";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
Assert.equal(istream.available(), 1634);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 20 ========== */
testnum++;
testdesc = "test scaling a JPG without a given width";
// we'll reuse the container from the previous test
istream = imgTools.encodeScaledImage(container, "image/jpeg", 0, 16);
encodedBytes = streamToArray(istream);
// Get bytes for expected result
refName = "image2jpg32x16scaled.jpg";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
Assert.equal(istream.available(), 1227);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 21 ========== */
testnum++;
testdesc = "test scaling a JPG without a given height";
// we'll reuse the container from the previous test
istream = imgTools.encodeScaledImage(container, "image/jpeg", 16, 0);
encodedBytes = streamToArray(istream);
// Get bytes for expected result
refName = "image2jpg16x32scaled.jpg";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
Assert.equal(istream.available(), 1219);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 22 ========== */
testnum++;
testdesc = "test scaling a JPG without a given width and height";
// we'll reuse the container from the previous test
istream = imgTools.encodeScaledImage(container, "image/jpeg", 0, 0);
encodedBytes = streamToArray(istream);
// Get bytes for expected result
refName = "image2jpg32x32.jpg";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
Assert.equal(istream.available(), 1634);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 23 ========== */
testnum++;
testdesc = "test invalid arguments for cropping";
var numErrors = 0;
try {
// width/height can't be negative
imgTools.encodeScaledImage(container, "image/jpeg", -1, -1);
} catch (e) {
numErrors++;
}
try {
// offsets can't be negative
imgTools.encodeCroppedImage(container, "image/jpeg", -1, -1, 16, 16);
} catch (e) {
numErrors++;
}
try {
// width/height can't be negative
imgTools.encodeCroppedImage(container, "image/jpeg", 0, 0, -1, -1);
} catch (e) {
numErrors++;
}
try {
// out of bounds
imgTools.encodeCroppedImage(container, "image/jpeg", 17, 17, 16, 16);
} catch (e) {
numErrors++;
}
try {
// out of bounds
imgTools.encodeCroppedImage(container, "image/jpeg", 0, 0, 33, 33);
} catch (e) {
numErrors++;
}
try {
// out of bounds
imgTools.encodeCroppedImage(container, "image/jpeg", 1, 1, 0, 0);
} catch (e) {
numErrors++;
}
Assert.equal(numErrors, 6);
/* ========== 24 ========== */
testnum++;
testdesc = "test encoding webp";
// Load picture that we want to convert
imgName = "image1.png";
inMimeType = "image/png";
imgFile = do_get_file(imgName);
istream = getFileInputStream(imgFile);
Assert.equal(istream.available(), 8415);
buffer = NetUtil.readInputStreamToString(istream, istream.available());
container = imgTools.decodeImageFromBuffer(
buffer,
buffer.length,
inMimeType
);
// Convert image to webp
istream = imgTools.encodeImage(container, "image/webp");
encodedBytes = streamToArray(istream);
// Get bytes for expected result
refName = "image1.webp";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
Assert.equal(istream.available(), 3206);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== 25 ========== */
testnum++;
testdesc = "test encoding webp with quality parameter";
// Load picture that we want to convert
imgName = "image1.png";
inMimeType = "image/png";
imgFile = do_get_file(imgName);
istream = getFileInputStream(imgFile);
Assert.equal(istream.available(), 8415);
buffer = NetUtil.readInputStreamToString(istream, istream.available());
container = imgTools.decodeImageFromBuffer(
buffer,
buffer.length,
inMimeType
);
// Convert image to webp
istream = imgTools.encodeImage(container, "image/webp", "quality=50");
encodedBytes = streamToArray(istream);
// Get bytes for expected result
refName = "image1quality50.webp";
refFile = do_get_file(refName);
istream = getFileInputStream(refFile);
Assert.equal(istream.available(), 1944);
referenceBytes = streamToArray(istream);
// compare the encoder's output to the reference file.
compareArrays(encodedBytes, referenceBytes);
/* ========== bug 363986 ========== */
testnum = 363986;
testdesc = "test PNG and JPEG and WEBP encoders' Read/ReadSegments methods";
var testData = [
{
preImage: "image3.ico",
preImageMimeType: "image/x-icon",
refImage: AppConstants.USE_LIBZ_RS
? "image3ico16x16.libz-rs.png"
: "image3ico16x16.png",
refImageMimeType: "image/png",
},
{
preImage: "image1.png",
preImageMimeType: "image/png",
refImage: "image1png64x64.jpg",
refImageMimeType: "image/jpeg",
},
{
preImage: "image1.png",
preImageMimeType: "image/png",
refImage: "image1.webp",
refImageMimeType: "image/webp",
},
];
for (var i = 0; i < testData.length; ++i) {
var dict = testData[i];
imgFile = do_get_file(dict.refImage);
istream = getFileInputStream(imgFile);
var refBytes = streamToArray(istream);
imgFile = do_get_file(dict.preImage);
istream = getFileInputStream(imgFile);
buffer = NetUtil.readInputStreamToString(istream, istream.available());
container = imgTools.decodeImageFromBuffer(
buffer,
buffer.length,
dict.preImageMimeType
);
istream = imgTools.encodeImage(container, dict.refImageMimeType);
var sstream = Cc["@mozilla.org/storagestream;1"].createInstance(
Ci.nsIStorageStream
);
sstream.init(4096, 4294967295, null);
var ostream = sstream.getOutputStream(0);
var bostream = Cc[
"@mozilla.org/network/buffered-output-stream;1"
].createInstance(Ci.nsIBufferedOutputStream);
// use a tiny buffer to make sure the image data doesn't fully fit in it
bostream.init(ostream, 8);
bostream.writeFrom(istream, istream.available());
bostream.flush();
bostream.close();
var encBytes = streamToArray(sstream.newInputStream(0));
compareArrays(refBytes, encBytes);
}
/* ========== bug 413512 ========== */
testnum = 413512;
testdesc = "test decoding bad favicon (bug 413512)";
imgName = "bug413512.ico";
inMimeType = "image/x-icon";
imgFile = do_get_file(imgName);
istream = getFileInputStream(imgFile);
Assert.equal(istream.available(), 17759);
// bug413512.ico is a malformed favicon: five of its six resources have a
// corrupt BITMAPINFOHEADER, but the first resource is a valid 16x16 BMP.
// The decoder should recover that one resource and decode the file
// successfully rather than failing outright.
buffer = NetUtil.readInputStreamToString(istream, istream.available());
container = imgTools.decodeImageFromBuffer(
buffer,
buffer.length,
inMimeType
);
Assert.equal(container.width, 16);
Assert.equal(container.height, 16);
istream = imgTools.encodeImage(container, "image/png");
Assert.greater(
istream.available(),
0,
"should be able to decode and encode bug413512.ico"
);
/* ========== bug 815359 ========== */
testnum = 815359;
testdesc = "test correct ico hotspots (bug 815359)";
imgName = "bug815359.ico";
inMimeType = "image/x-icon";
imgFile = do_get_file(imgName);
istream = getFileInputStream(imgFile);
Assert.equal(istream.available(), 4286);
buffer = NetUtil.readInputStreamToString(istream, istream.available());
container = imgTools.decodeImageFromBuffer(
buffer,
buffer.length,
inMimeType
);
Assert.equal(container.hotspotX, 10);
Assert.equal(container.hotspotY, 9);
/* ========== end ========== */
} catch (e) {
throw new Error(
"FAILED in test #" + testnum + " -- " + testdesc + ": " + e
);
}
}