Skip to content

Commit b60a783

Browse files
committed
feat: Add concurrency option to integration-browser
Add a `-c` option to integration-browser that will increase the number of browsers tested. This speeds up the integration tests considerably. The -c option, takes a number or the string ‘cpu’. The string will set the concurrency to the number of cpus - 1.
1 parent e784679 commit b60a783

File tree

7 files changed

+621
-139
lines changed

7 files changed

+621
-139
lines changed

modules/integration-browser/karma.conf.js

+15-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
// Karma configuration
22
process.env.CHROME_BIN = require('puppeteer').executablePath()
3+
const { readFileSync } = require('fs')
34

45
module.exports = function (config) {
6+
// karma-parallel will use the number CPUs as the default number of browsers to spawn
7+
// But ideally this would be a command line option.
8+
// Since I'm already using these files to pass information back and forth,
9+
// I'm just coopting the path.
10+
const concurrency = JSON.parse(readFileSync('./fixtures/concurrency.json'))
511
config.set({
612
basePath: '',
7-
frameworks: ['jasmine'],
13+
frameworks: ['parallel', 'jasmine'],
814
files: [
915
'fixtures/decrypt_tests.json',
1016
'fixtures/encrypt_tests.json',
1117
'fixtures/decrypt_oracle.json',
18+
'/fixtures/concurrency.json',
1219
{ pattern: 'fixtures/*.json', included: false, served: true, watched: false, nocache: true },
1320
'build/module/integration.decrypt.test.js',
1421
'build/module/integration.encrypt.test.js',
@@ -18,8 +25,8 @@ module.exports = function (config) {
1825
'build/module/integration.encrypt.test.js': ['webpack', 'credentials'],
1926
'./fixtures/decrypt_tests.json': ['json_fixtures'],
2027
'./fixtures/encrypt_tests.json': ['json_fixtures'],
21-
'./fixtures/decrypt_oracle.json': ['json_fixtures']
22-
28+
'./fixtures/decrypt_oracle.json': ['json_fixtures'],
29+
'./fixtures/concurrency.json': ['json_fixtures'],
2330
},
2431
webpack: {
2532
mode: 'development',
@@ -32,6 +39,7 @@ module.exports = function (config) {
3239
devtool: 'inline-source-map'
3340
},
3441
plugins: [
42+
'karma-parallel',
3543
'@aws-sdk/karma-credential-loader',
3644
'karma-webpack',
3745
'karma-json-fixtures-preprocessor',
@@ -52,6 +60,9 @@ module.exports = function (config) {
5260
},
5361
singleRun: true,
5462
concurrency: Infinity,
55-
exclude: ['**/*.d.ts']
63+
exclude: ['**/*.d.ts'],
64+
parallelOptions: {
65+
executors: concurrency
66+
}
5667
})
5768
}

modules/integration-browser/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"karma-jasmine": "^3.1.1",
3232
"karma-json-fixtures-preprocessor": "0.0.6",
3333
"karma-webpack": "^4.0.2",
34+
"karma-parallel": "^0.3.1",
3435
"puppeteer": "^2.1.1",
3536
"stream-to-promise": "^2.2.0",
3637
"tslib": "^1.9.3",

modules/integration-browser/src/cli.ts

+17-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
import yargs from 'yargs'
1818
import { spawnSync } from 'child_process'
19+
import { cpus } from 'os'
20+
import { needs } from '@aws-crypto/client-browser'
1921

2022
import { join } from 'path'
2123
import { existsSync, mkdirSync, writeFileSync } from 'fs'
@@ -65,6 +67,19 @@ const cli = yargs
6567
describe: 'start karma and run the tests',
6668
type: 'boolean'
6769
})
70+
.option('concurrency', {
71+
alias: 'c',
72+
describe: `an optional concurrency for running tests, pass 'cpu' to maximize`,
73+
default: 1,
74+
coerce: (value: any) => {
75+
if (typeof value === 'string') {
76+
needs(value.toLowerCase() === 'cpu', `The only supported string is 'cpu'`)
77+
return cpus().length - 1
78+
}
79+
needs(typeof value === 'number' && value > 0, `Must be a number greater than 0`)
80+
return value
81+
}
82+
})
6883
.demandCommand()
6984
const fixtures = join(__dirname, '../../fixtures')
7085
/* Sad side effect. */
@@ -73,10 +88,11 @@ if (!existsSync(fixtures)) {
7388
}
7489

7590
;(async (argv) => {
76-
const { _: [ command ], testName, slice, karma } = argv
91+
const { _: [ command ], testName, slice, karma, concurrency } = argv
7792

7893
writeFileSync(`${fixtures}/decrypt_tests.json`, JSON.stringify([]))
7994
writeFileSync(`${fixtures}/encrypt_tests.json`, JSON.stringify([]))
95+
writeFileSync(`${fixtures}/concurrency.json`, JSON.stringify(concurrency))
8096

8197
if (command === 'decrypt') {
8298
// It is not clear how to get yargs/typescript to play nicely with sub commands

modules/integration-browser/src/integration.decrypt.test.ts

+33-21
Original file line numberDiff line numberDiff line change
@@ -27,24 +27,36 @@ const notSupportedMessages = [
2727
'192-bit AES keys are not supported',
2828
'Unsupported right now'
2929
]
30-
describe('browser decryption vectors', function () {
31-
const tests: string[] = __fixtures__['fixtures/decrypt_tests']
32-
33-
for (const testName of tests) {
34-
it(testName, async () => {
35-
console.log(`start: ${testName}`)
36-
const response = await fetch(`base/fixtures/${testName}.json`)
37-
const { keysInfo, cipherText, plainText } = await response.json()
38-
39-
const cipher = fromBase64(cipherText)
40-
const good = fromBase64(plainText)
41-
try {
42-
const cmm = await decryptMaterialsManagerWebCrypto(keysInfo)
43-
const { plaintext } = await decrypt(cmm, cipher)
44-
expect(good).toEqual(plaintext)
45-
} catch (e) {
46-
if (!notSupportedMessages.includes(e.message)) throw e
47-
}
48-
})
49-
}
50-
})
30+
31+
const tests: string[] = __fixtures__['fixtures/decrypt_tests']
32+
const chunk = __fixtures__['fixtures/concurrency'] || 1
33+
34+
for (let i = 0, j = tests.length; i < j; i += chunk) {
35+
aGroup(chunk, tests.slice(i, i + chunk))
36+
}
37+
38+
function aGroup (groupNumber: number, tests: string[]) {
39+
describe(`browser decryption vectors: ${groupNumber}`, () => {
40+
for (const testName of tests) {
41+
aTest(testName)
42+
}
43+
})
44+
}
45+
46+
function aTest (testName: string) {
47+
it(testName, async () => {
48+
console.log(`start: ${testName}`)
49+
const response = await fetch(`base/fixtures/${testName}.json`)
50+
const { keysInfo, cipherText, plainText } = await response.json()
51+
52+
const cipher = fromBase64(cipherText)
53+
const good = fromBase64(plainText)
54+
try {
55+
const cmm = await decryptMaterialsManagerWebCrypto(keysInfo)
56+
const { plaintext } = await decrypt(cmm, cipher)
57+
expect(good).toEqual(plaintext)
58+
} catch (e) {
59+
if (!notSupportedMessages.includes(e.message)) throw e
60+
}
61+
})
62+
}

modules/integration-browser/src/integration.encrypt.test.ts

+43-31
Original file line numberDiff line numberDiff line change
@@ -29,34 +29,46 @@ const notSupportedMessages = [
2929
'frameLength out of bounds: 0 > frameLength >= 4294967295',
3030
'Unsupported right now'
3131
]
32-
describe('browser encrypt tests', function () {
33-
const tests = __fixtures__['fixtures/encrypt_tests']
34-
const decryptOracle = __fixtures__['fixtures/decrypt_oracle']
35-
36-
for (const testName of tests) {
37-
it(testName, async () => {
38-
console.log(`start: ${testName}`)
39-
const response = await fetch(`base/fixtures/${testName}.json`)
40-
const { keysInfo, plainTextData, encryptOp } = await response.json()
41-
42-
const plainText = fromBase64(plainTextData)
43-
try {
44-
const cmm = await encryptMaterialsManagerWebCrypto(keysInfo)
45-
const { result } = await encrypt(cmm, plainText, encryptOp)
46-
const response = await fetch(decryptOracle, {
47-
method: 'POST',
48-
headers: {
49-
'Content-Type': 'application/octet-stream',
50-
'Accept': 'application/octet-stream'
51-
},
52-
body: result
53-
})
54-
const body = await response.arrayBuffer()
55-
needs(response.ok, `Failed to decrypt: ${toUtf8(body)}`)
56-
expect(plainText).toEqual(new Uint8Array(body))
57-
} catch (e) {
58-
if (!notSupportedMessages.includes(e.message)) throw e
59-
}
60-
})
61-
}
62-
})
32+
33+
const tests = __fixtures__['fixtures/encrypt_tests']
34+
const decryptOracle = __fixtures__['fixtures/decrypt_oracle']
35+
const chunk = __fixtures__['fixtures/concurrency'] || 1
36+
37+
for (let i = 0, j = tests.length; i < j; i += chunk) {
38+
aGroup(chunk, tests.slice(i, i + chunk), decryptOracle)
39+
}
40+
41+
function aGroup (groupNumber: number, tests: string[], decryptOracle: string) {
42+
describe(`'browser encrypt tests': ${groupNumber}`, () => {
43+
for (const testName of tests) {
44+
aTest(testName, decryptOracle)
45+
}
46+
})
47+
}
48+
49+
function aTest (testName: string, decryptOracle: string) {
50+
it(testName, async () => {
51+
console.log(`start: ${testName}`)
52+
const response = await fetch(`base/fixtures/${testName}.json`)
53+
const { keysInfo, plainTextData, encryptOp } = await response.json()
54+
55+
const plainText = fromBase64(plainTextData)
56+
try {
57+
const cmm = await encryptMaterialsManagerWebCrypto(keysInfo)
58+
const { result } = await encrypt(cmm, plainText, encryptOp)
59+
const response = await fetch(decryptOracle, {
60+
method: 'POST',
61+
headers: {
62+
'Content-Type': 'application/octet-stream',
63+
'Accept': 'application/octet-stream'
64+
},
65+
body: result
66+
})
67+
const body = await response.arrayBuffer()
68+
needs(response.ok, `Failed to decrypt: ${toUtf8(body)}`)
69+
expect(plainText).toEqual(new Uint8Array(body))
70+
} catch (e) {
71+
if (!notSupportedMessages.includes(e.message)) throw e
72+
}
73+
})
74+
}

0 commit comments

Comments
 (0)