tame/core/test/core/class.xml

734 lines
24 KiB
XML

<?xml version="1.0"?>
<!--
Copyright (C) 2014-2023 Ryan Specialty, LLC.
This file is part of tame-core.
tame-core is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<package xmlns="http://www.lovullo.com/rater"
xmlns:c="http://www.lovullo.com/calc"
xmlns:t="http://www.lovullo.com/rater/apply-template"
desc="Classification System Specs">
<import package="../../test/spec" />
<import package="../../vector/stub" />
<import package="../../base" />
Note that many of these classifications may match on similar values to try
to thwart potential optimizations,
present or future,
but these approaches
may need further adjustment to thwart future optimizations (or a way to
explicitly inhibit them).
These tests are also written a bit lazily,
given the difficulties in matching comprehensively;
that ought to be fixed in the future.
<const name="MAT3X3" desc="3x3 Matrix, Ones">
<set desc="Row 0">
<item value="1" desc="0,0" />
<item value="1" desc="0,1" />
<item value="1" desc="0,2" />
</set>
<set desc="Row 1">
<item value="1" desc="1,0" />
<item value="1" desc="1,1" />
<item value="1" desc="1,2" />
</set>
<set desc="Row 2">
<item value="1" desc="2,0" />
<item value="1" desc="2,1" />
<item value="1" desc="2,2" />
</set>
</const>
<const name="MAT3X3Z" desc="3x3 Matrix, Zeroes">
<set desc="Row 0">
<item value="0" desc="0,0" />
<item value="0" desc="0,1" />
<item value="0" desc="0,2" />
</set>
<set desc="Row 1">
<item value="0" desc="1,0" />
<item value="0" desc="1,1" />
<item value="0" desc="1,2" />
</set>
<set desc="Row 2">
<item value="0" desc="2,0" />
<item value="0" desc="2,1" />
<item value="0" desc="2,2" />
</set>
</const>
<const name="MAT3X3OOZ" desc="3x3 Matrix, Columns 1, 1, 0">
<set desc="Row 0">
<item value="1" desc="0,0" />
<item value="1" desc="0,1" />
<item value="0" desc="0,2" />
</set>
<set desc="Row 1">
<item value="1" desc="1,0" />
<item value="1" desc="1,1" />
<item value="0" desc="1,2" />
</set>
<set desc="Row 2">
<item value="1" desc="2,0" />
<item value="1" desc="2,1" />
<item value="0" desc="2,2" />
</set>
</const>
<const name="MAT3X1" desc="3x2 Matrix, Ones">
<set desc="Row 0">
<item value="1" desc="0,0" />
</set>
<set desc="Row 1">
<item value="1" desc="1,0" />
</set>
<set desc="Row 2">
<item value="1" desc="2,0" />
</set>
</const>
<const name="MAT3X1Z" desc="3x2 Matrix, Zeroes">
<set desc="Row 0">
<item value="0" desc="0,0" />
</set>
<set desc="Row 1">
<item value="0" desc="1,0" />
</set>
<set desc="Row 2">
<item value="0" desc="2,0" />
</set>
</const>
<const name="MAT1X3Z" desc="1x3 Matrix, Zeroes">
<set desc="Row 0">
<item value="0" desc="0,0" />
<item value="0" desc="0,1" />
<item value="0" desc="0,2" />
</set>
</const>
<template name="_class-tests_" desc="Classification system tests">
<param name="@system@" desc="SUT (lowercase)" />
<param name="@systemuc@" desc="SUT (title case)">
<param-value name="@system@" ucfirst="true" />
</param>
<t:describe name="{@system@} classify">
<t:describe name="without predicates">
<t:it desc="yields TRUE for conjunction">
<classify as="conj-no-pred-{@system@}"
yields="conjNoPred{@systemuc@}"
desc="No predicate, conjunction" />
<t:given name="conjNoPred{@systemuc@}" />
<t:expect>
<t:match-result value="TRUE" />
</t:expect>
</t:it>
<t:it desc="yields FALSE for disjunction">
<classify as="disj-no-pred-{@system@}"
yields="disjNoPred{@systemuc@}"
any="true"
desc="No predicate, disjunction" />
<t:given name="disjNoPred{@systemuc@}" />
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
</t:describe>
<t:describe name="with scalar predicates">
<t:it desc="yields TRUE when scalar value is TRUE">
<t:given-classify>
<match on="alwaysTrue" />
</t:given-classify>
<t:expect>
<t:match-result value="TRUE" />
</t:expect>
</t:it>
<t:it desc="yields FALSE when scalar value is FALSE">
<t:given-classify>
<match on="neverTrue" />
</t:given-classify>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
<t:it desc="yields TRUE for all-true scalar conjunction">
<t:given-classify>
<match on="alwaysTrue" />
<match on="neverTrue" value="FALSE" />
</t:given-classify>
<t:expect>
<t:match-result value="TRUE" />
</t:expect>
</t:it>
<t:it desc="yields TRUE for all-true scalar disjunction">
<t:given-classify>
<any>
<match on="alwaysTrue" />
<match on="neverTrue" value="FALSE" />
</any>
</t:given-classify>
<t:expect>
<t:match-result value="TRUE" />
</t:expect>
</t:it>
<t:it desc="yields TRUE for single-true scalar disjunction">
<t:given-classify>
<any>
<match on="alwaysTrue" />
<match on="neverTrue" />
</any>
</t:given-classify>
<t:expect>
<t:match-result value="TRUE" />
</t:expect>
</t:it>
</t:describe>
<t:describe name="with vector predicates">
<t:it desc="yields TRUE for all-true element-wise conjunction">
<t:given-classify-scalar>
<match on="NVEC3" value="ZERO" />
<match on="nClass3" value="TRUE" />
</t:given-classify-scalar>
<t:expect>
<t:match-result value="TRUE" />
</t:expect>
</t:it>
<t:it desc="yields FALSE for some-true element-wise conjunction">
<t:given-classify-scalar>
<match on="NVEC3" value="ZERO" />
<match on="nClass3" value="FALSE" />
</t:given-classify-scalar>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
<t:it desc="yields TRUE for some-true element-wise disjunction">
<t:given-classify-scalar>
<any>
<match on="NVEC3" value="ZERO" />
<match on="nClass3" value="FALSE" />
</any>
</t:given-classify-scalar>
<t:expect>
<t:match-result value="TRUE" />
</t:expect>
</t:it>
<t:it desc="yields FALSE for all-false element-wise disjunction">
<t:given-classify-scalar>
<any>
<match on="NVEC3" value="TRUE" />
<match on="nClass3" value="FALSE" />
</any>
</t:given-classify-scalar>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
The old classification system would interpret missing values as $0$,
which could potentially trigger a match.
The new classification system will always yield \tparam{FALSE}
regardless of predicate when values are undefined.
<t:describe name="of different lengths">
<if name="@system@" eq="legacy">
<t:describe name="with legacy classification system">
<t:it desc="interprets undefined values as zero during match">
<classify as="vec-len-mismatch-conj-{@system@}"
yields="vecLenMismatchConj{@systemuc@}"
desc="Multi vector length mismatch (legacy)">
<!-- actually ZERO for all indexes -->
<match on="NVEC3" value="ZERO" />
<!-- legacy system, implicitly zero for match -->
<match on="NVEC2" value="ZERO" />
</classify>
<t:given>
<c:value-of name="vecLenMismatchConj{@systemuc@}" index="#2" />
</t:given>
<t:expect>
<t:match-result value="TRUE" />
</t:expect>
</t:it>
</t:describe>
</if>
<if name="@system@" eq="new">
<t:describe name="with new classification system">
<t:it desc="yields false for conjunction rather than implicit zero">
<classify as="vec-len-mismatch-conj-{@system@}"
yields="vecLenMismatchConj{@systemuc@}"
desc="Multi vector length mismatch (new system)">
<!-- actually ZERO for all indexes -->
<match on="NVEC3" value="ZERO" />
<!-- must not be implicitly ZERO for third index -->
<match on="NVEC2" value="ZERO" />
</classify>
<t:given>
<c:value-of name="vecLenMismatchConj{@systemuc@}" index="#2" />
</t:given>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
</t:describe>
</if>
</t:describe>
</t:describe>
<t:describe name="with matrix predicates">
<t:it desc="yields TRUE for all-true element-wise conjunction">
<t:given-classify-scalar>
<match on="MAT3X3Z" value="FALSE" />
<match on="MAT3X3" value="TRUE" />
</t:given-classify-scalar>
<t:expect>
<t:match-result value="TRUE" />
</t:expect>
</t:it>
<t:it desc="yields FALSE for some-true element-wise conjunction">
<t:given-classify-scalar>
<match on="MAT3X3Z" value="TRUE" />
<match on="MAT3X3" value="TRUE" />
</t:given-classify-scalar>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
<t:it desc="yields TRUE for some-true element-wise disjunction">
<t:given-classify-scalar>
<any>
<match on="MAT3X3Z" value="ZERO" />
<match on="MAT3X3" value="ZERO" />
</any>
</t:given-classify-scalar>
<t:expect>
<t:match-result value="TRUE" />
</t:expect>
</t:it>
<t:it desc="yields FALSE for all-false element-wise disjunction">
<t:given-classify-scalar>
<any>
<match on="MAT3X3Z" value="TRUE" />
<match on="MAT3X3" value="FALSE" />
</any>
</t:given-classify-scalar>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
<t:describe name="of different column lengths">
Certain behavior is the same between the old and the new system---%
in particular,
when the match of lower length is first.
<classify as="mat-len-mismatch-first-conj-{@system@}"
yields="matLenMismatchFirstConj{@systemuc@}"
desc="Multi matrix length mismatch when first match">
<!-- fallthrough for undefined (note that this is
intentionally matching on FALSE to test against an
implicit 0 in place of undefined) -->
<match on="MAT3X1Z" value="FALSE" />
<!-- first two columns ones, last column zero -->
<match on="MAT3X3OOZ" value="TRUE" />
</classify>
<t:it desc="always yields FALSE when first match (TRUE)">
<t:given>
<c:value-of name="matLenMismatchFirstConj{@systemuc@}">
<c:index>
<c:value-of name="#1" />
</c:index>
<c:index>
<c:value-of name="#1" />
</c:index>
</c:value-of>
</t:given>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
<t:it desc="always yields FALSE when first match (FALSE)">
<t:given>
<c:value-of name="matLenMismatchFirstConj{@systemuc@}">
<c:index>
<c:value-of name="#2" />
</c:index>
<c:index>
<c:value-of name="#2" />
</c:index>
</c:value-of>
</t:given>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
<if name="@system@" eq="legacy">
<t:describe name="with legacy classification system">
The legacy system is frightenly problematic when the matrix of
lesser column length appears after the first match---%
the commutative properites of the system are lost,
and the value from the previous match falls through!
<classify as="mat-len-mismatch-conj-{@system@}"
yields="matLenMismatchConj{@systemuc@}"
desc="Multi matrix length mismatch (legacy)">
<!-- first two columns ones, last column zero -->
<match on="MAT3X3OOZ" value="TRUE" />
<!-- fallthrough for undefined (note that this is
intentionally matching on FALSE to test against an
implicit 0 in place of undefined) -->
<match on="MAT3X1Z" value="FALSE" />
</classify>
<!-- which means that it's not cummutatitve! -->
<t:it desc="causes values from previous match to fall through
into undefined (TRUE)">
<t:given>
<c:value-of name="matLenMismatchConj{@systemuc@}">
<c:index>
<c:value-of name="#1" />
</c:index>
<c:index>
<c:value-of name="#1" />
</c:index>
</c:value-of>
</t:given>
<t:expect>
<t:match-result value="TRUE" />
</t:expect>
</t:it>
<t:it desc="causes values from previous match to fall through
into undefined (FALSE)">
<t:given>
<c:value-of name="matLenMismatchConj{@systemuc@}">
<c:index>
<c:value-of name="#2" />
</c:index>
<c:index>
<c:value-of name="#2" />
</c:index>
</c:value-of>
</t:given>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
</t:describe>
</if>
<if name="@system@" eq="new">
<t:describe name="with new classification system">
<classify as="mat-len-mismatch-conj-{@system@}"
yields="matLenMismatchConj{@systemuc@}"
desc="Multi matrix length mismatch (new)">
<!-- first two columns ones, last column zero -->
<match on="MAT3X3OOZ" value="TRUE" />
<!-- must not fall through like legacy (must always be
FALSE; note that this is intentionally matching on
FALSE to test against an implicit 0 in place of
undefined) -->
<match on="MAT3X1Z" value="FALSE" />
</classify>
<!-- which means that it's not cummutatitve! -->
<t:it desc="is FALSE regardless of previous match or current
value (TRUE)">
<t:given>
<c:value-of name="matLenMismatchConj{@systemuc@}">
<c:index>
<c:value-of name="#1" />
</c:index>
<c:index>
<c:value-of name="#1" />
</c:index>
</c:value-of>
</t:given>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
<t:it desc="is FALSE regardless of previous match or current
value (FALSE)">
<t:given>
<c:value-of name="matLenMismatchConj{@systemuc@}">
<c:index>
<c:value-of name="#2" />
</c:index>
<c:index>
<c:value-of name="#2" />
</c:index>
</c:value-of>
</t:given>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
</t:describe>
</if>
</t:describe>
<t:describe name="of different row lengths">
<if name="@system@" eq="legacy">
<t:describe name="with legacy classification system">
The legacy classification system does something terrible when
the second match is the shorter---%
it discards the indexes entirely!
<classify as="mat-len-mismatch-rows-conj-{@system@}"
yields="matLenMismatchRowsConj{@systemuc@}"
desc="Multi matrix row mismatch (legacy)">
<!-- we _should_ have a 3x3 result matrix -->
<match on="MAT3X3OOZ" value="TRUE" />
<!-- but instead we get [[1, 1, 1], [0], [0]] because of
this match being second! -->
<match on="MAT1X3Z" value="FALSE" />
</classify>
<classify as="mat-len-mismatch-rows-first-conj-{@system@}"
yields="matLenMismatchRowsFirstConj{@systemuc@}"
desc="Multi matrix row mismatch first match (legacy)">
<match on="MAT1X3Z" value="TRUE" />
<match on="MAT3X3OOZ" value="TRUE" />
</classify>
<!-- note that this is testing buggy behavior; the new system
corrects it -->
<t:it desc="replaces all inner vectors of other rows">
<t:given>
<c:length-of>
<c:value-of name="matLenMismatchRowsConj{@systemuc@}">
<c:index>
<c:value-of name="#1" />
</c:index>
</c:value-of>
</c:length-of>
</t:given>
<t:expect>
<!-- were it not for this bug, it should be 3 -->
<t:match-result value="#1" />
</t:expect>
</t:it>
<!-- note that this is testing buggy behavior; the new system
corrects it -->
<t:it desc="considers only defined rows' values when smaller
is first">
<t:given>
<c:value-of name="matLenMismatchRowsFirstConj{@systemuc@}">
<c:index>
<c:value-of name="#1" />
</c:index>
<c:index>
<c:value-of name="#0" />
</c:index>
</c:value-of>
</t:given>
<!-- we get [[0, 0, 0], [1, 1, 0], [1, 1, 0]] -->
<!-- ^ -->
<t:expect>
<t:match-result value="#1" />
</t:expect>
</t:it>
</t:describe>
</if>
<if name="@system@" eq="new">
<t:describe name="with new classification system">
<classify as="mat-len-mismatch-rows-conj-{@system@}"
yields="matLenMismatchRowsConj{@systemuc@}"
desc="Multi matrix row mismatch (new)">
<match on="MAT3X3OOZ" value="TRUE" />
<!-- must yield FALSE rather than matching on 0 -->
<match on="MAT1X3Z" value="FALSE" />
</classify>
<classify as="mat-len-mismatch-rows-first-conj-{@system@}"
yields="matLenMismatchRowsFirstConj{@systemuc@}"
desc="Multi matrix row mismatch first match (new)">
<match on="MAT1X3Z" value="TRUE" />
<match on="MAT3X3OOZ" value="TRUE" />
</classify>
<t:it desc="retains shape of larger matrix">
<t:given>
<c:length-of>
<c:value-of name="matLenMismatchRowsConj{@systemuc@}">
<c:index>
<c:value-of name="#2" />
</c:index>
</c:value-of>
</c:length-of>
</t:given>
<t:expect>
<!-- unlike legacy system -->
<t:match-result value="#3" />
</t:expect>
</t:it>
<t:it desc="always yields FALSE for each undefined element (TRUE)">
<t:given>
<c:length-of>
<c:value-of name="matLenMismatchRowsConj{@systemuc@}">
<c:index>
<c:value-of name="#2" />
</c:index>
<c:index>
<c:value-of name="#1" />
</c:index>
</c:value-of>
</c:length-of>
</t:given>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
<t:it desc="always yields FALSE for each undefined element (FALSE)">
<t:given>
<c:length-of>
<c:value-of name="matLenMismatchRowsConj{@systemuc@}">
<c:index>
<c:value-of name="#1" />
</c:index>
<c:index>
<c:value-of name="#2" />
</c:index>
</c:value-of>
</c:length-of>
</t:given>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
<!-- unlike legacy -->
<t:it desc="is commutative with different row lengths">
<t:given>
<c:value-of name="matLenMismatchRowsFirstConj{@systemuc@}">
<c:index>
<c:value-of name="#1" />
</c:index>
<c:index>
<c:value-of name="#2" />
</c:index>
</c:value-of>
</t:given>
<t:expect>
<t:match-result value="FALSE" />
</t:expect>
</t:it>
</t:describe>
</if>
</t:describe>
</t:describe>
</t:describe>
</template>
<section title="Legacy System Tests">
<t:class-tests system="legacy" />
</section>
<section title="New System Tests">
<t:use-new-classification-system />
<t:class-tests system="new" />
</section>
</package>