Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Recursive Arbs #1515

Merged
merged 14 commits into from
Jan 18, 2024
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ jobs:
"property_tests/tests/principal",
"property_tests/tests/query_methods",
"property_tests/tests/record",
"property_tests/tests/recursive",
"property_tests/tests/service",
"property_tests/tests/stable_b_tree_map",
"property_tests/tests/text",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import fc from 'fast-check';
import { candidDefinitionMemo } from '.';
import { BlobDefinitionArb } from '../constructed/blob_arb/definition_arb';
import { OptDefinitionArb } from '../constructed/opt_arb/definition_arb';
import { RecordDefinitionArb } from '../constructed/record_arb/definition_arb';
import { TupleDefinitionArb } from '../constructed/tuple_arb/definition_arb';
import { VariantDefinitionArb } from '../constructed/variant_arb/definition_arbs';
import { VecDefinitionArb } from '../constructed/vec_arb/definition_arb';
import { FuncDefinitionArb } from '../reference/func_arb/definition_arb';
import { ServiceDefinitionArb } from '../reference/service_arb/definition_arb';
import {
CandidDefinitionMemo,
DefinitionConstraints,
RecursiveCandidName
} from './types';

export type ComplexDefinitionWeights = Partial<{
blob: number;
opt: number;
record: number;
tuple: number;
variant: number;
vec: number;
func: number;
service: number;
}>;

// The number of options below (blob, func, opt, etc)
export const COMPLEX_ARB_COUNT = 8;

export function complexCandidDefinitionMemo(
parents: RecursiveCandidName[],
constraints: DefinitionConstraints = {}
): CandidDefinitionMemo {
const weights = constraints.weights ?? {};
const newConstraints: DefinitionConstraints = {
depthLevel: constraints.depthLevel,
weights:
constraints.recursiveWeights ?? false
? constraints.weights ?? {}
: {},
recursiveWeights: constraints.recursiveWeights
};
return fc.memo((depthLevel) => {
return fc.oneof(
{
arbitrary: BlobDefinitionArb(),
weight: weights.blob ?? 1
},
{
arbitrary: FuncDefinitionArb(
candidDefinitionMemo([], newConstraints)(depthLevel)
),
weight: weights.func ?? 1
},
{
arbitrary: OptDefinitionArb(
candidDefinitionMemo,
parents,
newConstraints
),
weight: weights.opt ?? 1
},
{
arbitrary: RecordDefinitionArb(
candidDefinitionMemo([], newConstraints)(depthLevel)
),
weight: weights.record ?? 1
},
{
arbitrary: TupleDefinitionArb(
candidDefinitionMemo([], newConstraints)(depthLevel)
),
weight: weights.tuple ?? 1
},
{
arbitrary: VariantDefinitionArb(
candidDefinitionMemo,
parents,
newConstraints
),
weight: weights.variant ?? 1
},
{
arbitrary: VecDefinitionArb(
candidDefinitionMemo,
parents,
constraints
),
weight: weights.vec ?? 1
},
{
arbitrary: ServiceDefinitionArb(
candidDefinitionMemo([], newConstraints)(depthLevel)
),
weight: weights.service ?? 0
// TODO Service is disabled until it is more refined. Maybe the
// only thing missing is deepEquals
}
);
});
}
137 changes: 57 additions & 80 deletions property_tests/arbitraries/candid/candid_definition_arb/index.ts
Original file line number Diff line number Diff line change
@@ -1,85 +1,62 @@
import fc from 'fast-check';
import { BoolDefinitionArb } from '../primitive/bool';
import { Float32DefinitionArb } from '../primitive/floats/float32_arb';
import { Float64DefinitionArb } from '../primitive/floats/float64_arb';
import { FuncDefinitionArb } from '../reference/func_arb/definition_arb';
import { Int16DefinitionArb } from '../primitive/ints/int16_arb';
import { Int32DefinitionArb } from '../primitive/ints/int32_arb';
import { Int64DefinitionArb } from '../primitive/ints/int64_arb';
import { Int8DefinitionArb } from '../primitive/ints/int8_arb';
import { IntDefinitionArb } from '../primitive/ints/int_arb';
import { Nat16DefinitionArb } from '../primitive/nats/nat16_arb';
import { Nat32DefinitionArb } from '../primitive/nats/nat32_arb';
import { Nat64DefinitionArb } from '../primitive/nats/nat64_arb';
import { Nat8DefinitionArb } from '../primitive/nats/nat8_arb';
import { NatDefinitionArb } from '../primitive/nats/nat_arb';
import { OptDefinitionArb } from '../constructed/opt_arb/definition_arb';
import { PrincipalDefinitionArb } from '../reference/principal_arb';
import { RecordDefinitionArb } from '../constructed/record_arb/definition_arb';
import { TextDefinitionArb } from '../primitive/text';
import { TupleDefinitionArb } from '../constructed/tuple_arb/definition_arb';
import { VariantDefinitionArb } from '../constructed/variant_arb/definition_arbs';
import { VecDefinitionArb } from '../constructed/vec_arb/definition_arb';
import {
CandidDefinition,
FuncCandidDefinition,
OptCandidDefinition,
RecordCandidDefinition,
TupleCandidDefinition,
VariantCandidDefinition,
VecCandidDefinition,
CandidDefinitionArb
CandidDefinitionArb,
RecursiveCandidName,
CandidDefinitionMemo,
DefinitionConstraints
} from './types';
import { BlobDefinitionArb } from '../constructed/blob_arb/definition_arb';
import {
COMPLEX_ARB_COUNT,
complexCandidDefinitionMemo
} from './complex_candid_definition_memo';
import {
PRIM_ARB_COUNT,
primitiveCandidDefinitionArb
} from './simple_candid_definition_arb';
import {
REC_ARB_COUNT,
recursiveCandidDefinitionMemo
} from './recursive_candid_definition_memo';
import { DEFAULT_DEFINITION_MAX_DEPTH } from '../../config';
import { RecursiveShapes } from '../recursive';

export function candidDefinitionArb(
recursiveShapes: RecursiveShapes,
parents: RecursiveCandidName[] = [],
constraints: DefinitionConstraints = {}
): CandidDefinitionArb {
return candidDefinitionMemo(
parents,
constraints
)(constraints.depthLevel ?? DEFAULT_DEFINITION_MAX_DEPTH);
}

export function candidDefinitionArb(): CandidDefinitionArb {
return fc.letrec((tie) => ({
CandidDefinition: fc.oneof(
BlobDefinitionArb(),
tie('Opt').map((sample) => sample as OptCandidDefinition),
tie('Record').map((sample) => sample as RecordCandidDefinition),
tie('Tuple').map((sample) => sample as TupleCandidDefinition),
tie('Variant').map((sample) => sample as VariantCandidDefinition),
tie('Vec').map((sample) => sample as VecCandidDefinition),
BoolDefinitionArb(),
Float32DefinitionArb(),
Float64DefinitionArb(),
IntDefinitionArb(),
Int8DefinitionArb(),
Int16DefinitionArb(),
Int32DefinitionArb(),
Int64DefinitionArb(),
NatDefinitionArb(),
Nat8DefinitionArb(),
Nat16DefinitionArb(),
Nat32DefinitionArb(),
Nat64DefinitionArb(),
// NullDefinitionArb(), // Must be excluded until https://github.com/demergent-labs/azle/issues/1453 gets resolved
TextDefinitionArb(),
tie('Func').map((sample) => sample as FuncCandidDefinition),
PrincipalDefinitionArb()
// tie('Service').map((sample) => sample as ServiceCandidDefinition) // Services Aren't working with deep equals
),
Func: FuncDefinitionArb(
tie('CandidDefinition') as fc.Arbitrary<CandidDefinition>
),
Opt: OptDefinitionArb(
tie('CandidDefinition') as fc.Arbitrary<CandidDefinition>
),
Record: RecordDefinitionArb(
tie('CandidDefinition') as fc.Arbitrary<CandidDefinition>
),
// Service: ServiceDefinitionArb(
// tie('CandidDefinition') as fc.Arbitrary<CandidDefinition>
// ),
Tuple: TupleDefinitionArb(
tie('CandidDefinition') as fc.Arbitrary<CandidDefinition>
),
Variant: VariantDefinitionArb(
tie('CandidDefinition') as fc.Arbitrary<CandidDefinition>
),
Vec: VecDefinitionArb(
tie('CandidDefinition') as fc.Arbitrary<CandidDefinition>
)
})).CandidDefinition;
export function candidDefinitionMemo(
parents: RecursiveCandidName[],
constraints: DefinitionConstraints = {}
): CandidDefinitionMemo {
return fc.memo((depthLevel) => {
if (depthLevel <= 1) {
return primitiveCandidDefinitionArb();
}
return fc.oneof(
{
arbitrary: primitiveCandidDefinitionArb(constraints.weights),
weight: PRIM_ARB_COUNT
},
{
arbitrary: complexCandidDefinitionMemo(
parents,
constraints
)(depthLevel - 1),
weight: COMPLEX_ARB_COUNT
},
{
arbitrary: recursiveCandidDefinitionMemo(parents)(
depthLevel - 1
),
weight: REC_ARB_COUNT
}
);
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import fc from 'fast-check';
import { RecursiveDefinitionArb } from '../recursive/definition_arb';
import { complexCandidDefinitionMemo } from './complex_candid_definition_memo';
import { RecursiveCandidName, CandidDefinitionMemo } from './types';

// The number of options below (it's just recursive)
export const REC_ARB_COUNT = 0;
// TODO there are a lot of bugs with recursion so we are disabling the whole thing until these issues are resolved
// https://github.com/demergent-labs/azle/issues/1518
// https://github.com/demergent-labs/azle/issues/1513
// https://github.com/demergent-labs/azle/issues/1525

export function recursiveCandidDefinitionMemo(
parents: RecursiveCandidName[]
): CandidDefinitionMemo {
return fc.memo((depthLevel) =>
RecursiveDefinitionArb(complexCandidDefinitionMemo, parents, {
depthLevel: depthLevel
})
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import fc from 'fast-check';
import { BoolDefinitionArb } from '../primitive/bool';
import { Float32DefinitionArb } from '../primitive/floats/float32_arb';
import { Float64DefinitionArb } from '../primitive/floats/float64_arb';
import { Int16DefinitionArb } from '../primitive/ints/int16_arb';
import { Int32DefinitionArb } from '../primitive/ints/int32_arb';
import { Int64DefinitionArb } from '../primitive/ints/int64_arb';
import { Int8DefinitionArb } from '../primitive/ints/int8_arb';
import { IntDefinitionArb } from '../primitive/ints/int_arb';
import { Nat16DefinitionArb } from '../primitive/nats/nat16_arb';
import { Nat32DefinitionArb } from '../primitive/nats/nat32_arb';
import { Nat64DefinitionArb } from '../primitive/nats/nat64_arb';
import { Nat8DefinitionArb } from '../primitive/nats/nat8_arb';
import { NatDefinitionArb } from '../primitive/nats/nat_arb';
import { NullDefinitionArb } from '../primitive/null';
import { TextDefinitionArb } from '../primitive/text';
import { PrincipalDefinitionArb } from '../reference/principal_arb';
import { PrimitiveDefinition, WithShapesArb } from './types';

export type PrimitiveDefinitionWeights = Partial<{
bool: number;
float32: number;
float64: number;
int: number;
int8: number;
int16: number;
int32: number;
int64: number;
nat: number;
nat8: number;
nat16: number;
nat32: number;
nat64: number;
null: number;
text: number;
principal: number;
}>;

// The number of options below (bool, float32, float64, int, nat, etc)
export const PRIM_ARB_COUNT = 16;

const PRIM_DEF_WEIGHTS_DEFAULT = {};

export function primitiveCandidDefinitionArb(
constraints: PrimitiveDefinitionWeights = PRIM_DEF_WEIGHTS_DEFAULT
): WithShapesArb<PrimitiveDefinition> {
return fc.oneof(
{ arbitrary: BoolDefinitionArb(), weight: constraints.bool ?? 1 },
{ arbitrary: Float32DefinitionArb(), weight: constraints.float32 ?? 1 },
{ arbitrary: Float64DefinitionArb(), weight: constraints.float64 ?? 1 },
{ arbitrary: IntDefinitionArb(), weight: constraints.int ?? 1 },
{ arbitrary: Int8DefinitionArb(), weight: constraints.int8 ?? 1 },
{ arbitrary: Int16DefinitionArb(), weight: constraints.int16 ?? 1 },
{ arbitrary: Int32DefinitionArb(), weight: constraints.int32 ?? 1 },
{ arbitrary: Int64DefinitionArb(), weight: constraints.int64 ?? 1 },
{ arbitrary: NatDefinitionArb(), weight: constraints.nat ?? 1 },
{ arbitrary: Nat8DefinitionArb(), weight: constraints.nat8 ?? 1 },
{ arbitrary: Nat16DefinitionArb(), weight: constraints.nat16 ?? 1 },
{ arbitrary: Nat32DefinitionArb(), weight: constraints.nat32 ?? 1 },
{ arbitrary: Nat64DefinitionArb(), weight: constraints.nat64 ?? 1 },
{ arbitrary: NullDefinitionArb(), weight: constraints.null ?? 0 }, // TODO Null must be excluded until https://github.com/demergent-labs/azle/issues/1453 gets resolved
{ arbitrary: TextDefinitionArb(), weight: constraints.text ?? 1 },
{
arbitrary: PrincipalDefinitionArb(),
weight: constraints.principal ?? 1
}
);
}
40 changes: 39 additions & 1 deletion property_tests/arbitraries/candid/candid_definition_arb/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,35 @@ import fc from 'fast-check';
import { CandidType } from '../candid_type';
import { CandidType as RuntimeCandidType } from '../../../../src/lib/candid/candid_type';
import { ServiceMethodDefinition } from '../reference/service_arb/service_method_arb';
import { PrimitiveDefinitionWeights } from './simple_candid_definition_arb';
import { ComplexDefinitionWeights } from './complex_candid_definition_memo';
import { RecursiveShapes } from '../recursive';

export type CandidDefinitionArb = fc.Arbitrary<CandidDefinition>;
export type CandidDefinitionMemo = (
depthLevel: number
) => WithShapesArb<CandidDefinition>;
export type RecursiveCandidDefinitionMemo = (
parents: RecursiveCandidName[],
constraints?: DefinitionConstraints
) => CandidDefinitionMemo;

export type DefinitionConstraints = Partial<{
depthLevel: number;
recursiveWeights: boolean;
weights: CandidDefinitionWeights;
}>;

export type CandidDefinitionWeights = Partial<
Record<
keyof ComplexDefinitionWeights | keyof PrimitiveDefinitionWeights,
number
>
>;

export type WithShapes<T> = { definition: T; recursiveShapes: RecursiveShapes };
export type WithShapesArb<T> = fc.Arbitrary<WithShapes<T>>;

export type CandidDefinitionArb = WithShapesArb<CandidDefinition>;

export type CandidDefinition =
| MultiTypeConstructedDefinition
Expand Down Expand Up @@ -62,6 +89,17 @@ export type ServiceCandidDefinition = {
funcs: ServiceMethodDefinition[];
};

// Recursive
export type RecursiveCandidName = {
candidMeta: CandidMeta;
name: string;
};
export type RecursiveCandidDefinition = {
candidMeta: CandidMeta;
name: string;
innerType: CandidDefinition;
};

type CandidMeta = {
candidTypeAnnotation: string; // Either a type reference or type literal
candidTypeObject: string;
Expand Down
Loading
Loading