Skip to content

Commit 9b39d0a

Browse files
committed
using perceptual difference between generated images and known control images for tests rather than binary hashing
1 parent c77f2db commit 9b39d0a

File tree

2 files changed

+73
-22
lines changed

2 files changed

+73
-22
lines changed

package.json

+4
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,17 @@
3333
"yargs": "^15.1.0"
3434
},
3535
"devDependencies": {
36+
"buffer-to-uint8array": "^1.1.0",
3637
"chai": "^4.2.0",
3738
"end-of-stream": "^1.4.4",
3839
"eslint": "^5.16.0",
3940
"file-type": "^13.1.1",
4041
"fs-extra": "^8.1.0",
42+
"jpeg-js": "^0.4.1",
4143
"mocha": "^7.0.0",
4244
"node-fetch": "^2.6.0",
45+
"pixelmatch": "^5.2.1",
46+
"pngjs": "^5.0.0",
4347
"rootrequire": "^1.0.0",
4448
"shellton": "^5.0.0",
4549
"tempy": "^0.3.0"

test/index.test.js

+69-22
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
/* eslint-env mocha */
22
const path = require('path');
3-
const crypto = require('crypto');
43
const { spawn } = require('child_process');
54

65
const fs = require('fs-extra');
@@ -9,16 +8,43 @@ const tempy = require('tempy');
98
const { fromBuffer: filetype } = require('file-type');
109
const { expect } = require('chai');
1110
const eos = require('end-of-stream');
11+
const toUint8 = require('buffer-to-uint8array');
12+
const pixelmatch = require('pixelmatch');
13+
const { PNG } = require('pngjs');
14+
const jpegJs = require('jpeg-js');
1215

1316
describe('heic-convert', () => {
1417
const HELP_LINE = 'Convert HEIC image to JPEG or PNG';
1518

16-
const assertImage = async (buffer, mime, hash) => {
19+
const decode = {
20+
'image/png': buffer => PNG.sync.read(buffer),
21+
'image/jpeg': buffer => jpegJs.decode(buffer)
22+
};
23+
24+
const readControl = async name => {
25+
const buffer = await fs.readFile(path.resolve(root, `temp/${name}`));
26+
return decode['image/png'](buffer);
27+
};
28+
29+
const compare = (expected, actual, width, height, errString = 'actual image did not match control image') => {
30+
const result = pixelmatch(toUint8(Buffer.from(expected)), toUint8(Buffer.from(actual)), null, width, height, {
31+
threshold: 0.1
32+
});
33+
34+
// allow 5% of pixels to be different
35+
expect(result).to.be.below(width * height * 0.05, errString);
36+
};
37+
38+
const assertImage = async (buffer, mime, control) => {
1739
const type = await filetype(buffer);
1840
expect(type.mime).to.equal(mime);
1941

20-
const actual = crypto.createHash('sha256').update(buffer).digest('hex');
21-
expect(actual).to.equal(hash);
42+
const actual = decode[mime](buffer);
43+
44+
expect(actual.width).to.equal(control.width);
45+
expect(actual.height).to.equal(control.height);
46+
47+
compare(control.data, actual.data, control.width, control.height);
2248
};
2349

2450
const exec = async (args, options = {}, input = Buffer.from('')) => {
@@ -78,6 +104,12 @@ describe('heic-convert', () => {
78104
});
79105

80106
describe('using --input and --output', () => {
107+
let control;
108+
109+
before(async () => {
110+
control = await readControl('0002-control.png');
111+
});
112+
81113
it('converts known image to jpeg', async () => {
82114
const infile = path.resolve(root, 'temp', '0002.heic');
83115
const outfile = files.get({ extension: 'jpg' });
@@ -88,7 +120,7 @@ describe('heic-convert', () => {
88120
expect(stderr.toString()).to.equal('');
89121
expect(err).to.have.property('code', 0);
90122

91-
await assertImage(await fs.readFile(outfile), 'image/jpeg', 'f7f1ae16c3fbf035d1b71b1995230305125236d0c9f0513c905ab1cb39fc68e9');
123+
await assertImage(await fs.readFile(outfile), 'image/jpeg', control);
92124
});
93125

94126
it('converts known image to png', async () => {
@@ -101,11 +133,17 @@ describe('heic-convert', () => {
101133
expect(stderr.toString()).to.equal('');
102134
expect(err).to.have.property('code', 0);
103135

104-
await assertImage(await fs.readFile(outfile), 'image/png', '0efc9a4c58d053fb42591acd83f8a5005ee2844555af29b5aba77a766b317935');
136+
await assertImage(await fs.readFile(outfile), 'image/png', control);
105137
});
106138
});
107139

108140
describe('using -i and -o', () => {
141+
let control;
142+
143+
before(async () => {
144+
control = await readControl('0002-control.png');
145+
});
146+
109147
it('converts known image to jpeg', async () => {
110148
const infile = path.resolve(root, 'temp', '0002.heic');
111149
const outfile = files.get({ extension: 'jpg' });
@@ -116,7 +154,7 @@ describe('heic-convert', () => {
116154
expect(stderr.toString()).to.equal('');
117155
expect(err).to.have.property('code', 0);
118156

119-
await assertImage(await fs.readFile(outfile), 'image/jpeg', 'f7f1ae16c3fbf035d1b71b1995230305125236d0c9f0513c905ab1cb39fc68e9');
157+
await assertImage(await fs.readFile(outfile), 'image/jpeg', control);
120158
});
121159

122160
it('converts known image to png', async () => {
@@ -129,19 +167,25 @@ describe('heic-convert', () => {
129167
expect(stderr.toString()).to.equal('');
130168
expect(err).to.have.property('code', 0);
131169

132-
await assertImage(await fs.readFile(outfile), 'image/png', '0efc9a4c58d053fb42591acd83f8a5005ee2844555af29b5aba77a766b317935');
170+
await assertImage(await fs.readFile(outfile), 'image/png', control);
133171
});
134172
});
135173

136174
describe('using stdin and stdout', () => {
175+
let control;
176+
177+
before(async () => {
178+
control = await readControl('0002-control.png');
179+
});
180+
137181
it('converts known image to jpeg', async () => {
138182
const infile = path.resolve(root, 'temp', '0002.heic');
139183
const inbuffer = await fs.readFile(infile);
140184

141185
const { stdout, stderr, err } = await exec([], {}, inbuffer);
142186

143187
expect(stderr.toString()).to.equal('');
144-
await assertImage(stdout, 'image/jpeg', 'f7f1ae16c3fbf035d1b71b1995230305125236d0c9f0513c905ab1cb39fc68e9');
188+
await assertImage(stdout, 'image/jpeg', control);
145189
expect(err).to.have.property('code', 0);
146190
});
147191

@@ -152,25 +196,28 @@ describe('heic-convert', () => {
152196
const { stdout, stderr, err } = await exec(['-f', 'PNG'], {}, inbuffer);
153197

154198
expect(stderr.toString()).to.equal('');
155-
await assertImage(stdout, 'image/png', '0efc9a4c58d053fb42591acd83f8a5005ee2844555af29b5aba77a766b317935');
199+
await assertImage(stdout, 'image/png', control);
156200
expect(err).to.have.property('code', 0);
157201
});
158202
});
159203

160204
describe('using a multi-image file', () => {
161-
const hashes = [
162-
{ i: 0, hash: '11e8083208d6357642624a2481b8c5e376d6655e14a46be44a2ca1221986a0bc' },
163-
{ i: 1, hash: '1f1ef0b3b19a1c10d9659702c5d32fb380358501e20c9d6e491bbea181873286' },
164-
{ i: 2, hash: '1f1ef0b3b19a1c10d9659702c5d32fb380358501e20c9d6e491bbea181873286' },
165-
];
205+
let controls;
206+
207+
before(async () => {
208+
controls = await Promise.all([
209+
readControl('0003-0-control.png'),
210+
readControl('0003-1-control.png'),
211+
]);
212+
});
166213

167214
it('converts the first image by default', async () => {
168215
const infile = path.resolve(root, 'temp', '0003.heic');
169216

170217
const { stdout, stderr, err } = await exec(['--input', `"${infile}"`], {});
171218

172219
expect(stderr.toString()).to.equal('');
173-
await assertImage(stdout, 'image/jpeg', hashes[0].hash);
220+
await assertImage(stdout, 'image/jpeg', controls[0]);
174221
expect(err).to.have.property('code', 0);
175222
});
176223

@@ -187,8 +234,8 @@ describe('heic-convert', () => {
187234
expect(stderr.toString()).to.equal('');
188235
expect(err).to.have.property('code', 0);
189236

190-
await assertImage(await fs.readFile(path.resolve(outdir, `${rand}-1-1.jpg`)), 'image/jpeg', hashes[1].hash);
191-
await assertImage(await fs.readFile(path.resolve(outdir, `${rand}-2-2.jpg`)), 'image/jpeg', hashes[2].hash);
237+
await assertImage(await fs.readFile(path.resolve(outdir, `${rand}-1-1.jpg`)), 'image/jpeg', controls[1]);
238+
await assertImage(await fs.readFile(path.resolve(outdir, `${rand}-2-2.jpg`)), 'image/jpeg', controls[1]);
192239
});
193240

194241
it('can convert all images using -m -1', async () => {
@@ -204,9 +251,9 @@ describe('heic-convert', () => {
204251
expect(stderr.toString()).to.equal('');
205252
expect(err).to.have.property('code', 0);
206253

207-
await assertImage(await fs.readFile(path.resolve(outdir, `0-${rand}-0-0.jpg`)), 'image/jpeg', hashes[0].hash);
208-
await assertImage(await fs.readFile(path.resolve(outdir, `1-${rand}-1-1.jpg`)), 'image/jpeg', hashes[1].hash);
209-
await assertImage(await fs.readFile(path.resolve(outdir, `2-${rand}-2-2.jpg`)), 'image/jpeg', hashes[2].hash);
254+
await assertImage(await fs.readFile(path.resolve(outdir, `0-${rand}-0-0.jpg`)), 'image/jpeg', controls[0]);
255+
await assertImage(await fs.readFile(path.resolve(outdir, `1-${rand}-1-1.jpg`)), 'image/jpeg', controls[1]);
256+
await assertImage(await fs.readFile(path.resolve(outdir, `2-${rand}-2-2.jpg`)), 'image/jpeg', controls[1]);
210257
});
211258

212259
it('can write a single non-default image to standard out', async () => {
@@ -216,7 +263,7 @@ describe('heic-convert', () => {
216263
const { stdout, stderr, err } = await exec(['--images', '2'], {}, inbuffer);
217264

218265
expect(stderr.toString()).to.equal('');
219-
await assertImage(stdout, 'image/jpeg', hashes[2].hash);
266+
await assertImage(stdout, 'image/jpeg', controls[1]);
220267
expect(err).to.have.property('code', 0);
221268
});
222269

0 commit comments

Comments
 (0)