diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..ad44c3b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "support/xspec"] + path = lib/xspec + url = https://github.com/xspec/xspec.git diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7592591 --- /dev/null +++ b/Makefile @@ -0,0 +1,54 @@ +include common/make_common.mk + +# We'd like to descend to each subdirectory (recursively) that has a makefile +# Makefile wildcard function does not support that, so we use the shell +# function with the find utility and examine each Makefile in a child dir +# relative to this one, excluding this one to use with the FOREACH macro. +dirs:=$(shell find '.' ! -wholename ./Makefile -name 'Makefile' -printf "%h\n") + +# This enables project-level configuration. Each project's own Makefile +# can arrange its own testing accordingly, while making it easy to expose new tests to +# the CI/CD runtime, by using any of the targets provided for here. + +# Make logic modeled after an example provided by NW: we are still learning + +module_path:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) +output_folder:=$(module_path)/test_output + +# $folder can be passed in as folder=[folder] +folder?=. + + +# See Makefile configuration in project files for examples of how to set up calls to 'make' +# including wiring XSpec tests and wiring XSpec tests to these (top-level) targets + + +.PHONY: test +test: ## Run all tests + $(call FOREACH_MAKE,$@,$(dirs)) + + +.PHONY: pre-checks +pre-checks: ## Run all "pre checks", enforcing validation contracts across input artifacts + $(call FOREACH_MAKE_OPTIONAL,$@,$(dirs)) + + +.PHONY: smoke-test +smoke-test: ## Run all "smoke tests", establishing a baseline of sanity across the project + $(call FOREACH_MAKE_OPTIONAL,$@,$(dirs)) + + +.PHONY: spec-test +spec-test: ## Run all "specification tests", validating an implementation against a spec + $(call FOREACH_MAKE_OPTIONAL,$@,$(dirs)) + + +.PHONY: unit-test +unit-test: ## Run all unit tests (ci/cd) + $(call FOREACH_MAKE_OPTIONAL,$@,$(dirs)) + + +.PHONY: clean +clean: ## Remove any generated test or build artifacts + rm -fr $(output_folder)/* + $(call FOREACH_MAKE_OPTIONAL,$@,$(dirs)) diff --git a/README.md b/README.md index b5fa045..4e9e14d 100644 --- a/README.md +++ b/README.md @@ -10,28 +10,53 @@ This repository offers a library of XSLT stylesheets and pipelines supporting ge It is motivated primarily by the need to make available to the community and public logic that we have coded for use in our projects, but that could (more or less easily) be refitted to uses elsewhere. -Most of the code here is not intended to be used standalone, but rather as imports (generally `xsl:import`) from external code. As documented per utility, however, demonstrations may be included to show how to use them, which can be executed independently of any other configuration. Additionally, some functionality has been unit tested (sometimes fairly exhaustively), with unit tests included. +The development model for the repository is to aim for a state of punctuated equilibrium, in which a stable code base remains unchanged functionally even while supporting ongoing development and innovation. Each project in this repository should be maintained and documented independently, with its own readme and testing documents. Thus the place to start with any project is the project itself. -To use these transformations a conformant XSLT 3.0 transformation engine is required such as [Saxon 11](https://saxonica.com/documentation11/documentation.xml) from Saxonica (see SourceForge for the free-to-use HE version), which is available on several platforms and sometimes bundled with commercial software. Outside that practical requirement, this library is free to use and open for contributions. +At the same time, the different projects will, over the long term, be able to borrow logic from one another to support testing, unit testing, and runtime interfaces supporting broader distribution. -Applications here for the most part assume inputs to be OSCAL XML, as distinct from OSCAL in other data formats such as JSON and YAML. Please convert your data first into XML before attempting to work further with these tools. XSLT-based data converters for OSCAL (capable of generating OSCAL XML from valid OSCAL JSON) are available in the [main repository](https://github.com/usnistgov/OSCAL/tree/main/xml/convert). - -If there is interest in XSLT to support OSCAL JSON, YAML or other notations, please express this requirement to the developers. +To use these transformations a conformant XSLT 3.0 transformation engine is required such as [Saxon 12](https://saxonica.com/documentation12/documentation.xml) from Saxonica (see [Github](https://github.com/Saxonica/Saxon-HE/) or [Maven](https://central.sonatype.com/artifact/net.sf.saxon/Saxon-HE?smo=true) for the free-to-use HE version), which is available on several platforms and sometimes bundled with commercial software. Outside that practical requirement, this library is free to use and open for contributions. ### Project purpose and maturity -The [OSCAL project](https://pages.nist.gov/OSCAL) has published XSLT since 2018. Over this time it has grown in complexity, warranting a reorganization and refactoring of the development efforts that have supported it. - -Current refactoring (2022) encapsulates XSLT functionality supporting OSCAL into two repositories, the [OSCAL XSLT](https://github.com/usnistgov/oscal-xslt) repository, and this one. This repository includes logic of general use (i.e., not specific to OSCAL use cases or implementation), and is kept kept separate in order to facilitate reuse of these offerings outside the OSCAL context, potentially (but not necessarily) as a git submodule. +Some of this code originates with the [OSCAL project](https://pages.nist.gov/OSCAL), which has published XSLT since 2018. Over this time it has grown in complexity and refactored into several supporting repositories, including this one. Future development of this resource depends largely on uptake and engagement from users who find it valuable. If you have an interest in keeping this code base viable, please make your needs and ideas known to the developers. Demonstrations that take us further into features of XSLT 3 that are demonstrably useful, whether in functionality or architecture (e.g., packaging), are particularly welcome. See the readme in each project to gauge its scope of application, approach to design, and level of testing. Several of the applications are also accompanied by test suites using the [XSpec XSLT Unit Testing framework](https://github.com/xspec/xspec/). -### Repository contents +## Repository contents See the subdirectory list for projects and applications currently supported. +Additionally to the project folders are resources at the top level: + + +- `.github` - for Github CI/CD +- `common` - holds common `bash` scripting - utilities for reuse +- `lib` - contains submodules, dynamic resources and libraries (or scripts for downloading them) + +## Running and building on applications + +As a 4GL, XSLT is well suited for certain types of applications, as it is hoped this site demonstrates. The resources offered here can be used and adapted for many different platforms and runtimes. While an effort has been made to create no dependendencies on tooling that is costly, encumbering in any way, or hard to acquire, at the same time the applications and interfaces offered and described here should not be considered to be normative or necessary, but as examples for evaluation only. + +## `make` support + +This repository supports command-line access to its tools via `make`, configured in each folder (when supported) by a `Makefile` configuration. + +From any directory, `make --help` from a `bash` command line will show scripted logic for that directory, if any is available. + +Make tasks with certain target designations can be run under CI/CD - tbd + +## CI/CD + +Currently CI/CD is set to use an XProc configuration that runs all available XSpec instances. + +It can be run locally on any platform with a configured Maven installation, using the `test` target: + +> mvn test + +Scripts for more granular or targeted application of XSpec under CI/CD are also available in the [XSpec-dev](xspec-dev/) application, requiring `bash` and `make`. + ### Rights and license See the [LICENSE.md](LICENSE.md) file. As work product of a Bureau of the Department of Commerce (U.S. Government), or of volunteers working in support of our projects, code in this repository is in the public domain unless specifically marked otherwise. diff --git a/TESTING.md b/TESTING.md new file mode 100644 index 0000000..0436363 --- /dev/null +++ b/TESTING.md @@ -0,0 +1,94 @@ +# Testing xslt3-functions + +This repository is being developed incrementally, in stages. While never ruling out a complete overhaul of organization and resources, we intend most changes to be isolated within projects, when they do not take the form of entirely new work. Accordingly, we need a testing model that is both robust and stable enough to be reliable, while also adaptable enough so that any contributor working on a project can use as much or as little of the generalized infrastructure as seems appropriate. + +Testing of utilities offered in this repository includes both informal, interactive testing per project, and in some projects, formal automated testing of XSLT functionalities using [XSpec](https://github.com/xspec/xspec). Over time it is our hope that all testing will improve by virtue of each project's being able to emulate the successful practice that is developed in any other. + +To ensure the integrity of our tests we rely on code review as well as other means. + +The full testing stack (all XSpec files unless excluded) will be run on all PRs before any git merge; the same tests should first be run off line to help reduce noise in the commit history. + +## Dependencies + +- [Apache Maven](https://maven.apache.org/) must be [installed and available on your path](https://maven.apache.org/install.html), supported with a JDK. + +We give no link for Java Development Kit installation options are available and anything that works with Maven is fine. + +*Further options*: Additionally, at developers' discretion, projects in the repo may deploy scripts supporting runtimes in GNU `bash` syntax, Windows batch scripts or others considered appropriate. These tests and runtimes are not currently provided for under CI/CD, but they are nonetheless encouraged in the spirit of *more testing*. See the testing.md or readme.md documentation of any project for more details. + +## Guidelines (tl/dr) + +- Write unit tests for XSLT using [XSpec](https://github.com/xspec/xspec). The repository already includes some excellent XSpec to study for examples of its use. +- Save your XSpec file as `*.xspec` to be found by the test runner. Place it in a directory named `xspec/pending` (in your project) if you actually wish to hide it. +- In development, you can run the tests in an IDE (make inquiries or search for more details). +- The CI/CD test run is initiated using `mvn tests` from the top. This will execute all XSpec files in the repository that are not excluded by the configuration. +- In the test run, any failure of any XSpec test in an executed XSpec that is not marked as 'pending' is considered a blocking error. +- For more granular testing or testing beyond XSpec, consider using `bash` and `make` as illustrated in project directories such as [`xspec-dev`](xspec-dev/) + +## Details + +### Verify your JDK installation + +If your JDK is installed and configured (JAVA_HOME) you should be able to execute + +``` +javac --version +``` + +And a version of the Java Compiler is announced. + +If you run into trouble, see Maven documentation for JDK requirements. + +### Verify your Maven installation + +Once it is installed and on the system path, try + +``` +> mvn --help +``` + +A working Maven installation will tell you about itself. + + +### Repository CI/CD setup + +[Under CI/CD we are currently configured](.github/workflows/maven.yml) to run the 'tests target defined in the top-level [Maven configuration pom.xml](pom.xml) file. + +With Maven and a JDK installed and the command prompt open at the repository root, run all these tests using + +``` +> mvn tests +``` + +The configuration executes *all XSpec test suites in the repository*, unless subject to exclusion rules given in the pom.xml. + +An exclusion for files matching `**/xspec/pending/**` permits developers to create an `xspec/pending` directory at any time and place XSpec files there that will *not* be run under CI/CD. + +### More about XSpec + +[XSpec](https://github.com/xspec/xspec) is a unit testing framework for XSLT supporting [behavior-driven development](https://github.com/xspec/xspec/wiki/What-is-XSpec). It was originated by Jeni Tennison and has been developed as a community-based project since 2016. + +### More about more options + +Projects in this repository also take advantage of scripts and the GNU **make** build utility to provide testing (or other) runtimes. Because they are run unless specifically designed not to, any XSpec files in place will be executed in CI/CD even while other testing utilities and harnesses can also use (or ignore) them. + +In particular, the `xspec-dev` project provides scripts that make its (XSpec-oriented) functionalities available to developers and quality engineers as black box utilities (i.e., no "XML" anywhere, just files) for running tests in more complex configurations. + +For simplicity and to reduce the burden on all contributors to this repository, these functions are not being used in the repository CI/CD - while other repositories using this one as a submodule may do so. + +At the same time, contributors with `bash` and `make` support on their systems may wish to try + +``` +> make help +``` + +from within any directory containing a `Makefile`. + +For convenience, the top level file configures a recursive descent into the file system, so all testing configured under `make` (not the same as CI/CD) can be run using + +``` +> make tests +``` + +Note to developers: the `make` configuration has its [own pom.xml](common/pom.xml) file in the [common directory](common/). + diff --git a/common/make_common.mk b/common/make_common.mk new file mode 100644 index 0000000..1f83290 --- /dev/null +++ b/common/make_common.mk @@ -0,0 +1,59 @@ +# Common makefile utilities for the metaschema-xslt project +# Note: any makefile including this file will have the following side-effects: +# 1. The SHELL will be set to bash. +# 2. A "help" target will be added. If the include statement is at the top the +# default target will become "help" unless overridden with .DEFAULT_GOAL. +# 3. The environment will be populated with the variables seen below + +# Gratefully adapted from an original by NW + +SHELL:=/usr/bin/env bash + +.PHONY: help +# Run "make" or "make help" to get a list of user targets +# Adapted from https://marmelab.com/blog/2016/02/29/auto-documented-makefile.html +# To include a target in the help text, follow the following format: +# : [dependencies] ## Comment +help: ## Show this help message + @grep --no-filename -E '^[a-zA-Z_-]+:.*?##.*$$' $(MAKEFILE_LIST) | awk 'BEGIN { \ + FS = ":.*?## "; \ + printf "\033[1m%-30s\033[0m %s\n", "TARGET", "DESCRIPTION" \ + } \ + { printf "\033[32m%-30s\033[0m %s\n", $$1, $$2 }' + +# The path to the Maven POM to use +pom_path:=./pom.xml + +define EXEC_MAVEN + mvn --quiet -f "${pom_path}" exec:java \ + -Dexec.mainClass="$1" \ + -Dexec.args="$2" +endef + +# XML Calabash helper macro +define EXEC_CALABASH + $(call EXEC_MAVEN,com.xmlcalabash.drivers.Main,$1) +endef + +# Saxon helper macro +define EXEC_SAXON + $(call EXEC_MAVEN,net.sf.saxon.Transform,$1) +endef + +define FOREACH + for $1 in $2; do {\ + $3 ;\ + } done +endef + +# Run a Makefile target for each directory, requiring each directory to have a given target +define FOREACH_MAKE + @echo Running makefile target \'$1\' on all subdirectory makefiles + @$(call FOREACH,dir,$2,$(MAKE) -C $$dir $1) +endef + +# Run a Makefile target for each directory, skipping directories whose Makefile does not contain a rule +define FOREACH_MAKE_OPTIONAL + @echo Running makefile target \'$1\' on all subdirectory makefiles that contain the rule + @$(call FOREACH,dir,$2,$(MAKE) -C $$dir -n $1 &> /dev/null && $(MAKE) -C $$dir $1 || echo "Makefile target '$1' failed or does not exist in "$$dir". Continuing...") +endef diff --git a/common/pom.xml b/common/pom.xml new file mode 100644 index 0000000..1ae6eab --- /dev/null +++ b/common/pom.xml @@ -0,0 +1,90 @@ + + 4.0.0 + + gov.nist.secauto.oscal.tools.core + java-xml-xslt-support + 1.0.0 + + + UTF-8 + + + pom + + + + + Maven Central + https://repo1.maven.org/maven2 + + + maven.restlet.org + maven.restlet.org + https://maven.restlet.talend.com + + + + + + net.sf.saxon + Saxon-HE + 11.5 + + + com.xmlcalabash + xmlcalabash + 1.5.3-110 + + + + + org.nineml + coffeegrinder + 3.2.6 + runtime + + + org.nineml + coffeefilter + 3.2.6 + runtime + + + org.nineml + coffeesacks + 3.2.6 + runtime + + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 3.5.0 + + + copy-dependencies + package + + copy-dependencies + + + + + + + + diff --git a/common/subcommand_common.bash b/common/subcommand_common.bash new file mode 100644 index 0000000..43bdfe2 --- /dev/null +++ b/common/subcommand_common.bash @@ -0,0 +1,35 @@ +# Provides common functions for subcommands + +set -Eeuo pipefail + +# Each subcommand will require Maven to invoke calabash or saxon +if ! [ -x "$(command -v mvn)" ]; then + echo 'Error: Maven (mvn) is not in the PATH, is it installed?' >&2 + exit 1 +fi + +_SUBCOMMAND_COMMON_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +POM_FILE="${_SUBCOMMAND_COMMON_DIR}/pom.xml" + +function exec_maven() { + mvn --quiet \ + -f "$POM_FILE" \ + exec:java \ + -Dexec.mainClass="$1" \ + -Dexec.args="$2" +} + +CALABASH_MAIN_CLASS="com.xmlcalabash.drivers.Main" + +function invoke_calabash() { + exec_maven "$CALABASH_MAIN_CLASS" "$@" +} + +SAXON_MAIN_CLASS="net.sf.saxon.Transform" + +function invoke_saxon() { + exec_maven "$SAXON_MAIN_CLASS" "$@" +} + +# Clean up unneeded targets +unset -f _SUBCOMMAND_COMMON_DIR diff --git a/directory-manifest/html-to-markdown.xsl b/directory-manifest/html-to-markdown.xsl index 88a2548..330131f 100644 --- a/directory-manifest/html-to-markdown.xsl +++ b/directory-manifest/html-to-markdown.xsl @@ -74,13 +74,19 @@ ` - - * - - * - - - + + * + + * + + + + ** + + ** + + + " " diff --git a/directory-manifest/manifest.md b/directory-manifest/manifest.md index 7d34697..3b77a56 100644 --- a/directory-manifest/manifest.md +++ b/directory-manifest/manifest.md @@ -2,7 +2,7 @@ # Directory Manifest: `directory-manifest` -April 12 2023 5:35 p.m. - 2023-04-12T17:35:46.4135981-04:00 - +April 3 2024 4:54 p.m. - 2024-04-03T16:54:41.77948-04:00 - Listing files suffixed `xml`, `xpl`, `sch`, `xsl`, `xslt`, `xsd` or `xspec`. @@ -20,11 +20,11 @@ Note: Logic is extensible to handle analysis/synopsis of any XML document type ### directory-manifest.xpl -Document 'c:directory' in namespace http://www.w3.org/ns/xproc-step (13 elements) +Document 'c:directory' in namespace http://www.w3.org/ns/xproc-step (14 elements) ### html-to-markdown.xsl -XSLT stylesheet version 3.0 (19 templates) +XSLT stylesheet version 3.0 (20 templates) Purpose: Cast HTML into Markdown notation @@ -64,9 +64,13 @@ Reads from (p:with-input) - `directory-listing.xsl` Reads from (p:with-input) - `html-to-markdown.xsl` -### pomx.xml +### markdown-from-html.xspec -Failure reading file pomx.xml ::: [err:FODC0002] org.xml.sax.SAXParseException; systemId: file:/C:/Users/wap1/Documents/usnistgov/xslt3-functions/directory-manifest/pomx.xml; lineNumber: 64; columnNumber: 7; The element type "build" must be terminated by the matching end-tag "".(The element type "build" must be terminated by the matching end-tag "".) +Document 'x:description' in namespace http://www.jenitennison.com/xslt/xspec (29 elements) + +### pom.xml + +Document 'project', in no namespace (35 elements) ----- diff --git a/directory-manifest/markdown-from-html.xspec b/directory-manifest/markdown-from-html.xspec new file mode 100644 index 0000000..27603b3 --- /dev/null +++ b/directory-manifest/markdown-from-html.xspec @@ -0,0 +1,61 @@ + + + + + + + + + + A page + + + +

Frankenstein

+

Or, the Modern Prometheus

+ + +
+ + +# Frankenstein + +## Or, the Modern Prometheus +
+ + + +

Four score and seven years ago --

+
+ + +Four score and seven years ago -- +
+ + + our fathers brought forth, on this continent, a new nation, conceived in Liberty and dedicated + our fathers brought forth, on this continent, a *new nation*, conceived in **Liberty** and dedicated + + + + + + + + + + + + + +
+ +
diff --git a/directory-manifest/mvn-manifest.sh b/directory-manifest/mvn-manifest.sh index 5ac31fc..9c60033 100644 --- a/directory-manifest/mvn-manifest.sh +++ b/directory-manifest/mvn-manifest.sh @@ -29,7 +29,7 @@ HEREPATH="file://${HEREPATH}" echo Producing directory manifest ... SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)" -POM_FILE="${SCRIPT_DIR}/../pom.xml" +POM_FILE="${SCRIPT_DIR}/pom.xml" PIPELINE="${SCRIPT_DIR}/make-markdown-manifest.xpl" mvn -q \ diff --git a/directory-manifest/mvn-x3f-manifest.bat b/directory-manifest/mvn-x3f-manifest.bat index 90528d9..c909b82 100644 --- a/directory-manifest/mvn-x3f-manifest.bat +++ b/directory-manifest/mvn-x3f-manifest.bat @@ -8,7 +8,7 @@ set HEREPATH=file:///%HERE:\=/% set APPLICATION_DIR=%~dp0 -call mvn --quiet -f %APPLICATION_DIR%../pom.xml exec:java -Dexec.mainClass="com.xmlcalabash.drivers.Main" -Dexec.args="-omd=%HEREPATH%/manifest.md -ohtml=NUL %APPLICATION_DIR%/directory-manifest.xpl path=%HEREPATH%" +call mvn --quiet -f %APPLICATION_DIR%pom.xml exec:java -Dexec.mainClass="com.xmlcalabash.drivers.Main" -Dexec.args="-omd=%HEREPATH%/manifest.md -odirlist=NUL -ohtml=NUL %APPLICATION_DIR%/directory-manifest.xpl path=%HEREPATH%" echo Check for file %HEREPATH%/manifest.md diff --git a/directory-manifest/mvn-x3f-manifest.sh b/directory-manifest/mvn-x3f-manifest.sh index b36c122..dfc5708 100644 --- a/directory-manifest/mvn-x3f-manifest.sh +++ b/directory-manifest/mvn-x3f-manifest.sh @@ -29,8 +29,10 @@ HEREPATH="file://${HEREPATH}" echo Producing directory manifest ... SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)" -POM_FILE="${SCRIPT_DIR}/../pom.xml" -PIPELINE="${SCRIPT_DIR}/directory-manifest.xpl" +POM_FILE="${SCRIPT_DIR}/pom.xml" +PIPELINE="file://${SCRIPT_DIR}/directory-manifest.xpl" + +echo PIPELINE is $PIPELINE mvn -q \ -f $POM_FILE \ diff --git a/directory-manifest/pomx.xml b/directory-manifest/pom.xml similarity index 96% rename from directory-manifest/pomx.xml rename to directory-manifest/pom.xml index e7bd019..c9d6e5f 100644 --- a/directory-manifest/pomx.xml +++ b/directory-manifest/pom.xml @@ -2,7 +2,7 @@ 4.0.0 gov.nist.secauto.oscal.tools.core - ci-cd-deps + xslt3-functions-support 1.0.0 @@ -61,5 +61,5 @@ - + diff --git a/ixml/.gitignore b/ixml/.gitignore new file mode 100644 index 0000000..efff1c1 --- /dev/null +++ b/ixml/.gitignore @@ -0,0 +1,4 @@ +# Download by ./download-jars.sh + +lib/*.jar +# xmlcalabash-1.5.7-120/ diff --git a/ixml/Makefile b/ixml/Makefile new file mode 100644 index 0000000..9b9cee5 --- /dev/null +++ b/ixml/Makefile @@ -0,0 +1,29 @@ +include ../common/make_common.mk + +# XML Calabash prefers absolute paths + +module_path:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) +output_folder:=test_output + + +.PHONY: test +test: spec-test ## Run all tests + + +# binding xspec-tests to spec-test brings it under CI/CD when that task is run from above +.PHONY: spec-test +spec-test: xspec-tests ## Run specification tests (currently xspec-tests) + + +.PHONY: xspec-tests +xspec-tests: ## Run XSpecs found in tests folder + mkdir -p $(output_folder) + mvn --quiet \ + -f "../common/pom.xml" \ + exec:java \ + -Dexec.mainClass="net.sf.saxon.Transform" \ + -Dexec.args="-it:go -xsl:\"../xspec-dev/XSPEC-BATCH.xsl\" -init:org.nineml.coffeesacks.RegisterCoffeeSacks stop-on-error=yes folder=random-util/tests" + +.PHONY: clean +clean: ## Remove test output + rm -fr $(output_folder)/* diff --git a/ixml/download-jars.sh b/ixml/download-jars.sh new file mode 100644 index 0000000..798dd12 --- /dev/null +++ b/ixml/download-jars.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +# This script attempts to download NineML libraries needed to provide support in oXygen XML +# NOTNEEDED (disabled clause) includes calls for other components not needed by oXygen + +mkdir -p lib + +download() { + proj=$1 + lcproj=`echo $proj | tr '[:upper:'] '[:lower:]'` + uri="https://github.com/nineml/$lcproj/releases/latest" + ver=`curl -sI $uri | grep -i location: | sed "s#^.*/tag/##" | tr -d '\n\r'` + if [ ! -f "lib/$proj-$ver.jar" ]; then + echo "Downloading $proj ..." + rm -f lib/$proj-*.jar + uri="https://github.com/nineml/$lcproj/releases/download/$ver/$lcproj-$ver.zip" + curl -s -L -o lib/$lcproj-$ver.zip $uri + cd lib + unzip -q -j $lcproj-$ver.zip "*.jar" + rm $lcproj-$ver.zip + cd .. + fi +} + +download CoffeeGrinder +download CoffeeFilter +download CoffeeSacks + +: <<'NOTNEEDED' +if [ ! -d lib/xmlcalabash1-coffeepress.jar ]; then + echo "Downloading CoffeePress ..." + curl -s -L -o coffeepress.zip \ + https://github.com/ndw/xmlcalabash1-coffeepress/releases/download/1.0.0/xmlcalabash1-coffeepress-1.0.0.zip + unzip -q -j coffeepress.zip "*.jar" + mv *.jar lib/ + rm -f coffeepress.zip +fi + +if [ ! -d xmlcalabash-1.5.7-120 ]; then + echo "Downloading XML Calabash ..." + curl -s -L -o xmlcalabash.zip \ + https://github.com/ndw/xmlcalabash1/releases/download/1.5.7-120/xmlcalabash-1.5.7-120.zip + unzip -q xmlcalabash.zip + rm -f xmlcalabash.zip +fi + +if [ ! -f saxon-he-12.3.jar ]; then + echo "Downloading Saxon 12.3 ..." + curl -s -L -o SaxonHE12-3J.zip https://www.saxonica.com/download/SaxonHE12-3J.zip + unzip -q -o SaxonHE12-3J.zip "*.jar" + rm -f SaxonHE12-3J.zip saxon-he-test*.jar saxon-he-xqj*.jar +fi +NOTNEEDED diff --git a/ixml/iso8601.ixml b/ixml/iso8601.ixml new file mode 100644 index 0000000..9a4b693 --- /dev/null +++ b/ixml/iso8601.ixml @@ -0,0 +1,171 @@ +{ An Invisible XML grammar for ISO 8601(:2004?) dates and times. } +{ Originally constructed by Norm Tovey-Walsh, 17 April 2022 } +{ See https://en.wikipedia.org/wiki/ISO_8601 among others } + +-iso8601-datetime = date + ; time + ; datetime + ; duration + ; interval + ; recurrence . + +date = calendar-date ; ordinal-date ; week-date . + +-time = local-time ; utc-time . + +-datetime = local-datetime ; utc-datetime . + +local-datetime = -date, -'T', -local-time-no-t . +utc-datetime = -date, -'T', -utc-time-no-t . + +duration = -standard-duration; -alternate-duration . + +interval = start-end-interval + ; start-plus-duration-interval + ; duration-plus-end-interval . + +recurrence = repeat-count, -'/', interval . + +{ -- Calendar dates -- } + +-calendar-date = complete-calendar-date + ; reduced-accuracy-calendar-date + ; expanded-calendar-date . + +-complete-calendar-date = year, month, day; year, -'-', month, -'-', day . + +-reduced-accuracy-calendar-date = year, -'-', month; year; century . + +-expanded-calendar-date = expanded-year, month, day + ; expanded-year, -'-', month, -'-', day + ; expanded-year, -'-', month + ; expanded-year + ; expanded-century . + +{ -- Ordinal dates -- } + +-ordinal-date = complete-ordinal-date ; expanded-ordinal-date . + +-complete-ordinal-date = year, day-of-year + ; year, -'-', day-of-year . + +-expanded-ordinal-date = expanded-year, day-of-year + ; expanded-year, -'-', day-of-year . + +{ -- Week dates -- } + +-week-date = complete-week-date + ; reduced-accuracy-week-date + ; expanded-week-date . + +-complete-week-date = year, -'W', week, day-of-week + ; year, -'-W', week, -'-', day-of-week . + +-reduced-accuracy-week-date = year, -'W', week + ; year, -'-W', week . + +-expanded-week-date = expanded-year, -'W', week, day-of-week + ; expanded-year, -'-W', week, '-', day-of-week + ; expanded-year, -'W', week + ; expanded-year, -'-W', week . + +{ -- Local time -- } + +local-time = local-time-t ; local-time-no-t . + +-local-time-t = local-time-without-utc-t; local-time-with-utc-t . +-local-time-no-t = local-time-without-utc; local-time-with-utc . + +-local-time-without-utc-t = -'T', local-time-without-utc . + +-local-time-without-utc = hour, minute, second + ; hour, -':', minute, -':', second + ; hour, minute + ; hour, -':', minute + ; hour + ; fractional-second-local-time + ; fractional-minute-local-time + ; fractional-hour-local-time . + +-local-time-with-utc-t = -local-time-without-utc-t, utc-offset . +-local-time-with-utc = -local-time-without-utc, utc-offset . + +-fractional-second-local-time = (hour, minute, second; hour, -':', minute, -':', second), + fraction-sep, + fractional-second . + +-fractional-minute-local-time = (hour, minute; hour, -':', minute), + fraction-sep, + fractional-minute . + +-fractional-hour-local-time = hour, fraction-sep, fractional-hour . + +utc-offset = offset-hour, (-':'?, offset-minute)? . + +{ -- UTC time -- } + +utc-time = utc-time-t ; utc-time-no-t . +-utc-time-t = -local-time-without-utc-t, -'Z' . +-utc-time-no-t = -local-time-without-utc, -'Z' . + +{ -- Durations -- } + +-standard-duration = -'P', (standard-ymdhs-duration ; standard-week-duration) . + +{ N.B. This tecnically allows a standard-ymdhs-duration with no fields. } +-standard-ymdhs-duration = duration-years?, duration-months?, duration-days?, + (-'T', duration-hours?, duration-minutes?, duration-seconds?)? . +-standard-week-duration = duration-weeks . + +-alternate-duration = -'P', (alternate-ymd-duration ; alternate-yd-duration) . + +-alternate-ymd-duration = year, month, day, -'T', hour, minute, second + ; year, -'-', month, -'-', day, -'T', hour, -':', minute, -':', second . +-alternate-yd-duration = year, number-of-days, -'T', hour, minute, second + ; year, -'-', number-of-days, -'T', hour, -':', minute, -':', second . + +-start-end-interval = (date; datetime), -'/', (date; datetime) . +-start-plus-duration-interval = (date; datetime), -'/', duration . +-duration-plus-end-interval = duration, -'/', (date; datetime) . + +duration-years = digit+, -'Y' . +duration-months = digit+, -'M' . +duration-days = digit+, -'D' . +duration-hours = digit+, -'H' . +duration-minutes = digit+, -'M' . +duration-seconds = digit+, -'S' . +duration-weeks = digit+, -'W' . + +{ -- Recurrence -- } + +-repeat-count = -'R-1' ; -'R' ; -'R', repetitions . +repetitions = digit+ . + +{ -- Utility rules -- } + +expanded-year = sign, digit+, digit, digit, digit . +expanded-century = sign, digit+, digit, digit . +-sign = -'+' ; '-' . + +offset-hour = @direction, digit, digit . +offset-minute = digit, digit . +direction = '+'; '-' . + +year = digit, digit, digit, digit . +century = digit, digit . +month = digit, digit . +week = digit, digit . +day = digit, digit . +hour = digit, digit . +minute = digit, digit . +second = digit, digit . +fractional-second = digit+ . +fractional-minute = digit+ . +fractional-hour = digit+ . + +day-of-year = digit, digit, digit . +number-of-days = -day-of-year . +day-of-week = digit . + +-digit = ["0"-"9"] . +-fraction-sep = -[','; '.'] . diff --git a/ixml/ixml-demo.xsl b/ixml/ixml-demo.xsl new file mode 100644 index 0000000..c58484f --- /dev/null +++ b/ixml/ixml-demo.xsl @@ -0,0 +1,57 @@ + + + + + + + + iso8601.ixml + + + + + 1962-06-28 + 1963-11-04 + 2024-02-30 + 2024-02-99 + 2024-01-17T17:26:38.416457100-05:00 + 2024-02-300 + 2025-12-25 + 2025-12-31T + + + + + + + + + + + + + + + + + + + + BUST on '{ $maybeDateTime }' + + + + + + diff --git a/ixml/ixml-parsing.xspec b/ixml/ixml-parsing.xspec new file mode 100644 index 0000000..a441fdc --- /dev/null +++ b/ixml/ixml-parsing.xspec @@ -0,0 +1,92 @@ + + + + + Parsing some dates + + + + 2024-01-20 + + + + + 2024 + 01 + 20 + + + + + + + 2024-01-0 + + + + BUST on '2024-01-0' + + + + + + 2024-01-18T14:44 + + + + + 2024 + 01 + 18 + 14 + 44 + + + + + + + 2024-01-18T14:44:10 + + + + + 2024 + 01 + 18 + 14 + 44 + 10 + + + + + + + 2024-01-18T14:44:10.777578600-05:00 + + + + + 2024 + 01 + 18 + 14 + 44 + 10 + 777578600 + + 05 + 00 + + + + + + + diff --git a/ixml/readme.md b/ixml/readme.md new file mode 100644 index 0000000..bee6db7 --- /dev/null +++ b/ixml/readme.md @@ -0,0 +1,88 @@ +# Invisible XML support + + +For more on Invisible XML: + - https://invisiblexml.org/ + - https://github.com/usnistgov/ixml-breadboard + +Aims: + +- Demonstrate and document some minimal iXML functionality in XSLT v3 +- Include iXML support in XSpec testing of this XSLT +- Address and document configuration issues in different runtimes (e.g.: XProc, Ant, Saxon from CL, oXygen XML) + + +## To do + +iXML in XSLT and XSpec under XProc runtime + +See https://github.com/ndw/xmlcalabash1/issues/335 + + +## Test grammar + +Included in this folder for testing is a grammar copied from the iXML repository at https://github.com/invisibleXML/ixml/blob/master/samples/ISO-8601-2004/iso8601.ixml + +It defines ISO dateTime format such that it can be parsed into an XML representation. For purposes of this testing it does not have to be perfect. + +## iXML setup + +To support Invisible XML (iXML), libraries from NineML can be made available to an XSLT engine such as Saxon as an extension function library. NineML, a set of Invisible XML tools by Norm Walsh, is documented at http://nineml.org. It is selected here because of platform compatibility with Saxon. + +Setting up the extension will depend on the processing architecture, whether Ant, XProc, a Java application or calling Saxon directly from the command line. + +All configurations entail the following: + +### NineML iXML Libraries + +For iXML processing inside Saxon, three jar files must be made available on the classpath: **CoffeeGrinder**, **CoffeeFilter** and **CoffeeSacks**, available respectively from + + - https://github.com/nineml/CoffeeGrinder/releases/latest + - https://github.com/nineml/CoffeeFilter/releases/latest + - https://github.com/nineml/CoffeeSacks/releases/latest + +A bash script in this folder, `download-jars.sh` (cribbed from Norm Walsh) is provided that will download these into a `lib` directory. + +### Saxon configuration + +An initialization setting must be provided to Saxon: `init:org.nineml.coffeesacks.RegisterCoffeeSacks` + +How you pass this to Saxon will depend on the tooling. + +### iXML in XSLT in oXygen XML + +**oXygen XML Editor** (or **Developer**) can run your XSLT and your XSpec, and your iXML with it. + +Norm Walsh offers [some hints](https://github.com/nineml/HOWTO/tree/main/oxygen). However, when we don't need to parameterize to handle inputs, it turns out to be a little simpler than the example depicted: + +#### Using a scenario + +For running [an XSLT like `ixml-demo.xsl`](ixml-demo.xsl), which + - binds the extension namespace + - includes or references one or more iXML grammars + - using the extension, provides functions supporting syntax defined by those grammars + +Configure a Transformation Scenario for running it + +- Locate three jar files (as named above): **CoffeeGrinder**, **CoffeeFilter**, **CoffeeSacks** +- Create an XSLT Scenario for applying your XSLT with appropriate settings +- Add each of these as a Library under Extensions on this scenario (XSLT tab) +- Under the Transformation Scenario's *Saxon Transformer* options, list the Initializer: `org.nineml.coffeesacks.RegisterCoffeeSacks` +- Save the Scenario + +This scenario will be able to execute CoffeeSacks functions supporting iXML parsing in your XSLT. + +#### iXML in XSpec in oXygen + +When writing iXML-based functionality in XSLT, one wishes to be able to test it using XSpec. + +Fortunately it is all Java, so the same settings can be provided to Ant for XSpec run under Ant, hence oXygen scenarios using Ant for XSpec can run these tests as well. + +Create a new Scenario in oXygen by copying the built-in **Run XSpec Test** Ant Transformation Scenario. Open the **Configure Transformation Scenarios** dialog (Ctrl-Shift-C) and find **Run XSpec Test** under XSpec, and select Duplicate. Give your new scenario a new name. + +Then: + +- Add the three jar files to the set of libraries (from the button on the bottom right on Options tab) +- Add the value `init:org.nineml.coffeesacks.RegisterCoffeeSacks` to the `saxon.custom.options` parameter (middle tab) + +Again your XSpec should run as XPected. diff --git a/lib/xspec b/lib/xspec new file mode 160000 index 0000000..9cef371 --- /dev/null +++ b/lib/xspec @@ -0,0 +1 @@ +Subproject commit 9cef3713f5a7aaedf38a4ec2190ba3262d51cd72 diff --git a/pom.xml b/pom.xml index fad9476..36fc436 100644 --- a/pom.xml +++ b/pom.xml @@ -94,7 +94,14 @@ ${project.basedir} - target/** + target/** + lib/xspec/** + + **/xspec/pending/** + + ixml/ixml-parsing.xspec + + xspec-dev/testing/xspec-shell.xspec diff --git a/random-util/Makefile b/random-util/Makefile new file mode 100644 index 0000000..f99a59a --- /dev/null +++ b/random-util/Makefile @@ -0,0 +1,29 @@ +include ../common/make_common.mk + +# XML Calabash prefers absolute paths + +module_path:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) +output_folder:=test_output + + +.PHONY: test +test: spec-test ## Run all tests + + +# binding xspec-tests to spec-test brings it under CI/CD when that task is run from above +.PHONY: spec-test +spec-test: xspec-tests ## Run specification tests (currently xspec-tests) + + +.PHONY: xspec-tests +xspec-tests: ## Run XSpecs found in tests folder + mkdir -p $(output_folder) + mvn --quiet \ + -f "../common/pom.xml" \ + exec:java \ + -Dexec.mainClass="net.sf.saxon.Transform" \ + -Dexec.args="-it:go -xsl:\"../xspec-dev/XSPEC-BATCH.xsl\" stop-on-error=yes folder=random-util/tests" + +.PHONY: clean +clean: ## Remove test output + rm -fr $(output_folder)/* diff --git a/xproc3/refresh-lib.sh b/xproc3/refresh-lib.sh new file mode 100644 index 0000000..04f6909 --- /dev/null +++ b/xproc3/refresh-lib.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +# This script attempts to download MorganaXProcIII + + +mkdir -p lib + +pushd lib + +morgana=MorganaXProc-IIIse-1.3.6 + +morgana_download="https://sourceforge.net/projects/morganaxproc-iiise/files/${morgana}/${morgana}.zip/download" + +if [ ! -f "${morgana}.zip" ]; then + echo "Downloading Morgana XProc III SE ..." + curl -L -o "${morgana}.zip" "${morgana_download}" + unzip -qo "${morgana}.zip" -x __MACOSX/** +fi + + +if [ ! -f MorganaXProc-IIIse-1.3.6/MorganaXProc-IIIse_lib/saxon-he-12.3.jar ]; then + echo "Downloading Saxon 12.3 ..." + curl -s -L -o SaxonHE12-3J.zip https://www.saxonica.com/download/SaxonHE12-3J.zip + unzip -q -o SaxonHE12-3J.zip "*.jar" + rm -f SaxonHE12-3J.zip saxon-he-test*.jar saxon-he-xqj*.jar lib/* + # rmdir lib + mv -f saxon-he-12.3.jar MorganaXProc-IIIse-1.3.6/MorganaXProc-IIIse_lib + +fi + +popd + diff --git a/xspec-dev/XSPEC-BATCH.xsl b/xspec-dev/XSPEC-BATCH.xsl new file mode 100644 index 0000000..83dc465 --- /dev/null +++ b/xspec-dev/XSPEC-BATCH.xsl @@ -0,0 +1,332 @@ + + + + + + + + + + + ../lib/xspec/src/reporter/junit-report.xsl + + + + + + + + + + + . + + *.xspec + + no + + + + + + + + + + no + + + no + + + + + + select={$pattern} + recurse={ if ($recurse=('no','false')) then 'no' else 'yes' } + on-error={ if ($stopping-on-error) then 'fail' else 'warning' } + + + + + + + + + + ERROR: Unable to resolve collection at URI {$collection-uri} - getting {$err:code} '{$err:description}' + + + + + + Param $baseURI is: { $baseURI } + Static base URI is: { static-base-uri() } + Collection location is: { $collection-location } + + + + + + Acquiring collection from { $collection-uri } + + + + + + + + ERROR: Unable to compile XSpec at { $my?name } - getting {$err:code} '{$err:description}' + + + + + + + + + + ERROR: + Unable to execute compiled XSpec from { /*/xsl:variable[@name='Q{{http://www.jenitennison.com/xslt/xspec}}xspec-uri'] } - getting {$err:code} + '{$err:description}' + + + + + + + + + + + + + + + + + + + + + + + WARNING: of { count($collection-in) } { if (count($collection-in)=1) then 'file' else 'files' }, { count($dropped) } { if (count($dropped) = 1) then 'file selected was' else 'were' } dropped - either unavailable, would not compile (XSpec to XSLT), or would not execute (XSLT): + { $dropped?name => string-join(', ') } + + + + + + + Failures detected - process terminating - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WARNING: No JUnit report is written to '{ $junit-to }' - try a file name with suffix + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {$report-to}/{$xspec-basename}.html + + + + + + + + + + + + + + Writing report {$filename} ... + + + + + + + + + Writing report {$filename} ... + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xspec-dev/XSPEC-SINGLE.xsl b/xspec-dev/XSPEC-SINGLE.xsl new file mode 100644 index 0000000..e35ff7d --- /dev/null +++ b/xspec-dev/XSPEC-SINGLE.xsl @@ -0,0 +1,100 @@ + + + + + + + + ../lib/xspec/src/compiler/compile-xslt-tests.xsl + + + + + + + + + + + + + + + ... and formatted a report. + + + + + + + + + + + + + + + + + + + + + + + + + + ... compiled XSpec ... + + + + + + + + + + + + + + + + + + + + ... executed XSpec ... + + + \ No newline at end of file diff --git a/xspec-dev/gha/publish.yml b/xspec-dev/gha/publish.yml new file mode 100644 index 0000000..6c2a598 --- /dev/null +++ b/xspec-dev/gha/publish.yml @@ -0,0 +1,39 @@ +# Rationale and instructions for this GitHub Actions workflow: +# https://github.com/EnricoMi/publish-unit-test-result-action?tab=readme-ov-file#support-fork-repositories-and-dependabot-branches + +name: Publish Test Results +on: + workflow_run: + workflows: ["CI"] + types: + - completed +permissions: {} +jobs: + test-results: + name: Test Results + runs-on: ubuntu-latest + if: github.event.workflow_run.conclusion != 'skipped' + permissions: + checks: write + pull-requests: write + actions: read + steps: + - name: Download and Extract Artifacts + uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 + with: + run_id: ${{ github.event.workflow_run.id }} + path: artifacts + - name: Publish Test Results + uses: EnricoMi/publish-unit-test-result-action@8885e273a4343cd7b48eaa72428dea0c3067ea98 + with: + commit: ${{ github.event.workflow_run.head_sha }} + action_fail: true + fail_on: "test failures" + event_file: artifacts/Test Results Event/event.json + event_name: ${{ github.event.workflow_run.event }} + github_token: ${{ secrets.GITHUB_TOKEN }} + check_name: XSpec Test Results + comment_mode: always + files: "**/*_junit-report.xml" + report_individual_runs: true + deduplicate_classes_by_file_name: false diff --git a/xspec-dev/gha/test.yml b/xspec-dev/gha/test.yml new file mode 100644 index 0000000..8720d85 --- /dev/null +++ b/xspec-dev/gha/test.yml @@ -0,0 +1,55 @@ +name: CI +on: + push: + branches: + - main + - develop + pull_request: {} +permissions: + checks: write + pull-requests: write +env: + JAVA_VERSION: "17" + JAVA_DISTRIBUTION: "temurin" +jobs: + test: + name: "Test and Collect Results" + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + with: + submodules: true + fetch-depth: 0 + - uses: actions/setup-java@v3 + with: + distribution: "${{ env.JAVA_DISTRIBUTION }}" + java-version: "${{ env.JAVA_VERSION }}" + - name: Run unit tests + run: | + make unit-test + id: unit-tests + - name: Run integration tests + run: | + make smoke-test + id: integration-tests + - name: Run specification tests + run: | + make spec-test + id: spec-tests + - name: Upload test results + if: always() + uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 + with: + name: Test Results + path: | + **/*_junit-report.xml + event_file: + name: "Upload Results to Event File" + runs-on: ubuntu-20.04 + needs: test + steps: + - name: Upload + uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 + with: + name: Test Results Event + path: ${{ github.event_path }} diff --git a/xspec-dev/mvn-saxon-xspec-batch-quiet.sh b/xspec-dev/mvn-saxon-xspec-batch-quiet.sh new file mode 100644 index 0000000..2aef170 --- /dev/null +++ b/xspec-dev/mvn-saxon-xspec-batch-quiet.sh @@ -0,0 +1,91 @@ +#!/usr/bin/env bash + +set -o pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=../common/subcommand_common.bash + +source "$SCRIPT_DIR/../common/subcommand_common.bash" + +XSLT_FILE="${SCRIPT_DIR}/XSPEC-BATCH.xsl" +LOGFILE=${LOGFILE:-"xspec_$(date +"%Y%m%d%H%M").log.txt"} + +usage() { + cat </dev/null to drop all runtime messages / progress reports instead of logging + # the process should error out only if stop-on-error=yes, otherwise it will do its best to complete + # invoke_saxon "${SAXON_ARGS}" 2>${LOGFILE} | echo_on_error "Failure running XSpec: see ${LOGFILE}" | tee ${LOGFILE} + invoke_saxon "${SAXON_ARGS}" 2>${LOGFILE} | tee ${LOGFILE} + +fi diff --git a/xspec-dev/mvn-saxon-xspec-batch.sh b/xspec-dev/mvn-saxon-xspec-batch.sh new file mode 100644 index 0000000..9e71278 --- /dev/null +++ b/xspec-dev/mvn-saxon-xspec-batch.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash + +set -o pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=../common/subcommand_common.bash + +source "$SCRIPT_DIR/../common/subcommand_common.bash" + +XSLT_FILE="${SCRIPT_DIR}/XSPEC-BATCH.xsl" +LOGFILE=${LOGFILE:-"xspec_$(date +"%Y%m%d%H%M").log.txt"} + +usage() { + cat </dev/null to drop all runtime messages / progress reports instead of logging + # the process should error out only if stop-on-error=yes, otherwise it will do its best to complete + invoke_saxon "${SAXON_ARGS}" 2>&1 | tee ${LOGFILE} + +fi + diff --git a/xspec-dev/mvn-saxon-xspec-html.sh b/xspec-dev/mvn-saxon-xspec-html.sh new file mode 100644 index 0000000..c5a23c6 --- /dev/null +++ b/xspec-dev/mvn-saxon-xspec-html.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=../common/subcommand_common.bash + +source "$SCRIPT_DIR/../common/subcommand_common.bash" + +XSLT_FILE="${SCRIPT_DIR}/XSPEC-SINGLE.xsl" + +usage() { + cat < git submodule update --init --recursive +``` + +## XML Processing Pipelines + +The pipelines in this project depend on prior art in the XSpec repository. Pipelines are currently implemented using XProc 1.0 to be run under XML Calabash, or in pure XSLT 3.0 to be run under Saxon. + +[XML Calabash home page](https://xmlcalabash.com/) +[Saxon home page](https://www.saxonica.com/products/products.xml) + +XProc 3.0 is planned for a later date. + +Five pipelines are provided at the top level. All have dependencies on XSpec [in the XSpec distribution included as a repo submodule](../lib/xspec/src/harnesses/). Rather than forking from XSpec, the aim is eventually to offer these capabilities or derivates from them back into the main project. + +Three pipelines rely on XProc 1.0 / XML Calabash (running Saxon internally): + +- xspec-single-report.xpl +- xspec-single-xspec-repo-report.xpl +- xspec-batch-report.xpl + +Two of these rely on Saxon / XSLT 3.0 only: +- XSPEC-SINGLE.xsl +- XSPEC-BATCH.xsl + +A `testing` directory also shows a model pipeline, designed to be copied and modified to deliver functionality (calls on configurable sets of XSpec instances) in a development folder or branch: + +- testing/xspec-test-batch.xpl + +Additionally, scripts in the directories show how runtime calls of these processors can be instrumented and run, using Apache Maven to manage libraries. + +These scripts can be copied and adapted for other scenarios and runtimes, or used as models for scripts on other platforms. The XProc and XSLT should also be conformant with the relevant (standard) specifications, apart from library dependencies. + +### Details + +`xspec-single-report.xpl` - processes an XSPec input file by applying XSpec execution using the XSpec XProc test harness, then processing its native report to make an HTML page using a *new* presentation stylesheet. + +*Limitation*: At present, the presentation stylesheet meets these goals with respect to XSpec testing XSLT - not (yet) testing XQuery, Schematron or other runtimes to which XSpec might be applied. + +`xspec-batch-report.xpl` - provides the same set of operations, except instead of a single XSpec (file instance), it can accept any number of XSpec files designated as inputs (either wired in or via XProc port bindings). Results from these tests are *aggregated* into a single report. + +This pipeline also demonstrates how reports can be further processed to provide other outputs such as XML summaries (for batches) and simple plain-text summaries. + +Additionally, another XProc is included for future diagnostics: + +`xspec-single-xspec-repo-report.xpl` - calls the XSpec project XProc (pipeline), delivering an HTML file using the 'regular' XSLT in the XSpec distribution. *Note*: YMMV especially as it regards CSS setup in HTML file results. + +This pipeline is essentially a minimal, functionally standalone wrapper around XSpec-native capabilities, made to expose the 'pain points' in using the current resources (which were originally authored and tested over ten years ago). For good results using XProc 1.0 under XML Calabash (itself no longer cutting-edge technology), certain modifications must be made in the XSpec repository libraries: + +* Serialization needs to use latest (HTML5) serialization options to deal with character handling, rather than the current 'double reverse' character escaping of markup characters into [the Unicode Private Use Areas (PUA)](https://en.wikipedia.org/wiki/Private_Use_Areas) -- thus effectively requiring a character mapping back, which is not well supported under XProc 1.0. +* CSS handling/deployment needs to be improved or exposed such that generated HTML files have their CSS available. + +## 'testing' directory resources + +The `testing` directory contains a number of small self-contained XSpecs convenient for testing, along with the small XSLT stylesheets that they test. + +### How to batch XSpec - a 'mini' demo + +Additionally, `testing` contains an [XProc example](testing/xspec-test-batch.xpl) of how to use the batch pipelining XSpec in your project. This is a useful way of configuring and running a set of XSpecs assuming a listing can be given (i.e. it does not query the file system dynamically). Along with this is a [script for calling it](testing/mvn-xproc-xspec-test-batch.sh) - copy and edit these two resources together to have an entire self-contained runtime. + +The advantage of such a 'wrapper' XProc is that it can encapsulate logic for an even simpler interface or runtime for the specific use case (whether running from the command line, under CI/CD, in an IDE etc). Both the batch (the XSpecs to be run) and what to do with the test results can thus be hard-wired, or provided with customized interface controls, depending on your needs. + +The ['test batch' XProc](testing/xspec-test-batch.xpl) in this folder can be copied anywhere and adjusted per case, restoring its import link, pointing it to local XSpec file sources, and setting up ports or `p:store` (file save) options as wanted. It then runs in place to execute, as a set, all the XSpecs it points to. + +Summary: how to use - + +- Copy the XPL (and batch script if wanted) to a convenient location +- Rename and comment both of these for local use, rewiring their configuration, comments, and help messaging according to your design +- In editing the XProc, consider adding or removing any ports or whether to expose output ports vs write file outputs (`p:store`) +- Run the XProc standalone using the script or otherwise + +## Scripts calling XProc + +The `bash` scriot [mvn-xproc-xspec-html.sh](mvn-xproc-xspec-html.sh) shows how a script can invoke XML Calabash to execute one of these pipelines. + +How a particular XProc is used depends on the ports defined in the XProc. XML Calabash provides syntax and interfaces for those ports. Accordingly, a script or command-line invocation typically has to set one or more of the following, for these XProcs: + +- input port `xspec` is where XSpec inputs are configured - these must be files accessible on the system +- output port `xspec-report` or `xspec-results`, if any, shows XSpec runtime outputs, with no further processing, as a single (XML) report +- output port `html-report` shows the results, rendered in HTML for viewing in a web browser +- output `summary` shows the XML report reduced to a simple summary form +- output `determination` shows a plain text result for the entire run,with the summary results + +Additionally, a runtime option `theme` supports changing the HTML page rendition settings (CSS), including its color palette, as described below. + +## XProc alone + +A script can be nice for repeated use, but for testing / one-time use, the same commands or their equivalents can be used to run the pipelines directly in XML Calabash, binding the input XSpecs to the `xspec` input port. Assuming `xml-calabash.sh` executes XML Calabash, this syntax will serve to combine execution of `testing_1.xspec` and `testing_2.xspec` into a single runtime: + +```bash +./xml-calabash.sh path/to/xspec-batch.xpl -ixspec=testing_1.xspec -ixspec=testing_2.xspec -oxspec-report=/dev/null -ohtml-report=/dev/null -osummary=/dev/null +``` + +Since the port `determination` is unnamed, its outputs are echoed to the console. Bindings to `dev/null` have the effect of silencing the other output ports. (They might otherwise be directed to file outputs for inspection.) + +See XML Calabash docs for more info on its flags and switches. + +## Scripts calling Saxon + +Two more `bash` scripts, [mvn-saxon-xspec-batch.sh](mvn-saxon-xspec-batch.sh) and [mvn-saxon-xspec-batch.sh](mvn-saxon-xspec-batch.sh), show how XSpec may be invoked using XSLT only to produce an HTML report for a single XSpec, or a batched report (to console and/or to a result report file or file set) from running sets of XSpec files selected dynamically. + +Running Saxon directly has the advantage of exposing a configurability surface allowing the binding of extension functionality into Saxon (via naming an initialization class). This feature permits the execution not only of XSpec, but of XSpec that tests extended functionility in XSLT stylesheets, notably including extension functionality supporting [Invisible XML]() + +Use these as follows: + +```bash +./mvn-saxon-xspec-html.sh special-testing.xspec +``` + +In a directory named `xspec`, creates a report file `special-testing.html` in HTML format. + +``` +./mvn-saxon-xspec-batch.sh folder=src/project report-to=project-report.html +``` + +In folder `src/project` relative to the repository root, this creates a single HTML report file aggregating runtime results from compiling and executing all XSpecs in that folder. + +Other arguments can provide for: + +- glob-matching file names for precise selection +- one report per XSpec file instead of a single aggregated report +- processing recursively over folders and subfolders (aggregating all results) +- stopping on parse or processing errors +- or no HTML reports, just console messages showing results +- JUnit XML report production suitable for CI/CD integration + +See inline comments in XSLT [XSPEC-BATCH.xsl](XSPEC-BATCH.xsl) for details. + +Hint: these scripts can be 'noisy' because they return progress reports. Runtime messages can be silenced by redirecting STDERR outputs, for example using by `2>/dev/null` in the command invoking the script. + +``` +./mvn-saxon-xspec-batch.sh folder=src/project recurse=yes 2>/dev/null +``` + +All XSpec files in the folder `src/project` and any subfolders, reporting results to the console (STDOUT) but not creating HTML reports and not echoing warning or error messages to the console. + +It is best to silence STDERR only after you are confident that all XSpecs in your file set can be run successfully, especially if the "break early on error" option is set (since you will neither see STDERR nor get any results). + +## Enhanced HTML Production + +In order to work around limitations in the current XSpec HTML production (details with respect to its deployment under XProc 1.0), a [new HTML production XSLT](xspec-mx-html-report.xsl) is provided here, for use either for standalone XSpec reporting, or for reporting results from several XSpecs in aggregate ("batch"). + +[This XSLT ](xspec-mx-html-report.xsl) produces standalone HTML including embedded CSS and some lightweight Javascript supporting navigation features. + +### Theming HTML from XProc + +The XProc files support a runtime option, `theme`, which also exposes control of the theme setting (in the HTML and CSS). This takes the form of an HTML `class` value on the `body` element, along with CSS to be applied to the page on the basis of that setting. + +- `theme=clean` (the default) produces a medium-contrast color-neutral format +- `theme=uswds` uses colors for emphasis from the USWDS color scheme +- `theme=classic` uses colors drawn from the good-old XSpec HTML +- `theme=toybox` provides a more extravagant scheme. + +New themes can be added in the XSLT or in a new "shell" XSLT importing it, by copying and modifying an appropriate template to match the new theme and give it style. Such an importing XSLT can also modify any other feature of the HTML page result. + diff --git a/xspec-dev/testing/copy_me.xsl b/xspec-dev/testing/copy_me.xsl new file mode 100644 index 0000000..6e34968 --- /dev/null +++ b/xspec-dev/testing/copy_me.xsl @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/xspec-dev/testing/mvn-xproc-xspec-test-batch.sh b/xspec-dev/testing/mvn-xproc-xspec-test-batch.sh new file mode 100644 index 0000000..8e41182 --- /dev/null +++ b/xspec-dev/testing/mvn-xproc-xspec-test-batch.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=../common/subcommand_common.bash + +source "$SCRIPT_DIR/../../common/subcommand_common.bash" + +XPROC_FILE="${SCRIPT_DIR}/xspec-test-batch.xpl" +REPORT_HTML="xspec-test-report.html" + +usage() { + cat < + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xspec-dev/testing/xspec-shell.xspec b/xspec-dev/testing/xspec-shell.xspec new file mode 100644 index 0000000..d4cddbf --- /dev/null +++ b/xspec-dev/testing/xspec-shell.xspec @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xspec-dev/testing/xspec-test-batch.xpl b/xspec-dev/testing/xspec-test-batch.xpl new file mode 100644 index 0000000..2b25a22 --- /dev/null +++ b/xspec-dev/testing/xspec-test-batch.xpl @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xspec-dev/xspec-assurance.sch b/xspec-dev/xspec-assurance.sch new file mode 100644 index 0000000..42c624c --- /dev/null +++ b/xspec-dev/xspec-assurance.sch @@ -0,0 +1,13 @@ + + + + + + + + No document found at + + + + \ No newline at end of file diff --git a/xspec-dev/xspec-batch-report.xpl b/xspec-dev/xspec-batch-report.xpl new file mode 100644 index 0000000..de7c4b3 --- /dev/null +++ b/xspec-dev/xspec-batch-report.xpl @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xspec-dev/xspec-mx-html-report.xsl b/xspec-dev/xspec-mx-html-report.xsl new file mode 100644 index 0000000..a817368 --- /dev/null +++ b/xspec-dev/xspec-mx-html-report.xsl @@ -0,0 +1,538 @@ + + + + + + + + + clean + + + + + + + + + + + + + + XSpec - { $result-count } { if ($result-count eq 1) then 'test' else 'tests'} in { $report-count } { if ($report-count eq 1) then 'report' else 'reports'} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
  • FAILING + + { ancestor-or-self::*/x:label => string-join(' - ') } +
  • +
    + + +
    +
    + +
    +

    XSpec Report - { @xspec/replace(.,'.*/','') }

    + +
    +
    + + + +
    + + XSpec Report - { @xspec/replace(.,'.*/','') } + +
    + +
    + +
    +
    + + + + + +
    + +
    +
    + +
    + + + + + + +
    + +
    +
    + + + + + + + + + + 🤞 + + + 👍 + + + 👎 + + + + +

    { local-name(..) }/{ local-name(.) }: { . }

    +
    + + + + +

    { local-name(..) }/{ local-name(.) }: { . }

    +
    + + +

    { local-name(..) }/{ local-name(.) }: { format-dateTime(xs:dateTime(.),'[MNn] [D1], [Y0001] [H01]:[m01]:[s01]') } ({.})

    +
    + + + + + +
    + + + open + + + + +
    + +
    +
    +
    + + + + + { $whose/ancestor::x:report ! ('report' || count(.|preceding-sibling::x:report || '-' )) }{ $whose/@id } + + + + + + + + + + { self::x:report/'Total tests '}{ child::x:label } +   + + + + + + + + + + + + { . } + : + + + + + + + + + { child::x:label } +   + + + { (child::x:test/@pending/('Pending ' || .),child::x:test[@successful='true']/'Passes','Fails')[1] } + + + + + Expecting { child::x:label } + + + +
    + + + +
    +
    + + +
    + + + + + +
    +
    + + +

    Expecting (testing against)

    +
    + + +

    Producing (actual result)

    +
    + + + + + + + + + +
    + +
    +
    + + +
    + +
    +
    + + + + + +

    From input

    +
    + + + +

    Expecting

    +
    + + + + + + +
    + + + +
    +
    + + +
    + +
    +
    + + + + + + + + + + + + + + + + + + + + + + +.uswds { + background-color: #f0f0f0; + + .label { background-color: #1a4480; color: white} + .pending .label { background-color: inherit; color: black } + .passing { background-color: #d9e8f6 } + .pending { background-color: white } + .failing { background-color: #f8dfe2 } + .failing .passing { background-color: #d9e8f6 } + .pending.zero, .failing.zero { background-color: inherit } +} + + + + + + + +.classic { + h1, .h1, .label { background-color: #606; color: #6f6 } + h1 { padding: 0.2em } + .pending .label { background-color: inherit; color: black } + .passing { background-color: #cfc } + .pending { background-color: #eee } + .failing { background-color: #fcc } + .failing .passing { background-color: #cfc } + .pending.zero, .failing.zero { background-color: inherit } +} + + + + + +.toybox { + .label { background-color: black; color: white } + .failing .label { background-color: darkred } + .passing .label { background-color: darkgreen } + .pending .label { background-color: inherit; color: black } + .passing { background-color: honeydew } + .pending { background-color: cornsilk } + .failing { background-color: mistyrose } + .failing .passing { background-color: honeydew } + .pending.zero, .failing.zero { background-color: inherit } +} + + + + + No template for theme '{ $theme }' - using 'simple' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    diff --git a/xspec-dev/xspec-single-report.xpl b/xspec-dev/xspec-single-report.xpl new file mode 100644 index 0000000..8e2d0d3 --- /dev/null +++ b/xspec-dev/xspec-single-report.xpl @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xspec-dev/xspec-single-xspec-repo-report.xpl b/xspec-dev/xspec-single-xspec-repo-report.xpl new file mode 100644 index 0000000..eefee5f --- /dev/null +++ b/xspec-dev/xspec-single-xspec-repo-report.xpl @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xspec-dev/xspec-summarize.xsl b/xspec-dev/xspec-summarize.xsl new file mode 100644 index 0000000..f895ffc --- /dev/null +++ b/xspec-dev/xspec-summarize.xsl @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + { @xspec } + { @stylesheet } + + + + + + { ancestor-or-self::*/child::label => string-join(' : ') } + + + + + \ No newline at end of file diff --git a/xspec-dev/xspec-summary-reduce.xsl b/xspec-dev/xspec-summary-reduce.xsl new file mode 100644 index 0000000..fad32c0 --- /dev/null +++ b/xspec-dev/xspec-summary-reduce.xsl @@ -0,0 +1,68 @@ + + + + + + + + { (1 to 14) ! ' ...' } + XSpec summary report: { mx:enumerate('XSpec',count(report)) } + + { (1 to 14) ! ' ...' } + + + + + + + SUCCESS - { mx:give-report-counts(.) } - NO FAILURES REPORTED + + + + + + FAILURE - { mx:give-report-counts(.) } - { count(descendant::fail) } { mx:pluralize('failure',count(descendant::fail)) => upper-case() } REPORTED + + + + + { xspec-file ! tokenize(.,'/')[last()] } testing { xslt-file ! tokenize(.,'/')[last()] }: { mx:enumerate('test',@test-count) }, { @pending-count} pending, { mx:enumerate('failure',count(fail)) } + + + + + { mx:enumerate('report',count($r/report)) } with { mx:enumerate('test', sum($r/report/@test-count)) } ({ sum($r/report/@pending-count) } pending) + + + + + + + { $c } { mx:pluralize($nom,$c) } + + + + + + + + + + + + + { . }{ 's'[$plural] } + + + \ No newline at end of file