2015-03-19 16:24:22 -04:00
|
|
|
<?xml version="1.0"?>
|
2013-02-13 09:27:33 -05:00
|
|
|
<!--
|
|
|
|
BDD specification framework
|
|
|
|
|
2022-05-03 14:14:29 -04:00
|
|
|
Copyright (C) 2014-2022 Ryan Specialty Group, LLC.
|
2015-03-18 13:32:24 -04:00
|
|
|
|
|
|
|
This file is part of tame-core.
|
|
|
|
|
|
|
|
tame-core is free software: you can redistribute it and/or modify it
|
2018-01-26 11:13:33 -05:00
|
|
|
under the terms of the GNU General Public License as
|
2015-03-18 13:32:24 -04:00
|
|
|
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/>.
|
|
|
|
|
2013-02-13 09:27:33 -05:00
|
|
|
This framework uses specifications, not stories[0]. Its syntax is heavily
|
|
|
|
motivated by popular BDD frameworks such as RSpec (Ruby), Jasmine
|
|
|
|
(JavaScript), and Mocha (JavaScript).
|
|
|
|
|
|
|
|
As an example, consider a specification for a simple stack implementation:
|
|
|
|
|
|
|
|
describe stack
|
|
|
|
describe push
|
|
|
|
it increases the stack size by 1
|
|
|
|
describe to an empty stack
|
|
|
|
it produces a stack of size 1
|
|
|
|
describe pop
|
|
|
|
it decreases the stack size by 1
|
|
|
|
it returns the most recently pushed item
|
|
|
|
describe multiple times
|
|
|
|
it returns elements in reverse order of push
|
|
|
|
describe from an empty stack
|
|
|
|
it produces an error
|
|
|
|
|
|
|
|
These templates expand all definitions flatly into their parent node;
|
|
|
|
descriptions should therefore be in the package root or within nodes that
|
|
|
|
support calculation and classification definitions.
|
|
|
|
|
|
|
|
|
|
|
|
[0]: http://en.wikipedia.org/wiki/Behavior-driven_development
|
|
|
|
#Story_versus_specification
|
|
|
|
-->
|
|
|
|
<package xmlns="http://www.lovullo.com/rater"
|
|
|
|
xmlns:t="http://www.lovullo.com/rater/apply-template"
|
2018-01-04 10:46:02 -05:00
|
|
|
xmlns:c="http://www.lovullo.com/calc"
|
2013-02-13 09:27:33 -05:00
|
|
|
|
|
|
|
core="true"
|
|
|
|
|
|
|
|
name="core/test/spec"
|
|
|
|
desc="Behavior-driven development specification system">
|
|
|
|
|
2015-03-18 11:31:47 -04:00
|
|
|
<import package="../base" />
|
2013-02-13 09:27:33 -05:00
|
|
|
|
2015-09-28 16:25:01 -04:00
|
|
|
<import package="../vector/cmatch"
|
|
|
|
export="true" />
|
2013-02-13 09:27:33 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
2015-09-28 16:25:01 -04:00
|
|
|
<template name="_verify-specs_"
|
|
|
|
desc="Declare a template suite">
|
|
|
|
<param name="@result@" desc="Name of boolean result" />
|
|
|
|
|
|
|
|
|
|
|
|
<!--
|
|
|
|
Determines whether all features are conformant to their
|
|
|
|
specifications; this classification is a simple way to determine
|
|
|
|
whether a test suite has passed.
|
|
|
|
-->
|
|
|
|
<classify as="expect-ok"
|
|
|
|
yields="@result@"
|
2022-08-22 14:59:16 -04:00
|
|
|
desc="All features conform to specifications">
|
2015-09-28 16:25:01 -04:00
|
|
|
<inline-template>
|
|
|
|
<for-each>
|
|
|
|
<sym-set name-prefix="expect-conform-"
|
|
|
|
type="class" />
|
|
|
|
</for-each>
|
|
|
|
|
|
|
|
<t:match-class name="{@sym_name@}" />
|
|
|
|
</inline-template>
|
|
|
|
</classify>
|
|
|
|
</template>
|
2013-02-13 09:27:33 -05:00
|
|
|
|
|
|
|
|
|
|
|
<!--
|
|
|
|
Describe a feature specification
|
|
|
|
|
|
|
|
Each specification has a name that describes the feature as concisely as
|
|
|
|
possible. For example, if testing a stack, then `@name@` should simply
|
|
|
|
be "stack".
|
|
|
|
|
|
|
|
Specifications may be arbitrarily nested to group together
|
|
|
|
sub-features. If testing a specific feature of a parent feature, defer
|
|
|
|
as much of the description to the "it" clauses as possible.
|
|
|
|
|
|
|
|
A high-level classification will be generated that will assert on the
|
|
|
|
results of all expectations contained therein.
|
|
|
|
|
|
|
|
Permitted children:
|
|
|
|
- _describe_* - describe sub-features
|
|
|
|
- _it_+ - describe expectations
|
|
|
|
-->
|
|
|
|
<template name="_describe_"
|
|
|
|
desc="Describe a specification">
|
|
|
|
<param name="@name@" desc="Subject (SUT)" />
|
|
|
|
<param name="@values@" desc="Specification descriptions" />
|
|
|
|
|
|
|
|
|
|
|
|
<param name="@__full_name@"
|
|
|
|
desc="Full subject name, inherited from parents">
|
|
|
|
<param-inherit meta="spec-name" />
|
|
|
|
<param-value name="@name@" />
|
|
|
|
<text> </text>
|
|
|
|
</param>
|
|
|
|
|
|
|
|
<param name="@__prefix@"
|
|
|
|
desc="Generated expectation prefix">
|
|
|
|
<!-- descriptions may be nested; this may or may not exist -->
|
|
|
|
<param-inherit meta="spec-prefix" />
|
2015-09-28 16:25:01 -04:00
|
|
|
<param-value name="@name@" dash="true" identifier="true" />
|
2013-02-13 09:27:33 -05:00
|
|
|
<text>-</text>
|
|
|
|
</param>
|
|
|
|
|
|
|
|
<param name="@__uniq@"
|
|
|
|
desc="Unique id">
|
|
|
|
<text unique="true" />
|
|
|
|
</param>
|
|
|
|
|
|
|
|
|
2015-09-28 16:25:01 -04:00
|
|
|
<expand-sequence>
|
2018-10-30 11:10:39 -04:00
|
|
|
<param-copy name="@values@">
|
|
|
|
<param-meta name="spec-name"
|
|
|
|
value="@__full_name@" />
|
|
|
|
<param-meta name="spec-prefix"
|
|
|
|
value="@__prefix@" />
|
|
|
|
</param-copy>
|
2015-09-28 16:25:01 -04:00
|
|
|
|
|
|
|
<!-- joins all generated classifications to provide a higher-level
|
|
|
|
failure if any expectations fail -->
|
2018-10-30 11:10:39 -04:00
|
|
|
<!-- XXX: there is a bug in expand-sequence where it does not wait for
|
|
|
|
all template expansions before continuing to expand the next item
|
|
|
|
in the sequence -->
|
|
|
|
<expand-sequence>
|
|
|
|
<expand-sequence>
|
|
|
|
<expand-sequence>
|
|
|
|
<expand-sequence>
|
|
|
|
<expand-sequence>
|
|
|
|
<classify as="expect-conform-{@__prefix@}{@__uniq@}"
|
2022-08-22 14:59:16 -04:00
|
|
|
desc="{@__full_name@} meets expectations">
|
2018-10-30 11:10:39 -04:00
|
|
|
<inline-template>
|
|
|
|
<for-each>
|
|
|
|
<sym-set name-prefix="expect-that-{@__prefix@}"
|
|
|
|
type="class" />
|
|
|
|
</for-each>
|
|
|
|
|
|
|
|
<t:match-class name="{@sym_name@}" />
|
|
|
|
</inline-template>
|
|
|
|
</classify>
|
|
|
|
</expand-sequence>
|
|
|
|
</expand-sequence>
|
|
|
|
</expand-sequence>
|
|
|
|
</expand-sequence>
|
|
|
|
</expand-sequence>
|
2015-09-28 16:25:01 -04:00
|
|
|
</expand-sequence>
|
2013-02-13 09:27:33 -05:00
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!--
|
|
|
|
Describe a logical group of expectations
|
|
|
|
|
|
|
|
While `_describe_` delimits features (or sub-features), `_it_` delimits
|
|
|
|
logical groups of expectations of those features. Each expectation
|
|
|
|
within an `_it_` group can assert on a common `_given_` value.
|
|
|
|
|
|
|
|
The description should complete the sentence that started at the
|
|
|
|
uppermost `_describe_` ancestor; see the examples at the head of this
|
|
|
|
package.
|
|
|
|
|
|
|
|
Permitted children:
|
2016-06-10 15:36:14 -04:00
|
|
|
- * - arbitrary definitions for feature test
|
2013-02-13 09:27:33 -05:00
|
|
|
- _given_? - describe common data for expectations
|
|
|
|
- _expect_+ - describe feature expectation
|
|
|
|
-->
|
|
|
|
<template name="_it_"
|
|
|
|
desc="Describe logical group of expectations">
|
|
|
|
<param name="@desc@" desc="Description of expectation" />
|
|
|
|
<param name="@values@" desc="Expectations" />
|
|
|
|
|
|
|
|
<param name="@__id@" desc="Unique identifier">
|
|
|
|
<param-inherit meta="spec-prefix" />
|
2015-09-28 16:25:01 -04:00
|
|
|
<param-value name="@desc@" dash="true" identifier="true" />
|
2013-02-13 09:27:33 -05:00
|
|
|
</param>
|
|
|
|
|
|
|
|
|
|
|
|
<param-copy name="@values@">
|
|
|
|
<param-meta name="spec-it"
|
|
|
|
value="@desc@" />
|
|
|
|
<param-meta name="spec-it-prefix"
|
|
|
|
value="@__id@" />
|
|
|
|
</param-copy>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!--
|
|
|
|
Describe a value for expectation groups
|
|
|
|
|
|
|
|
The defined value is available to adjacent expectations through use of
|
|
|
|
`_match-result`.
|
|
|
|
|
|
|
|
A `_given_` definition is not required; it exists as a convenient and
|
|
|
|
concise way to represent test data.
|
|
|
|
|
|
|
|
Permitted children:
|
|
|
|
- Any valid calculation
|
|
|
|
-->
|
|
|
|
<template name="_given_"
|
|
|
|
desc="Describe a value for expectation groups">
|
2015-03-19 16:24:22 -04:00
|
|
|
<param name="@values@" desc="Calculation" />
|
2016-06-10 15:36:14 -04:00
|
|
|
|
|
|
|
<param name="@name@" desc="Value to reference (optional)" />
|
|
|
|
|
2013-02-13 09:27:33 -05:00
|
|
|
<param name="@__id@"
|
|
|
|
desc="Unique identifier to avoid symbol conflicts">
|
|
|
|
<text unique="true">given</text>
|
|
|
|
</param>
|
|
|
|
|
|
|
|
|
2016-06-10 15:36:14 -04:00
|
|
|
<!-- name given; no need to generate -->
|
|
|
|
<if name="@name@">
|
|
|
|
<param-meta name="spec-given-id"
|
|
|
|
value="@name@" />
|
|
|
|
</if>
|
|
|
|
|
|
|
|
<!-- no name given; generate a unique one -->
|
|
|
|
<unless name="@name@">
|
|
|
|
<param-meta name="spec-given-id"
|
|
|
|
value="@__id@" />
|
|
|
|
|
|
|
|
<rate yields="@__id@">
|
|
|
|
<param-copy name="@values@" />
|
|
|
|
</rate>
|
|
|
|
</unless>
|
2013-02-13 09:27:33 -05:00
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-06-08 14:41:54 -04:00
|
|
|
<!--
|
|
|
|
Describe a classification-based value for expectation groups
|
|
|
|
|
|
|
|
The defined value is available to adjacent expectations through use of
|
|
|
|
`_match-result_`.
|
|
|
|
|
|
|
|
A `_given_` definition is not required; it exists as a convenient and
|
|
|
|
concise way to represent test data in clear terms.
|
|
|
|
|
|
|
|
Permitted children:
|
|
|
|
- Any match
|
|
|
|
-->
|
|
|
|
<template name="_given-classify_"
|
|
|
|
desc="Describe a classification-based value for expectation groups">
|
|
|
|
<param name="@values@" desc="Classification predicates" />
|
|
|
|
|
|
|
|
<param name="@__id@"
|
|
|
|
desc="Unique identifier to avoid symbol conflicts">
|
|
|
|
<text unique="true">given</text>
|
|
|
|
</param>
|
|
|
|
|
|
|
|
|
|
|
|
<param-meta name="spec-given-id"
|
|
|
|
value="{@__id@}Yield" />
|
|
|
|
|
|
|
|
<classify as="@__id@" yields="{@__id@}Yield"
|
|
|
|
desc="Given value generated via _given-clasify_">
|
|
|
|
<param-copy name="@values@" />
|
|
|
|
</classify>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<template name="_given-classify-scalar_"
|
|
|
|
desc="Describe a classification-based value for expectation groups">
|
|
|
|
<param name="@values@" desc="Classification predicates" />
|
|
|
|
|
|
|
|
<param name="@__id@"
|
|
|
|
desc="Unique identifier to avoid symbol conflicts">
|
|
|
|
<text unique="true">given</text>
|
|
|
|
</param>
|
|
|
|
|
|
|
|
|
|
|
|
<param-meta name="spec-given-id"
|
|
|
|
value="{@__id@}Yield" />
|
|
|
|
|
|
|
|
<classify as="{@__id@}-pre" yields="{@__id@}PreYield"
|
|
|
|
desc="Given value generated via _given-clasify-scalar_ pre-scalar">
|
|
|
|
<param-copy name="@values@" />
|
|
|
|
</classify>
|
|
|
|
|
|
|
|
<rate class="{@__id@}-pre" yields="__{@__id@}ScalarSum">
|
|
|
|
<c:sum of="_CMATCH_" />
|
|
|
|
</rate>
|
|
|
|
|
|
|
|
<classify as="@__id@" yields="{@__id@}Yield"
|
|
|
|
desc="Given value generated via _given-classify_">
|
|
|
|
<match on="__{@__id@}ScalarSum">
|
|
|
|
<c:gt>
|
|
|
|
<c:const value="0" desc="Any match" />
|
|
|
|
</c:gt>
|
|
|
|
</match>
|
|
|
|
</classify>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-02-13 09:27:33 -05:00
|
|
|
<!--
|
|
|
|
Describe a feature expectation
|
|
|
|
|
|
|
|
An expectation tests the adherence of a feature to its specification. It
|
|
|
|
generates a classification and therefore shares identical child nodes.
|
|
|
|
|
|
|
|
Expectations may assert upon any value within scope. The
|
|
|
|
`_given_` template exists to simplify test calculation definitions;
|
|
|
|
asserting on the result of the adjacent `_given_` can be done using
|
|
|
|
`_match-result_`.
|
|
|
|
|
|
|
|
Permitted children:
|
|
|
|
- Any valid classification predicates
|
|
|
|
-->
|
|
|
|
<template name="_expect_"
|
|
|
|
desc="Describe a feature expectation">
|
|
|
|
<param name="@values@"
|
|
|
|
desc="Predicates" />
|
|
|
|
|
|
|
|
<!-- generated by the _describe_ template -->
|
|
|
|
<param name="@__spec_name@"
|
|
|
|
desc="Inherit specification name">
|
|
|
|
<param-inherit meta="spec-name" />
|
|
|
|
</param>
|
|
|
|
|
|
|
|
<!-- generated by the _it_ template -->
|
|
|
|
<param name="@__spec_it@"
|
|
|
|
desc="Inherit specification clause">
|
|
|
|
<param-inherit meta="spec-it" />
|
|
|
|
</param>
|
|
|
|
|
|
|
|
<!-- generate a unique id to avoid class conflicts (be sure to use the
|
|
|
|
ineherited prefix so that they can all be combined into a larger
|
|
|
|
predicate to assert on failing tests) -->
|
|
|
|
<param name="@__id@"
|
|
|
|
desc="Unique identifier">
|
|
|
|
<text>expect-that-</text>
|
|
|
|
<param-inherit meta="spec-it-prefix" />
|
|
|
|
<text unique="true">-</text>
|
|
|
|
</param>
|
|
|
|
|
|
|
|
|
|
|
|
<classify as="@__id@"
|
|
|
|
desc="{@__spec_name@}{@__spec_it@}">
|
|
|
|
<param-copy name="@values@">
|
|
|
|
<param-meta name="spec-expect-id"
|
|
|
|
value="@__id@" />
|
|
|
|
</param-copy>
|
|
|
|
</classify>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<!--
|
|
|
|
Match against result of an adjacent `_given_` expression
|
|
|
|
|
|
|
|
This has equivalent syntax to the primitive `match`, except that `@on`
|
|
|
|
is determined for you.
|
|
|
|
|
|
|
|
Permitted children:
|
|
|
|
- Anything permitted by the `match` primitive
|
|
|
|
-->
|
|
|
|
<template name="_match-result_"
|
|
|
|
desc="Match against result of an adjacent _given_ expression">
|
|
|
|
<param name="@values@"
|
|
|
|
desc="Calculation as predicate">
|
|
|
|
<text></text>
|
|
|
|
</param>
|
|
|
|
|
|
|
|
<param name="@index@" desc="Constant index">
|
|
|
|
<text></text>
|
|
|
|
</param>
|
|
|
|
|
|
|
|
<param name="@__given_id@"
|
|
|
|
desc="Unique identifier of _given_ expression">
|
|
|
|
<param-inherit meta="spec-given-id" />
|
|
|
|
</param>
|
|
|
|
|
|
|
|
<!-- choose one -->
|
|
|
|
<param name="@value@" desc="Match value" />
|
2018-01-04 10:46:02 -05:00
|
|
|
<param name="@eq@" desc="Match equality" />
|
2013-02-13 09:27:33 -05:00
|
|
|
<param name="@anyOf@" desc="Match against domain of type" />
|
|
|
|
|
|
|
|
|
|
|
|
<if name="@value@">
|
|
|
|
<match on="@__given_id@" index="@index@" value="@value@" />
|
|
|
|
</if>
|
2018-01-04 10:46:02 -05:00
|
|
|
|
2013-02-13 09:27:33 -05:00
|
|
|
<unless name="@value@">
|
2018-01-04 10:46:02 -05:00
|
|
|
<if name="@eq@">
|
2013-02-13 09:27:33 -05:00
|
|
|
<match on="@__given_id@" index="@index@">
|
2018-01-04 10:46:02 -05:00
|
|
|
<c:eq>
|
|
|
|
<c:value-of name="#{@eq@}" />
|
|
|
|
</c:eq>
|
2013-02-13 09:27:33 -05:00
|
|
|
</match>
|
2018-01-04 10:46:02 -05:00
|
|
|
</if>
|
|
|
|
|
|
|
|
<unless name="@eq@">
|
|
|
|
<if name="@anyOf@">
|
|
|
|
<match on="@__given_id@" index="@index@" anyOf="@anyOf@" />
|
|
|
|
</if>
|
|
|
|
<unless name="@anyOf@">
|
|
|
|
<match on="@__given_id@" index="@index@">
|
|
|
|
<param-copy name="@values@" />
|
|
|
|
</match>
|
|
|
|
</unless>
|
2013-02-13 09:27:33 -05:00
|
|
|
</unless>
|
|
|
|
</unless>
|
|
|
|
</template>
|
|
|
|
</package>
|