wasm: Add minimal coverage for complete rule definitions

These changes also extend the test runner to allow disabling partial
evaluation (which is needed since complete definitons are inlined
normally), error checking, and module fixtures.

Signed-off-by: Torin Sandall <torinsandall@gmail.com>
This commit is contained in:
Torin Sandall
2019-05-16 15:35:00 -07:00
parent e5e470920e
commit 52e0853bb8
3 changed files with 206 additions and 19 deletions
+170
View File
@@ -0,0 +1,170 @@
cases:
- note: constants
disable_partial: true
query: data.x.p = 1
modules:
- |
package x
p = 1
return_code: 1
- note: constants (negative)
disable_partial: true
query: data.x.p = 1
modules:
- |
package x
p = 2
return_code: 0
- note: variable
disable_partial: true
query: data.x.p = 1
modules:
- |
package x
p = y { x = 1; y = x }
return_code: 1
- note: variable (negative)
disable_partial: true
query: data.x.p = 1
modules:
- |
package x
p = y { x = 2; y = x }
return_code: 0
- note: conjunction
disable_partial: true
query: data.x.p = 1
modules:
- |
package x
p = 1 { q; r }
q { true }
r { true }
return_code: 1
- note: conjunction (negative)
disable_partial: true
query: data.x.p = 1
modules:
- |
package x
p = 1 { q; r }
q { true }
r { false }
return_code: 0
- note: disjunction
disable_partial: true
query: data.x.p = 2
modules:
- |
package x
p = 1 { false }
p = 2 { true }
p = 3 { false }
return_code: 1
- note: disjunction (negative)
disable_partial: true
query: data.x.p = 1
modules:
- |
package x
p = 1 { false }
p = 2 { false }
p = 3 { true }
return_code: 0
- note: negation
disable_partial: true
query: not data.x.p = 1
modules:
- |
package x
p = 1 { false } # undefined
return_code: 1
- note: negation (negative)
disable_partial: true
query: not data.x.p = 1
modules:
- |
package x
p = 1
return_code: 0
- note: chain
disable_partial: true
query: data.x.p = 1
modules:
- |
package x
p = q
q = r
r = 1
return_code: 1
- note: chain (negative)
disable_partial: true
query: data.x.p = 1
modules:
- |
package x
p = q
q = r
r = 2
return_code: 0
- note: chain input
disable_partial: true
query: data.x.p = true
modules:
- |
package x
p = q
q = r
r { input.x = 1 }
input: {"x": 1}
return_code: 1
- note: chain input (negative)
disable_partial: true
query: data.x.p = true
modules:
- |
package x
p = q
q = r
r { input.x = 2 }
input: {"x": 1}
return_code: 0
- note: iteration
disable_partial: true
query: data.x.p = 1
modules:
- |
package x
p = 1 { input[x] = 1 }
input: [3,2,1]
return_code: 1
- note: iteration (negative)
disable_partial: true
query: data.x.p = 1
modules:
- |
package x
p = 1 { input[x] = 4 }
input: [3,2,1]
return_code: 0
- note: conflict error
disable_partial: true
query: data.x.p = 1
modules:
- |
package x
p = 1
p = 2
want_error: "unreachable" # TODO(tsandall): replace with conflict error.
- note: packages
disable_partial: true
query: data.x.p = 1
modules:
- |
package x
import data.y.p
p = p
- |
package y
p = 1
return_code: 1
+21 -14
View File
@@ -1,7 +1,7 @@
const { readFileSync, readdirSync } = require('fs');
function stringDecoder(mem) {
return function(addr) {
return function (addr) {
const i8 = new Int8Array(mem.buffer);
const start = addr;
var s = "";
@@ -47,10 +47,10 @@ function now() {
function formatMicros(us) {
if (us <= 1000) {
return us + 'µs'
} else if (us <= 1000*1000) {
} else if (us <= 1000 * 1000) {
return (us / 1000).toFixed(4) + 'ms'
} else {
return (us / (1000*1000)).toFixed(4) + 's'
return (us / (1000 * 1000)).toFixed(4) + 's'
}
}
@@ -60,14 +60,14 @@ function evaluate(mem, policy, input) {
const addr = policy.instance.exports.opa_malloc(str.length);
const buf = new Uint8Array(mem.buffer);
for(let i = 0; i < str.length; i++) {
buf[addr+i] = str.charCodeAt(i);
for (let i = 0; i < str.length; i++) {
buf[addr + i] = str.charCodeAt(i);
}
const returnCode = policy.instance.exports.eval(addr, str.length);
return {returnCode: returnCode};
return { returnCode: returnCode };
}
function namespace(cache, key) {
@@ -82,7 +82,7 @@ function namespace(cache, key) {
async function test() {
const mem = new WebAssembly.Memory({initial: 5});
const mem = new WebAssembly.Memory({ initial: 5 });
const addr2string = stringDecoder(mem);
const t0 = now();
@@ -115,12 +115,12 @@ async function test() {
let dirty = false;
let cache = {};
for(let i = 0; i < testCases.length; i++) {
for (let i = 0; i < testCases.length; i++) {
const policy = await WebAssembly.instantiate(testCases[i].wasmBytes, {
env: {
memory: mem,
opa_abort: function(addr) {
opa_abort: function (addr) {
throw addr2string(addr);
},
},
@@ -132,9 +132,16 @@ async function test() {
try {
const result = evaluate(mem, policy, testCases[i].input);
passed = result.returnCode === testCases[i].return_code;
} catch(e) {
passed = false;
error = e;
} catch (e) {
if (testCases[i].want_error === undefined) {
passed = false;
error = e;
} else if (e.message.includes(testCases[i].want_error)) {
passed = true;
} else {
passed = false;
error = e;
}
}
if (passed) {
@@ -152,7 +159,7 @@ async function test() {
const dt_end = t_end - t_load;
if (dirty) {
console.log();
console.log();
}
console.log('SUMMARY:');
@@ -170,7 +177,7 @@ async function test() {
console.log();
console.log('TOOK:', formatMicros(dt_end));
if ((numFailed+numErrors) > 0) {
if ((numFailed + numErrors) > 0) {
process.exit(1);
}
}
+15 -5
View File
@@ -9,6 +9,7 @@ import (
"compress/gzip"
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
@@ -32,10 +33,13 @@ type testCaseSet struct {
}
type testCase struct {
Note string `json:"note"`
Query string `json:"query"`
Input *interface{} `json:"input"`
ReturnCode int `json:"return_code"`
Note string `json:"note"`
Query string `json:"query"`
Modules []string `json:"modules"`
DisablePartial bool `json:"disable_partial"`
Input *interface{} `json:"input"`
ReturnCode int `json:"return_code"`
WantError string `json:"want_error"`
}
type compiledTestCaseSet struct {
@@ -50,7 +54,13 @@ type compiledTestCase struct {
func compileTestCases(ctx context.Context, tests testCaseSet) (*compiledTestCaseSet, error) {
var result []compiledTestCase
for _, tc := range tests.Cases {
cr, err := rego.New(rego.Query(tc.Query)).Compile(ctx)
args := []func(*rego.Rego){
rego.Query(tc.Query),
}
for idx, module := range tc.Modules {
args = append(args, rego.Module(fmt.Sprintf("module%d.rego", idx), module))
}
cr, err := rego.New(args...).Compile(ctx, rego.CompilePartial(!tc.DisablePartial))
if err != nil {
return nil, err
}