test-package-tr-284
v0.0.0
Published
o1js testing utilities
Readme
@o1js/testing
This package contains testing utils used internally in o1js to test primitives.
Table of Contents
Overview
- Property Testing: Generate random inputs to test properties of your functions
- Equivalence Testing: Compare two implementations of a function
- Constraint System Testing: Verify that your circuits generate the expected constraint systems
Installation
npm install --save-dev @o1js/testing
o1jsmust be installed in the project as peer dependency.
Features
Property Testing
The property testing allows you to test functions with randomly generated inputs, ensuring your code works across a wide range of inputs.
import { test, Random } from '@o1js/testing';
// Test that addition is commutative
test(Random.field, Random.field, (x, y) => {
const xField = Field(x);
const yField = Field(y);
// Verify that x + y = y + x
expect(xField.add(yField)).toEqual(yField.add(xField));
});import { test, Random } from '@o1js/testing';
// Test UInt8.from() correctly converts numbers to UInt8
test(Random.nat(25), (n, assert) => {
assert(UInt8.from(n).toString() === String(n));
});import { test, Random } from '@o1js/testing';
// Test UInt8.from() throws on negative numbers
test.negative(Random.int(-10, -1), (x) => UInt8.from(x));Equivalence Testing
The equivalence testing helps you test that two different implementations (e.g., one using native JavaScript types and another using o1js provable types) behave the same way.
import { bigintField, field, bool, equivalent } from '@o1js/testing';
// Field | bigint parameter
let fieldOrBigint = oneOf(field, bigintField);
equivalent({ from: [field, fieldOrBigint], to: bool })(
(x, y) => x < y,
(x, y) => x.lessThan(y)
);import { uint, field, equivalentProvable } from '@o1js/testing';
import { Gadgets } from 'o1js';
equivalentProvable({ from: [uint(64), uint(64)], to: field })(
(x, y) => x ^ y,
(x, y) => Gadgets.xor(x, y, 64)
);Constraint System Testing
The constraint system testing allows you to verify that your circuits generate the expected constraint system, which is valuable for ensuring optimal performance and the expected behavior of your provable code.
import { constraintSystem, ifNotAllConstant, withoutGenerics, equals } from '@o1js/testing';
import { Field, Gadgets } from 'o1js';
constraintSystem(
'range check 64',
{ from: [Field] },
Gadgets.rangeCheck64,
ifNotAllConstant(withoutGenerics(equals(['RangeCheck0'])))
);import { constraintSystem, ifNotAllConstant, contains } from '@o1js/testing';
import { Field, Gadgets, ZkProgram } from 'o1js';
let Bitwise = ZkProgram({
name: 'bitwise',
publicOutput: Field,
methods: {
notUnchecked: {
privateInputs: [Field],
async method(a: Field) {
return { publicOutput: Gadgets.not(a, 240, false) };
},
},
},
});
constraintSystem.fromZkProgram(Bitwise, 'notUnchecked', ifNotAllConstant(contains('Generic')));Examples
Check out src/lib/provable/test directory for more examples.
API Reference
Property Testing
test()
The main function for running property-based tests.
Parameters
...generators: Random value generatorsrun: A function that takes generated values and tests assertions
Variants
test.negative: Test that all runs fail/throw an errortest.custom: Create a customized test runner with options likeminRuns,maxRuns,timeBudget, etc.
sample()
Generate sample values from a random generator.
Parameters
random: The random generatorn: Number of samples to generate (default: 1)
withHardCoded()
Replace a random generator with hardcoded values.
Parameters
random: The original random generator...hardCoded: The values to return instead of random ones
Random
A class for generating random values of different types. It includes many methods like:
Random.constant(): Generate a constant valueRandom.int(): Generate random integers in a given rangeRandom.nat(): Generate random natural numbers up to a maximumRandom.fraction(): Generate random fractional valuesRandom.boolean(): Generate random boolean valuesRandom.byte(): Generate random byte valuesRandom.bytes(): Generate random byte arraysRandom.string(): Generate random stringsRandom.base58(): Generate random base58 stringsRandom.array(): Generate random arraysRandom.oneOf(): Choose randomly from provided optionsRandom.field(): Generate random field elementsRandom.bool(): Generate random boolean elementsRandom.uint8(),Random.uint32(),Random.uint64(): Generate random uint values- And many more specialized generators
Equivalence Testing
equivalent()
Test the equivalence of two functions with different input/output types.
Parameters
from: Specifications for converting input valuesto: Specification for converting output valuesverbose: Whether to log verbose output
equivalentAsync()
Test the equivalence of two functions with different input/output types.
Parameters
from: Specifications for converting input valuesto: Specification for converting output valuesruns: Number of test runs
equivalentProvable()
Test the equivalence of two functions where one is provable.
Parameters
from: Specifications for converting input valuesto: Specification for converting output valuesverbose: Whether to log verbose output
Specs
A Spec<T1, T2> is a type that defines how to convert between two types and compare their values. This is essential for equivalence testing. It contains:
rng: A random generator for type T1there: A function that converts from T1 to T2back: A function that converts from T2 back to T1assertEqual: (Optional) A function to compare two T1 valuesprovable: (Optional) A Provable for T2, if it's a provable type
field
A predefined ProvableSpec<bigint, Field> that converts between JavaScript's bigint and o1js's Field type.
fieldWithRng(rng)
Creates a ProvableSpec<bigint, Field> using a custom random generator for bigint values.
bigintField
A Spec<bigint, bigint> for testing with native bigint values, using the standard field random generator.
bool
A ProvableSpec<boolean, Bool> that converts between JavaScript's boolean and o1js's Bool type.
boolean
A Spec<boolean, boolean> for testing with native boolean values.
unit
A ToSpec<void, void> for testing functions that don't return values.
Spec Combinators
Spec combinators allow you to build complex specifications from simpler ones.
array(spec, size)
Creates a Spec<T[], S[]> for arrays by mapping the provided spec over each element.
Parameters
spec: The specification for array elementssize: Either a fixed number or a random generator for the array size
record(specs)
Creates a Spec for objects with fields that follow the specifications in the specs object.
Parameters
specs: An object mapping field names to their specifications
map({ from, to }, transform)
Transforms a specification by mapping the input values.
Parameters
from: The source specificationto: The target specificationtransform: A function that transforms inputs of the source type
onlyIf(spec, predicate)
Filters the random inputs of a specification using a predicate function.
Parameters
spec: The base specificationpredicate: A function that returns true for inputs that should be kept
fromRandom(rng)
Creates a simple Spec<T, T> from a random generator, using identity functions for conversion.
Parameters
rng: The random generator for type T
first(spec)
Creates a Spec<T, T> from a Spec<T, S> by using only the input type.
Parameters
spec: The original specification
second(spec)
Creates a Spec<S, S> from a Spec<T, S> by using only the output type.
Parameters
spec: The original specification
constant(spec, value)
Modifies a spec to always use a fixed input value.
Parameters
spec: The original specificationvalue: The constant value to use
Utils
Utility functions for working with specs and testing.
oneOf(...specs)
Creates a union specification that randomly selects from multiple specs for generating test inputs.
Parameters
...specs: A list of specifications to choose from
throwError(message?)
A utility function to throw an error with an optional message.
Parameters
message: Optional error message
handleErrors(op1, op2, useResults?, label?)
A helper function that ensures two operations throw the same errors or produce comparable results.
Parameters
op1: The first operation to executeop2: The second operation to executeuseResults: Optional function to compare the resultslabel: Optional label for error messages
defaultAssertEqual
The default equality assertion function (uses Node's deepEqual).
id(x)
An identity function that returns its input unchanged.
Parameters
x: The input value
spec(options)
A factory function for creating Spec objects with various configurations.
Parameters
options: Configuration for the spec, includingrng,there,back,assertEqual, andprovable
Constraint System Testing
Constraint System Testing helps you verify that your circuits generate the expected constraints. This is essential for ensuring both the correctness and performance of circuits.
constraintSystem(label, inputs, main, constraintSystemTest)
Tests properties of the constraint system generated by a circuit.
Parameters
label: Description of the constraint system for error reportinginputs: Input specification in the form{ from: [...provables] }main: The circuit method to testconstraintSystemTest: The property test to run on the constraint system
constraintSystem.fromZkProgram(program, methodName, test)
Convenience function to run constraintSystem directly on a method of a ZkProgram.
Parameters
program: The ZkProgram to testmethodName: Name of the method to testtest: The property test to run on the constraint system
Additional ConstraintSystem utilities
constraintSystem.gates(inputs, main): Get the constraint system as a list of gatesconstraintSystem.size(inputs, main): Get the number of gates in the constraint systemconstraintSystem.print(inputs, main): Print the constraint systemconstraintSystem.summary(inputs, main): Get a summary of gate types in the constraint system
ConstraintSystemTest Functions
The constraint system testing API provides a DSL for asserting properties of constraint systems.
Basic Tests
fulfills(label, testFn)
General-purpose test using a custom testing function.
Parameters
label: Description of the testtestFn: Function that takes gates and inputs and returns a boolean result
equals(gates)
Tests that a constraint system exactly matches the specified gates, in order.
Parameters
gates: Array of gate types to match exactly
contains(gates)
Tests that a constraint system contains the specified gates in sequence.
Parameters
gates: A gate type, array of gate types, or array of arrays of gate types
Examples
// Test for a single gate type
contains('Rot64')
// Test for consecutive gates
contains(['Rot64', 'RangeCheck0'])
// Test for multiple sequences of gates in order
contains([['Rot64', 'RangeCheck0'], ['Add', 'Mul']])isEmpty
Tests whether the constraint system has no gates.
allConstant
Tests whether all inputs are constant values.
Test Combinators
not(test)
Negates a test result.
Parameters
test: The test to negate
and(...tests)
Requires all specified tests to pass.
Parameters
...tests: List of tests that must all pass
or(...tests)
Requires at least one of the specified tests to pass.
Parameters
...tests: List of tests where at least one must pass
ifNotAllConstant(test)
Modifies a test to only apply when not all inputs are constant. When all inputs are constant, checks that the constraint system is empty.
Parameters
test: The test to conditionally apply
withoutGenerics(test)
Applies a test to the constraint system with Generic gates filtered out.
Parameters
test: The test to apply to the filtered system
Utility Functions
print
A "test" that always passes but pretty-prints the constraint system for debugging.
repeat(n, gates)
Utility function to repeat a gate or gate sequence multiple times.
Parameters
n: Number of repetitionsgates: Gate type or array of gate types to repeat
