covertable
Version:
Efficient TypeScript library for pairwise testing, generating minimal covering arrays with constraint support.
200 lines (199 loc) • 5.69 kB
TypeScript
import type { Controller } from "./controller";
export type ScalarType = number | string;
export type DictType = {
[s: string]: any;
};
export type ListType = {
[index: number]: any[];
};
export type SerialsType = Map<ScalarType, PairType>;
export type ParentsType = Map<number, ScalarType>;
export type IndicesType = Map<number, number>;
export type FilterRowType = {
[key: string]: any;
[index: number]: any;
};
export type ArrayTupleType = any[][];
export type ArrayObjectType = {
[s: string]: any[];
};
export type FactorsType = ArrayTupleType | ArrayObjectType;
export type SuggestRowType<T extends FactorsType> = T extends ArrayTupleType ? T[number][number][] : T extends ArrayObjectType ? {
[K in keyof T]: T[K][number];
} : unknown;
export type PairType = number[];
export type PairByKeyType = Map<ScalarType, PairType>;
export type CandidateType = [ScalarType, number][];
export interface RowType {
}
export type SorterType = (pairs: PairType[], sortArgs: SortArgsType) => PairType[];
export interface SortArgsType {
salt: ScalarType;
indices: IndicesType;
}
export interface CriterionArgsType {
row: RowType;
parents: ParentsType;
strength: number;
tolerance: number;
}
export interface SubModelType {
fields: ScalarType[];
strength: number;
}
export type WeightsType = {
[factorKey: string]: {
[index: number]: number;
};
};
export type PresetRowType = {
[key: string]: any;
[index: number]: any;
};
/**
* An operand is either a field reference (string, supports dot notation
* like `"payment.method"`) or an arithmetic expression.
*/
export type Operand = string | ArithmeticExpression;
/**
* Arithmetic expressions compute a value from two operands.
* Both `left` and `right` can be field references or nested expressions.
* When `right` is omitted, `value` provides a literal operand.
*/
export type ArithmeticExpression = {
operator: 'add' | 'sub' | 'mul' | 'div' | 'mod' | 'pow';
left: Operand;
right: Operand;
} | {
operator: 'add' | 'sub' | 'mul' | 'div' | 'mod' | 'pow';
left: Operand;
value: any;
};
/**
* A comparison condition. `left` is the first operand (field reference or
* expression). The second operand is either `right` (another field/expression)
* or `value` (a literal). For `in`, use `values` (an array of literals).
*/
export type ComparisonExpression = {
operator: 'eq';
left: Operand;
value: any;
} | {
operator: 'eq';
left: Operand;
right: Operand;
} | {
operator: 'ne';
left: Operand;
value: any;
} | {
operator: 'ne';
left: Operand;
right: Operand;
} | {
operator: 'gt';
left: Operand;
value: any;
} | {
operator: 'gt';
left: Operand;
right: Operand;
} | {
operator: 'lt';
left: Operand;
value: any;
} | {
operator: 'lt';
left: Operand;
right: Operand;
} | {
operator: 'gte';
left: Operand;
value: any;
} | {
operator: 'gte';
left: Operand;
right: Operand;
} | {
operator: 'lte';
left: Operand;
value: any;
} | {
operator: 'lte';
left: Operand;
right: Operand;
} | {
operator: 'in';
left: Operand;
values: any[];
};
export type LogicalExpression = {
operator: 'not';
condition: Expression;
} | {
operator: 'and';
conditions: Expression[];
} | {
operator: 'or';
conditions: Expression[];
};
/**
* Escape hatch for constraints that cannot be expressed declaratively.
* The engine cannot perform three-valued reasoning on these — when a
* dependency key is missing the condition is treated as `null`.
* Provide `requires` so the engine knows when it is safe to call `evaluate`.
*/
export type FnExpression = {
operator: 'fn';
requires: string[];
evaluate: (row: {
[key: string]: any;
}) => boolean;
};
export type Expression = ComparisonExpression | LogicalExpression | FnExpression;
/**
* Custom comparison functions. Each key matches a comparison operator name.
* When provided, the corresponding function is called instead of the default
* JS comparison. The function receives two resolved (non-undefined) values
* and must return a boolean.
*
* The `in` comparer receives the field value and the values array.
*
* `undefined` values (= field not yet set in the row) are **never** passed
* to a comparer — the engine returns `null` before reaching the
* comparer in that case.
*/
export interface Comparer {
eq?: (a: any, b: any) => boolean;
ne?: (a: any, b: any) => boolean;
gt?: (a: any, b: any) => boolean;
lt?: (a: any, b: any) => boolean;
gte?: (a: any, b: any) => boolean;
lte?: (a: any, b: any) => boolean;
in?: (value: any, values: Set<any>) => boolean;
}
export interface OptionsType<T extends FactorsType> {
strength?: number;
subModels?: SubModelType[];
weights?: WeightsType;
presets?: PresetRowType[];
sorter?: SorterType;
criterion?: (ctrl: Controller<T>) => IterableIterator<PairType>;
salt?: ScalarType;
tolerance?: number;
/**
* Declarative constraints. Each entry is a `Condition` tree that the
* generator evaluates under Kleene three-valued logic: when a referenced
* field is not yet present in the row the result is `null` (deferred)
* rather than `false`, so the generator can prune early without
* discarding viable combinations.
*
* The top-level array is an implicit AND: every condition must be
* satisfied for a row to be accepted.
*/
constraints?: Expression[];
/**
* Custom comparison functions. See `Comparer` for details.
*/
comparer?: Comparer;
}