doctest-0.8.1/0000755000000000000000000000000015061220010010036 5ustar doctest-0.8.1/CONTRIBUTORS0000644000000000000000000000036315061220010011720 0ustar Authors and Contributors ======================== Thomas Smith Michael Walter Colin B. Macdonald Oliver Heimlich Mike Miller Andrew Janke Manuel Leonhardt Markus Mützel (Please contact the developers if your name should be here but isn't!) doctest-0.8.1/COPYING0000644000000000000000000000304615061220010011074 0ustar Copyright (c) 2010 Thomas Grenfell Smith Copyright (c) 2011, 2013-2015 Michael Walter Copyright (c) 2015 Colin B. Macdonald All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. doctest-0.8.1/DESCRIPTION0000644000000000000000000000113015061220010011537 0ustar Name: doctest Version: 0.8.1 Date: 2025-09-13 Author: various authors Maintainer: Colin B. Macdonald , Michael Walter Title: Documentation tests Description: Find and run example code within documentation. Formatted blocks of example code are extracted from documentation files and executed to confirm their output is correct. This can be part of a testing framework or simply to ensure that documentation stays up-to-date during software development. Depends: octave (>= 4.2.0) Url: https://gnu-octave.github.io/packages/doctest/ License: BSD-3-Clause doctest-0.8.1/INDEX0000644000000000000000000000006015061220010010624 0ustar doctest >> Documentation tests testing doctest doctest-0.8.1/Makefile0000644000000000000000000001220515061220010011476 0ustar SHELL := /bin/bash # Maintainer makefile for Octave Doctest # # SPDX-License-Identifier: FSFAP # # Copyright 2015 Oliver Heimlich # Copyright 2015 Michael Walter # Copyright 2015-2017, 2019, 2022-2023 Colin B. Macdonald # Copyright 2016 Carnë Draug # Copyright 2019 Mike Miller # Copyright 2019 Andrew Janke # Copyright 2019 Manuel Leonhardt # Copyright 2022 Markus Muetzel # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without any warranty. TAR := $(shell which gtar 2>/dev/null || echo tar) PACKAGE := $(shell grep "^Name: " DESCRIPTION | cut -f2 -d" ") VERSION := $(shell grep "^Version: " DESCRIPTION | cut -f2 -d" ") BUILD_DIR := tmp MATLAB_PKG := ${BUILD_DIR}/${PACKAGE}-matlab-${VERSION} MATLAB_PKG_ZIP := ${MATLAB_PKG}.zip OCTAVE_RELEASE := ${BUILD_DIR}/${PACKAGE}-${VERSION} OCTAVE_RELEASE_TARBALL := ${BUILD_DIR}/${PACKAGE}-${VERSION}.tar.gz INSTALLED_PACKAGE := ~/octave/${PACKAGE}-${VERSION}/packinfo/DESCRIPTION HTML_DIR := ${BUILD_DIR}/${PACKAGE}-html HTML_TARBALL := ${HTML_DIR}.tar.gz OCTAVE ?= octave MKOCTFILE ?= mkoctfile -Wall MATLAB ?= matlab TEST_CODE=ver(), success = doctest({'doctest', 'test/', 'test/examples/'}); exit(~success); # run tests twice so we can see some output BIST_CODE=ver(), cd('test'); disp(pwd()), test('bist'); success1 = test('bist'); cd('..'); cd('test_extra'); disp(pwd()), test('run_tests'); success2 = test('run_tests'); exit(~success1 || ~success2); MATLAB_EXTRA_TEST_CODE=ver(), addpath(pwd()), disp(pwd()), cd('test_extra'); disp(pwd()), success = run_tests(); exit(~success); .PHONY: help clean install test test-interactive dist html matlab_test matlab_pkg help: @echo Available rules: @echo " clean clean all temporary files" @echo " install install package in Octave" @echo " test run tests with Octave" @echo " test-interactive run tests with Octave in interactive mode" @echo " test-bist run additional tests with Octave" @echo " dist create Octave package (${OCTAVE_RELEASE_TARBALL})" @echo " html create Octave Forge html (${HTML_TARBALL})" @echo " release create both tarballs and md5 sums" @echo @echo " matlab_test run tests with Matlab" @echo " matlab_pkg create Matlab package (${MATLAB_PKG_ZIP})" GIT_DATE := $(shell git show -s --format=\%ci) # Follows the recommendations of https://reproducible-builds.org/docs/archives define create_tarball $(shell set -o pipefail; cd $(dir $(1)) \ && find $(notdir $(1)) -print0 \ | LC_ALL=C sort -z \ | $(TAR) c --mtime="$(GIT_DATE)" \ --owner=root --group=root --numeric-owner \ --no-recursion --null -T - -f - \ | gzip -9n > "$(2)") endef %.tar.gz: % $(call create_tarball,$<,$(notdir $@)) %.zip: % cd "$(BUILD_DIR)" ; zip -9qr - "$(notdir $<)" > "$(notdir $@)" $(OCTAVE_RELEASE): .git/index | $(BUILD_DIR) @echo "Creating package version $(VERSION) release ..." -$(RM) -r "$@" git archive --format=tar --prefix="$@/" HEAD | $(TAR) -x $(RM) "$@/README.matlab.md" \ "$@/.gitignore" \ "$@/.mailmap" $(RM) -r "$@/.github" $(RM) -r "$@/util" chmod -R a+rX,u+w,go-w "$@" $(HTML_DIR): install | $(BUILD_DIR) @echo "Generating HTML documentation. This may take a while ..." -$(RM) -r "$@" $(OCTAVE) --no-window-system --silent \ --eval "pkg load generate_html; " \ --eval "pkg load $(PACKAGE);" \ --eval "options = get_html_options ('octave-forge');" \ --eval "generate_package_html ('${PACKAGE}', '${HTML_DIR}', options)" chmod -R a+rX,u+w,go-w $@ dist: $(OCTAVE_RELEASE_TARBALL) html: $(HTML_TARBALL) md5: $(OCTAVE_RELEASE_TARBALL) $(HTML_TARBALL) @md5sum $^ release: md5 @echo "Upload @ https://sourceforge.net/p/octave/package-releases/new/" @echo "*After review*, an Octave-Forge admin will tag this with:" @echo " git tag -a v$(VERSION) -m \"Version $(VERSION)\"" # TODO: more matlab subdirs ${BUILD_DIR} ${MATLAB_PKG}/private: mkdir -p "$@" clean: rm -rf "${BUILD_DIR}" test: ${OCTAVE} --path ${CURDIR}/inst --eval "${TEST_CODE}" test-interactive: script --quiet --command "${OCTAVE} --path ${CURDIR}/inst --eval \"${TEST_CODE}\"" /dev/null test-bist: ${OCTAVE} --path ${CURDIR}/inst --eval "${BIST_CODE}" ## Install in Octave (locally) install: ${INSTALLED_PACKAGE} ${INSTALLED_PACKAGE}: ${OCTAVE_RELEASE_TARBALL} $(OCTAVE) --silent --eval "pkg install $<" ## Matlab packaging matlab_pkg: $(MATLAB_PKG_ZIP) ${MATLAB_PKG}: | $(BUILD_DIR) ${MATLAB_PKG}/private $(OCTAVE) --path ${CURDIR}/util --silent --eval \ "convert_comments('inst/', '', '../${MATLAB_PKG}/')" cp -a inst/private/*.m ${MATLAB_PKG}/private/ cp -a COPYING ${MATLAB_PKG}/ cp -a CONTRIBUTORS ${MATLAB_PKG}/ cp -a NEWS ${MATLAB_PKG}/ cp -a README.matlab.md ${MATLAB_PKG}/ cp -a test ${MATLAB_PKG}/ cp -a test_extra ${MATLAB_PKG}/ matlab_test: matlab_pkg cd "${MATLAB_PKG}"; ${MATLAB} -nojvm -nodisplay -nosplash -r "${TEST_CODE}" matlab_extra_test: matlab_pkg cd "${MATLAB_PKG}"; ${MATLAB} -nojvm -nodisplay -nosplash -r "${MATLAB_EXTRA_TEST_CODE}" doctest-0.8.1/NEWS0000644000000000000000000001120315061220010010532 0ustar doctest 0.8.1 (2025-09-13) ========================== * Development repo moved to https://github.com/gnu-octave/octave-doctest * Fixes for Octave 9 and Octave 10. * Workaround for delete method of subclasses of "handle". doctest 0.8.0 (2023-01-03) ========================== * Expected error messages can be optionally prefixed with "error: ". * Writing "??? " at the beginning of an error is now deprecated. * Texinfo users can markup errors with `@error{}`. * `doctest myclass` has been refactored with various bugs fixed. * Preliminary support for `doctest classdef.method` on Octave. * Test classdef methods using dotted `classdef.method` while still using `@class/method` on old-style classes. * Functions defined directly in the Octave interpreter can now be tested. * Test suite fixes for Octave 6 and 7. * Source code is encoded with UTF-8, indicated with `.oct-config` files. * Don't doctest some classdef methods twice. doctest 0.7.0 (2019-03-23) ========================== * Functions within compiled `.oct` files can now be tested. * Tests are run with default number formatting (see `help doctest`). * More robust to errors during testing. * Makefile improvements and fixes. doctest 0.6.1 (2018-01-04) ========================== * Workaround regex bug on ARM (again!). doctest 0.6.0 (2017-12-25) ========================== * Tests can now call "clear" and "clear all". * Fixes for running on Octave development versions (upcoming 4.4.0). * Minimum supported Octave version is now 4.2.0. The package no longer has any compiled code and does not include an "evalc" implementation. doctest 0.5.0 (2016-11-13) ========================== * SKIP_IF and other conditional directives can include small single-line blocks of code. For example: - "% doctest: +SKIP_IF(foo() && bar(42))" * Recursion into subdirectories is now the default; pass "-nonrecursive" for the previous default behaviour. * In Texinfo mode, skip tests without output by default. This is an experimental change to help test the GNU Octave project; the feature might disappear without warning in a future version. * Workaround regex bug on ARM architecture. * Minimum supported Octave version is now 4.0.0. doctest 0.4.1 (2016-01-04) ========================== * Added conditional variants of SKIP and XFAIL directives to control test execution based on runtime conditions: - "% doctest: +SKIP_IF(condition)" - "% doctest: +SKIP_UNLESS(condition)" - "% doctest: +XFAIL_IF(condition)" - "% doctest: +XFAIL_UNLESS(condition)" * Added constants DOCTEST_OCTAVE and DOCTEST_MATLAB that can be used as conditions in SKIP_IF etc. * Improved handling of example code in TexInfo documentation. - Added support for @print{} macros, which may be used for output that is not part of a returned value. - Examples without ">>" markers use code indentation together with @result{} / @print{} macros to classify input and output lines in a natural way. It is no longer necessary to split code into several @example / @group blocks. - Allow arbitrary TexInfo macros. The documentation is interpreted by makeinfo before running the code examples. - Fixed handling of TexInfo files with Windows line endings. * Improved folder/directory traversals: - Ignore hidden (dot) directories. - Ignore files that are neither m-files nor texinfo. doctest 0.4.0 (2015-07-02) ========================== * Change doctest interface to be closer to Octave's test function. * Change wildcard string from '***' to '...'. * Doctests can be influenced with directives: - mark tests to be skipped by appending "% doctest: +SKIP". - mark tests expected to fail with "% doctest: +XFAIL". - stricter whitespace matching: "% doctest: -NORMALIZE_WHITESPACE". - disable "..." wildcard matching with "% doctest: -ELLIPSIS". * Support "doctest foldername" to run tests on the files/classes within the folder/directory "foldername". With optional recursion. * Improve evalc implementation on Octave. * Other bug fixes. doctest 0.3.0 (2015-05-12) ========================== * Multiline input now works (e.g., a matrix split across lines). * Allow "ans = " to be omitted. * Pure texinfo files can be tested: "doctest myfile.texinfo". * Other bug fixes. * Support and directory structure for being an Octave package. doctest 0.2.0 (2015-04-06) ========================== * Octave support, including examples in Texinfo blocks. * Return the number of tests and number failed. doctest-0.8.1/README.md0000644000000000000000000000247715061220010011327 0ustar Doctest ======= The [Octave Doctest](https://gnu-octave.github.io/packages/doctest/) package finds specially-formatted blocks of example code within documentation files. It then executes the code and confirms the output is correct. This can be useful as part of a testing framework or simply to ensure that documentation stays up-to-date during software development. To get started, here is a simple example: ~~~matlab function greet(user) % Returns a greeting. % % >> greet World % % Hello, World! disp(['Hello, ' user '!']); end ~~~ We can test it by invoking `doctest greet` at the Octave prompt, which will give the following output: ~~~ greet .................................................. PASS 1/1 Summary: PASS 1/1 1/1 targets passed, 0 without tests. ~~~ Doctest also supports Texinfo markup, which is [popular](https://www.gnu.org/software/octave/doc/interpreter/Documentation-Tips.html) in the Octave world, and it provides various toggles and switches for customizing its behavior. The [Doctest documentation](https://octave.sourceforge.io/doctest/function/doctest.html) contains information on all this. Quite appropriately, Doctest can test its own documentation. We also maintain a [list of software](https://github.com/gnu-octave/octave-doctest/wiki/WhoIsUsingDoctest) that is using Doctest. doctest-0.8.1/inst/0000755000000000000000000000000015061220010011013 5ustar doctest-0.8.1/inst/.oct-config0000644000000000000000000000001715061220010013042 0ustar encoding=utf-8 doctest-0.8.1/inst/doctest.m0000644000000000000000000002773315061220010012652 0ustar %% Copyright (c) 2010 Thomas Grenfell Smith %% Copyright (c) 2011, 2013-2016 Michael Walter %% Copyright (c) 2015-2019, 2023, 2025 Colin B. Macdonald %% Copyright (c) 2019 Manuel Leonhardt %% SPDX-License-Identifier: BSD-3-Clause %% %% Redistribution and use in source and binary forms, with or without %% modification, are permitted provided that the following conditions are met: %% %% 1. Redistributions of source code must retain the above copyright notice, %% this list of conditions and the following disclaimer. %% %% 2. Redistributions in binary form must reproduce the above copyright notice, %% this list of conditions and the following disclaimer in the documentation %% and/or other materials provided with the distribution. %% %% 3. Neither the name of the copyright holder nor the names of its %% contributors may be used to endorse or promote products derived from this %% software without specific prior written permission. %% %% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" %% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE %% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE %% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE %% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR %% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF %% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS %% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN %% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) %% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE %% POSSIBILITY OF SUCH DAMAGE. %% -*- texinfo -*- %% @documentencoding UTF-8 %% @deftypefn {} {} doctest @var{target} %% @deftypefnx {} {} doctest @var{target} -nonrecursive %% @deftypefnx {} {@var{success} =} doctest (@var{target}, @dots{}) %% @deftypefnx {} {[@var{numpass}, @var{numtests}, @var{summary}] =} doctest (@dots{}) %% Run examples embedded in documentation. %% %% Doctest finds and runs code found in @var{target}, which can be a: %% @itemize %% @item function; %% @item class; %% @item Texinfo file; %% @item .oct/.mex compiled code; %% @item directory/folder (pass @code{-nonrecursive} to skip subfolders); %% @item cell array of such items. %% @end itemize %% When called with a single return value, return whether all tests have %% succeeded (@var{success}). %% %% When called with two or more return values, return the number of tests %% passed (@var{numpass}), the total number of tests (@var{numtests}) and a %% structure @var{summary} with various fields. %% %% %% Doctest finds example blocks, executes the code and verifies that the %% results match the expected output. For example, running %% @code{doctest doctest} will execute this code: %% %% @example %% @group %% >> 1 + 3 %% ans = %% 4 %% @end group %% @end example %% %% If there's no output, just put the next line right after the one with %% no output. If the line does produce output (for instance, an error), %% this will be recorded as a test failure. %% %% @example %% @group %% >> x = 3 + 4; %% >> x %% x = %% 7 %% @end group %% @end example %% %% %% @strong{Wildcards} %% You can use a wildcard to match unpredictable output: %% %% @example %% @group %% >> disp(datestr(now, 'yyyy-mm-dd')) %% 2... %% @end group %% @end example %% %% @strong{Expecting an error} %% Doctest can deal with errors, to some extent. For instance, this case is %% handled correctly: %% %% @example %% @group %% >> not_a_real_function(42) %% error: ...ndefined ... %% @end group %% @end example %% %% Note use of wildcards here; MATLAB spells this @code{Undefined}, while %% Octave uses @code{undefined}. Writing @code{error: } is optional. %% Currently errors do not work if the code emits other output before the %% error message. Warnings are different; they work fine. %% %% %% @strong{Multiple lines of code} %% Code spanning multiple lines can be entered by prefixing all subsequent %% lines with @code{..}, e.g., %% %% @example %% @group %% >> for i = 1:3 %% .. i %% .. end %% i = 1 %% i = 2 %% i = 3 %% @end group %% @end example %% (But note this is not required when writing texinfo documentation, %% see below). %% %% %% @strong{Shortcuts} %% You can optionally omit @code{ans = } when the output is unassigned. But %% actual variable names (such as @code{x = }) must be included. Leading %% and trailing whitespace on each line of output will be discarded which %% gives some freedom to, e.g., indent the code output as you wish. %% %% %% @strong{Directives} %% You can skip certain tests by marking them with a special comment. This %% can be used, for example, for a test not expected to pass or to avoid %% opening a figure window during automated testing. %% %% @example %% @group %% >> a = 6 % doctest: +SKIP %% b = 42 %% >> plot(...) % doctest: +SKIP %% @end group %% @end example %% %% %% These special comments act as directives for modifying test behaviour. %% You can also mark tests that you expect to fail: %% %% @example %% @group %% >> a = 6 % doctest: +XFAIL %% b = 42 %% @end group %% @end example %% %% Both the @code{+SKIP} and the @code{+XFAIL} directives have conditional %% variants (e.g., @code{+SKIP_IF} and @code{+SKIP_UNLESS}) that control %% test execution and expectations based on runtime conditions, such as %% the platform, operating systems, or installed packages: %% %% @example %% @group %% >> license % doctest: +XFAIL_IF(DOCTEST_MATLAB) %% ans = GNU General Public License %% @end group %% @end example %% %% Doctest provides the default flags @code{DOCTEST_OCTAVE} and %% @code{DOCTEST_MATLAB}, but you can call functions and access arbitrary %% variables (including those defined by previous tests). %% %% %% By default, all adjacent white space is collapsed into a single space %% before comparison. A stricter mode where ``internal whitespace'' must %% match is available: %% %% @example %% @group %% >> fprintf('a b\nc d\n') %% a b %% c d %% %% >> fprintf('a b\nc d\n') % doctest: -NORMALIZE_WHITESPACE %% a b %% c d %% @end group %% @end example %% %% %% To disable the @code{...} wildcard, use the @code{-ELLIPSIS} directive. %% %% %% @strong{Numerical Format} %% Tests are run using default formatting: %% @example %% @group %% >> 6/5 %% ans = 1.2000 %% @end group %% @end example %% %% If your test changes the global state (e.g., @code{format} or %% @code{chdir}), you may need to undo your changes afterwards. %% In this example, we followup with @code{format} to reset to the %% default five digits: %% %% @example %% @group %% >> format long %% >> 355/113 %% ans = 3.14159292035... %% >> format %% @end group %% @end example %% %% %% @strong{Diary Style} %% When the m-file contains plaintext documentation, doctest finds tests %% by searching for lines that begin with @code{>>}. It then finds the %% expected output by searching for the next @code{>>} or two blank lines. %% %% @strong{Octave/Texinfo Style} %% If your m-file contains Texinfo markup, then doctest finds code in %% @code{@@example @dots{} @@end example} blocks. Note: %% @itemize %% @item The two-blank-lines convention is not required. %% @item The use of @code{>>} is neither required nor recommended as Octave %% documentation conventionally indicates output with @code{@@result@{@}} %% and @code{@@print@{@}}. Ambiguities are resolving by assuming output %% is indented further than input. %% @end itemize %% %% A typical Texinfo-style doctest looks like: %% @example %% a = 5; %% b = a + 1 %% @result{} b = 6 %% disp("hello\nthere") %% @print{} hello %% @print{} there %% @end example %% %% The two styles are not mutually exclusive: this documentation is written %% in Texinfo using a hybrid approach. %% %% @strong{Support for non-ASCII characters} %% If you encounter file encoding issues on Octave, @pxref{dir_encoding} and %% @ref{__mfile_encoding__}. For example, this file itself is encoded in %% utf-8 and declares this by installing a @code{.oct-config} file. %% Matlab users might want to try @code{feature('DefaultCharacterSet', 'UTF-8')}. %% %% @seealso{test} %% @end deftypefn function varargout = doctest(targets, varargin) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Process parameters. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% if (nargin < 1 || nargout > 3) if (is_octave) print_usage() end help doctest return end % if given a single object, wrap it in a cell array if ~iscell(targets) targets = {targets}; end % input parsing for options and directives recursive = true; if (nargout < 3) verbose = true; else verbose = false; end directives = doctest_default_directives(); for i = 1:(nargin-1) assert(ischar(varargin{i})) pm = varargin{i}(1); directive = varargin{i}(2:end); switch directive case 'recursive' % weakly deprecated, not mentioned in help text assert(strcmp(pm, '-')) recursive = true; case 'nonrecursive' assert(strcmp(pm, '-')) recursive = false; case 'quiet' % currently not mentioned in help text assert(strcmp(pm, '-')) verbose = false; case 'verbose' % currently not mentioned in help text assert(strcmp(pm, '-')) verbose = true; otherwise assert(strcmp(pm, '+') || strcmp(pm, '-')) warning('Doctest:deprecated', ... ['Support for specifying directives on the command line is deprecated\n' ... ' and will be removed in a future version (for discussion, see\n' ... ' https://github.com/gnu-octave/octave-doctest/issues/127).']); enable = strcmp(varargin{i}(1), '+'); directives = doctest_default_directives(directives, directive, enable); end end % for now, always print to stdout fid = 1; % get terminal color codes [color_ok, color_err, color_warn, reset] = doctest_colors(fid); if (verbose) fprintf(fid, 'Doctest v0.8.1: this is Free Software without warranty, see source.\n\n'); end summary = struct(); summary.num_targets = 0; summary.num_targets_passed = 0; summary.num_targets_without_tests = 0; summary.num_targets_with_extraction_errors = 0; summary.num_tests = 0; summary.num_tests_passed = 0; % stash user's formatting if (is_octave) try [save_format, save_spacing] = format(); catch % TODO: remove when we drop support for Octave < 4.4.0 save_format = eval('__formatstring__()'); save_spacing = eval('ifelse(__compactformat__(), "compact", "loose")'); end else save_format = get(0, 'Format'); save_spacing = get(0, 'FormatSpacing'); end % force default formatting format() %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Collect and run tests %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% for i=1:numel(targets) summary = doctest_collect(targets{i}, directives, summary, recursive, verbose, 0, fid); end % restore user's formatting format(save_format) format(save_spacing) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Report summary %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% if (verbose) fprintf(fid, '\nSummary:\n\n'); if (summary.num_tests_passed == summary.num_tests) fprintf(fid, [' ' color_ok 'PASS %4d/%-4d' reset '\n\n'], summary.num_tests_passed, summary.num_tests); else fprintf(fid, [' ' color_err 'FAIL %4d/%-4d' reset '\n\n'], summary.num_tests - summary.num_tests_passed, summary.num_tests); end fprintf(fid, '%d/%d targets passed, %d without tests', summary.num_targets_passed, summary.num_targets, summary.num_targets_without_tests); if summary.num_targets_with_extraction_errors > 0 fprintf(fid, [', ' color_err '%d with extraction errors' reset], summary.num_targets_with_extraction_errors); end fprintf(fid, '.\n\n'); end if nargout == 1 varargout = {summary.num_targets_passed == summary.num_targets}; elseif nargout > 1 varargout = {summary.num_tests_passed, summary.num_tests, summary}; end end doctest-0.8.1/inst/private/0000755000000000000000000000000015061220010012465 5ustar doctest-0.8.1/inst/private/doctest_collect.m0000644000000000000000000004633115061220010016024 0ustar function summary = doctest_collect(w, directives, summary, recursive, verbose, depth, fid) %DOCTEST_COLLECT Find and run doctests. % % The input W is the name of a class, directory, function or filename: % * For a directory, calls itself on the contents, recursively if % RECURSIVE is true; % * For a class, all methods are tested; % * When running Octave, it can also be the filename of a Texinfo file. %% % Copyright (c) 2010 Thomas Grenfell Smith % Copyright (c) 2015 Michael Walter % Copyright (c) 2015-2019, 2022-2025 Colin B. Macdonald % Copyright (c) 2015 Oliver Heimlich % Copyright (C) 2018 Mike Miller % SPDX-License-Identifier: BSD-3-Clause % TODO: methods('logical') octave/matlab differ: which behaviour do we want? % TODO: what about builtin "test" versus dir "test/"? Do we prefer dir? if (isempty(w)) return end % determine type of target if is_octave() % Note: ripe for refactoring once "exist(w, 'class')" works in Octave. [~, ~, ext] = fileparts(w); if any(strcmpi(ext, {'.texinfo' '.texi' '.txi' '.tex'})) type = 'texinfo'; elseif (strcmp (ext, '.oct') && exist (w) == 3) % .oct explicitly type = 'octfile'; elseif (exist (w) == 3) % .oct/.mex [~, w, ~] = fileparts (w); % strip extension if present type = 'function'; % then access like any function else type = 'unknown'; end %% Let's see if its a class by checking if methods returns % What about classdef in oct file above? Should we do this even if % type is 'octfile'? if (strcmp (type, 'unknown')) if (~ isempty (w) && strcmp (w(1), '@')) temp = w(2:end); else temp = w; end try temp = methods(temp); type = 'class'; catch type = 'unknown'; end end if (strcmp (type, 'unknown')) if (exist(w, 'dir')) type = 'dir'; elseif (exist(w, 'file') || exist(w, 'builtin') || exist(w) == 103) type = 'function'; else type = 'unknown'; end end % This covers anything that we can get help from that wasn't covered above, % notably "doctest class.method". Quite possibly some other things too. if (strcmp (type, 'unknown')) try if (~isempty (help (w))) type = 'function'; end catch % no-op end end else % Matlab if (strcmp(w(1), '@')) && ~isempty(methods(w(2:end))) % covers "doctest @class", but not "doctest @class/method" type = 'class'; elseif ~isempty(methods(w)) % covers "doctest class" type = 'class'; elseif (exist(w, 'dir')) type = 'dir'; elseif exist(w, 'file') || exist(w, 'builtin'); type = 'function'; elseif ~isempty(help(w)) % covers "doctest class.method" and "doctest class/method" % no try-catch needed as no error when w has no help type = 'function'; else type = 'unknown'; end % Note: ambiguous what happens for "doctest @class/method"... as it is % for "help @class/method", e.g., "help @class/class" does not give the % constructor's help. end % Deal with directories if (strcmp(type, 'dir')) if (strcmp(w, '.')) if (depth == 0) % cheap hack to not indent when calling "doctest ." depth = -1; end else spaces = repmat(' ', 1, 2*depth); if (strcmp(w(end), filesep())) slashchar = ''; else slashchar = filesep(); end if (verbose) fprintf(fid, '%s%s%s\n', spaces, w, slashchar); end end oldcwd = chdir(w); files = dir('.'); for i=1:numel(files) f = files(i).name; if (exist(f, 'dir')) if (strcmp(f, '.') || strcmp(f, '..')) % skip "." and ".." continue elseif (strcmp(f(1), '@')) % class, don't skip if nonrecursive elseif (~ recursive) % skip all directories continue elseif (strcmp(f(1), '.')) %fprintf(fid, 'Ignoring hidden directory "%s"\n', f) continue end else [~, ~, ext] = fileparts(f); if (~ any(strcmpi(ext, ... {'.m' '.texinfo' '.texi' '.txi' '.tex' '.oct' '.mex'}))) %fprintf(fid, 'Debug: ignoring file "%s"\n', f) continue end end summary = doctest_collect(f, directives, summary, recursive, verbose, depth + 1, fid); end chdir(oldcwd); return end % Build structure array with the following fields: % TARGETS(i).name Human-readable name of test. % TARGETS(i).link Hyperlink to test for use in Matlab. % TARGETS(i).docstring Associated docstring. % TARGETS(i).error: Contains error string if extraction failed. % TARGETS(i).depth How "deep" in a recursive traversal are we if strcmp(type, 'function') target = collect_targets_function(w); target.depth = depth; targets = [target]; elseif strcmp(type, 'class') targets = collect_targets_class(w, depth); elseif strcmp (type, 'octfile') targets = collect_targets_octfile (w, depth); elseif strcmp(type, 'texinfo') target = struct(); target.name = w; target.link = ''; target.depth = depth; [target.docstring, target.error] = parse_texinfo(fileread(w)); targets = [target]; else target = struct(); target.name = w; target.link = ''; target.depth = depth; target.docstring = ''; target.error = 'Function or class not found.'; targets = [target]; end % update summary summary.num_targets = summary.num_targets + numel(targets); % get terminal color codes [color_ok, color_err, color_warn, reset] = doctest_colors(fid); for i=1:numel(targets) % run doctests for target and update statistics target = targets(i); spaces = repmat(' ', 1, 2*target.depth); dots = repmat('.', 1, 55 - numel(target.name) - 2*target.depth); if (verbose) fprintf(fid, '%s%s %s ', spaces, target.name, dots); end % extraction error? if target.error summary.num_targets_with_extraction_errors = summary.num_targets_with_extraction_errors + 1; if (verbose) fprintf(fid, [color_err 'EXTRACTION ERROR' reset '\n\n']); fprintf(fid, ' %s\n\n', target.error); end continue; end % run doctest results = doctest_run_docstring(target.docstring, directives); % determine number of tests passed num_tests = numel(results); num_tests_passed = 0; for j=1:num_tests if results(j).passed num_tests_passed = num_tests_passed + 1; end end % update summary summary.num_tests = summary.num_tests + num_tests; summary.num_tests_passed = summary.num_tests_passed + num_tests_passed; if num_tests_passed == num_tests summary.num_targets_passed = summary.num_targets_passed + 1; end if num_tests == 0 summary.num_targets_without_tests = summary.num_targets_without_tests + 1; end if (verbose) % pretty print outcome if num_tests == 0 fprintf(fid, 'NO TESTS\n'); elseif num_tests_passed == num_tests fprintf(fid, [color_ok 'PASS %4d/%-4d' reset '\n'], num_tests_passed, num_tests); else fprintf(fid, [color_err 'FAIL %4d/%-4d' reset '\n\n'], num_tests - num_tests_passed, num_tests); for j = 1:num_tests if ~results(j).passed fprintf(fid, ' >> %s\n\n', results(j).source); fprintf(fid, [ ' expected: ' '%s' '\n' ], results(j).want); fprintf(fid, [ ' got : ' color_err '%s' reset '\n' ], results(j).got); if results(j).xfail fprintf(fid, ' expected failure, but test succeeded!'); end fprintf(fid, '\n'); end end end end end end function target = collect_targets_function(w) target = struct(); target.name = w; if is_octave() target.link = ''; else target.link = sprintf('%s', which(w), w); end [target.docstring, target.error] = extract_docstring(target.name); end function targets = collect_targets_class(w, depth) if (strcmp(w(1), '@')) % Octave methods('@foo') gives java error, Matlab just says "No methods" w = w(2:end); end % workaround github.com/gnu-octave/octave-doctest/issues/135 by % accessing all non-constructor method help text *before* "help obj" if (is_octave () && compare_versions (OCTAVE_VERSION, '7.0.0', '<')) meths = methods (w); for i=1:numel (meths) if (~ strcmp (meths{i}, w)) % skip @obj/obj name = sprintf ('@%s%s%s', w, filesep (), meths{i}); [docstring, format] = get_help_text (name); end end end % end workaround % First, "help class". For classdef, this differs from "help class.class" % (general class help vs constructor help). For old-style classes we will % probably end up testing the constructor twice but... meh. target.name = w; if is_octave() target.link = ''; else target.link = sprintf('%s', which(w), w); end target.depth = depth; [target.docstring, target.error] = extract_docstring(target.name); targets = target; % Next, add targets for all class methods meths = methods(w); meths = unique (meths); % Issue #260 for i=1:numel(meths) target = struct(); if is_octave() if compare_versions (OCTAVE_VERSION, '9.0.0', '>=') && exist (w, "class") == 8 % classdef on newish Octave: use cls.method target.name = sprintf ('%s.%s', w, meths{i}); target.link = ''; elseif compare_versions (OCTAVE_VERSION, '7.0.0', '>=') && exist (w, "class") == 8 % use cls.method, use cls.method, but skip cls.cls if strcmp (meths{i}, w) % gathering the ctor help fails https://savannah.gnu.org/bugs/?62803 continue end target.name = sprintf ('%s.%s', w, meths{i}); target.link = ''; else % use @cls/method for old-style classes https://savannah.gnu.org/bugs/?61521 target.name = sprintf ('@%s%s%s', w, filesep (), meths{i}); target.link = ''; end else target.name = sprintf('%s.%s', w, meths{i}); target.link = sprintf('%s', which(w), meths{i}, target.name); end target.depth = depth; [target.docstring, target.error] = extract_docstring(target.name); targets = [targets; target]; end end function targets = collect_targets_octfile (file, depth) % first target is the name of the octfile (w/o extension) [~, basename, ext] = fileparts (file); assert (strcmp (ext, '.oct')) target = collect_targets_function (basename); target.name = file; target.depth = depth; targets = [target]; % octfile may have many fcns in it: find them using the autoload map autoloadmap = autoload (); len = numel (file); % matches both "/foo/bar.oct" and "/baz/bar.oct"; uncommon in practice pmatch = @(e) (numel (e.file) >= len) && strcmp (e.file(end-len+1:end), file); idx = find (arrayfun (pmatch, autoloadmap)); if (~ isempty (idx)) % indicate that octfile has other fcns, and indent those targets targets(1).name = [targets(1).name ':']; for i = 1:numel (idx) f = autoloadmap(idx(i)).function; target = collect_targets_function (f); target.depth = depth + 1; targets = [targets; target]; end end end function [docstring, error] = extract_docstring(name) if is_octave() [docstring, format] = get_help_text(name); if strcmp(format, 'texinfo') [docstring, error] = parse_texinfo(docstring); elseif strcmp(format, 'plain text') error = ''; elseif strcmp(format, 'Not documented') assert (isempty (docstring)) error = ''; elseif strcmp(format, 'Not found') if (regexp(name,'\.m$')) % looks like "doctest test_no_docs.m" gets us here: octave bug? assert (isempty (docstring)) error = ''; elseif (is_octave() && compare_versions (OCTAVE_VERSION(), '9.0.0', '>=') && regexp(name,'.+\.delete$')) % forgive mssing "classdef.delete", which Octave 9 "methods" adds to handle-classes assert (isempty (docstring)) error = ''; else assert (isempty (docstring)) error = 'Not an m file.'; end else format warning('Doctest:unexpected-format', 'Unexpected format in that file/function'); error = ''; end else docstring = help(name); error = ''; end end function [docstring, error] = parse_texinfo(str) docstring = ''; error = ''; % no example blocks? not an error, but nothing to do if (isempty(strfind(str, '@example'))) % error = 'no @example blocks'; return end % Normalize line endings in files which have been edited in Windows % This simplifies the regular expressions below. str = strrep (str, sprintf ('\r\n'), sprintf ('\n')); % The subsequent regexprep would fail if the example block is located right % at the beginning of the file. This is probably a bug in regexprep and is % only possible inside included texinfo files. if (isempty (regexp (str, '^\s', 'once'))) str = cstrcat (sprintf ('\n'), str); end % Mark the occurrence of “@example” and “@end example” to be able to find % example blocks after conversion from texi to plain text. Also consider % indentation, so we can later correctly unindent the example's content. % These should work, but I keep hitting ARM-specific when $1 is empty: % https://savannah.gnu.org/bugs/index.php?52810 % TODO: fixed in 4.2.2, so can revert this once we drop 4.2.1 support %str = regexprep (str, ... % '^([ \t]*)(\@example)(.*)$', ... % [ '$1$2$3\n', ... % retain original line % '$1###### EXAMPLE START ######'], ... % 'lineanchors', 'dotexceptnewline', 'emptymatch'); %str = regexprep (str, ... % '^([ \t]*)(\@end example)(.*)$', ... % [ '$1###### EXAMPLE STOP ######\n', ... % '$1$2$3'], ... % retain original line % 'lineanchors', 'dotexceptnewline', 'emptymatch'); % Instead we do it manually [S, E, TE, M, T, NM, SP] = regexp (str, '^([ \t]*)(\@example)(.*)$', ... 'lineanchors', 'dotexceptnewline', 'emptymatch'); str = SP{1}; for i=1:length (T) str = [str ... T{i}{:} sprintf('\n') ... % retain original line T{i}{1} '###### EXAMPLE START ######' ... SP{i+1}]; end [S, E, TE, M, T, NM, SP] = regexp (str, '^([ \t]*)(\@end example)(.*)$', ... 'lineanchors', 'dotexceptnewline', 'emptymatch'); str = SP{1}; for i=1:length (T) str = [str ... T{i}{1} '###### EXAMPLE STOP ######' sprintf('\n') ... T{i}{:} ... % retain original line SP{i+1}]; end % special comments "@c doctest: cmd" are translated % FIXME the expression would also match @@c doctest: ... re = [ '(?:\@c(?:omment)?\s' ... % @c or @comment, ?: means no token '|#|%)\s*' ... % or one of #,% '(doctest:\s*.*)' ]; % want the doctest token str = regexprep (str, re, '% $1', 'dotexceptnewline'); % We use eval to not produce compile errors in Matlab, % the __makeinfo__ function exists in Octave only. [str, err] = eval('__makeinfo__ (str, ''plain text'')'); if (err ~= 0) error = sprintf('__makeinfo__ returned error code %d', err); return end % Normalize end of line characters again. __makeinfo__ returns end of line % characters depending on the current OS. Since we want Unix line endings, % the conversion is only required under Windows. if (ispc ()) str = strrep (str, sprintf ('\r\n'), sprintf ('\n')); end % extract examples and discard everything else T = regexp (str, ... [ '(^[ \t]*###### EXAMPLE START ######', ... '.*?', ... '###### EXAMPLE STOP ######$)'], ... 'tokens', 'lineanchors'); if (isempty (T)) error = 'malformed @example blocks'; return end % post-process each example block for i = 1 : length (T) % flatten assert (numel (T{i}), 1); T{i} = T{i}{1}; % unindent indent = regexp (T{i}, '#', 'once') - 1; T{i} = regexprep (T{i}, sprintf ('^[ \t]{%d}', indent), '', 'lineanchors'); % remove EXAMPLE markers T{i} = regexprep (T{i}, ... '[ \t]*###### EXAMPLE ST(?:ART|OP) ######(?:\n|$)', ... ''); if (regexp (T{i}, '^\s*$', 'once', 'emptymatch')) error = 'empty @example blocks'; return end if (regexp (T{i}, '^\s*>>', 'once')) % First nonblank line starts with '>>': assume diary style. However, % we strip @result and @print macros (TODO: perhaps unwisely?) L = strsplit (T{i}, '\n'); L = regexprep (L, '^(\s*)(?:⇒|=>|⊣|-\||error→|error->)', '$1', 'once', 'lineanchors'); T{i} = strjoin (L, '\n'); continue end % Hack: the @example block is commonly mis-used to store non-examples such as % diagrams or math. Delete an example block that has no indicated output. % (Hard to leave for "later" as we don't keep track of @example blocks.) R1 = regexp (T{i}, '^\s*(⇒|=>|⊣|-\||error→|error->)', 'lineanchors'); R2 = regexp (T{i}, '(doctest:\s+-TEXINFO_SKIP_BLOCKS_WO_OUTPUT)'); T{i} = regexprep (T{i}, '(doctest:\s+-TEXINFO_SKIP_BLOCKS_WO_OUTPUT)', ''); if (isempty (R1) && isempty (R2)) T{i} = ''; continue end % split into lines L = strsplit (T{i}, '\n'); % Categorize input and output lines in the example using % @result and @print macros. Everything else, including comment lines and % empty lines, is categorized as input (for now). Linput = cellfun ('isempty', regexp (L, '^\s*(⇒|=>|⊣|-\||error→|error->)', 'once')); if (not (Linput (1))) error = 'no command: @result on first line?'; return end % Output lines may be wrapped or output goes over several lines and not % every line is preceded by “=>”. indent = regexp(L, '\S', 'once'); indent(cellfun ('isempty', indent)) = inf; indent = [indent{:}] - 1; row = 1; while (row < numel (L)) begin_of_input = row; begin_of_output = row + find (not (Linput(row + 1 : end)), 1); if (isempty (begin_of_output)) begin_of_output = numel (L) + 1; end end_of_input = begin_of_output - 1; % determine minimum indentation of input lines min_indent = min (indent(begin_of_input : end_of_input)); % Find next input line with an equal or less indentation to determine the % end of the output. row = begin_of_output ... + find (Linput(begin_of_output + 1: end) ... & (indent(begin_of_output + 1: end) <= min_indent), ... 1); if (isempty (row)) row = numel (L) + 1; end end_of_output = row - 1; if (end_of_output <= numel (L)) Linput (begin_of_output : end_of_output) = false; end % Mark verified input lines as such L{begin_of_input} = ['>> ' L{begin_of_input}]; L(begin_of_input + 1 : end_of_input) = ... cellfun (@(s) ['.. ' s], L(begin_of_input + 1 : end_of_input), ... 'UniformOutput', false); end % strip @result and @print macro output Loutput = not (Linput); L(Loutput) = regexprep (L(Loutput), ... '^(\s*)(?:⇒|=>|⊣|-\||error→|error->)', ... '$1', ... 'once', 'lineanchors'); T{i} = strjoin (L, '\n'); end docstring = strjoin (T, '\n'); end doctest-0.8.1/inst/private/doctest_colors.m0000644000000000000000000000145615061220010015677 0ustar function [color_ok, color_err, color_warn, reset] = doctest_colors(fid) %DOCTEST_COLORS Return terminal color codes. % % FIXME: Shouldn't use colors if stdout is not a TTY. %% % Copyright (c) 2015 Michael Walter % Copyright (c) 2015, 2017 Colin B. Macdonald % SPDX-License-Identifier: BSD-3-Clause % by default, no colors color_ok = ''; color_err = ''; color_warn = ''; reset = ''; % only use colors in Octave, when printing to stdout, and when terminal supports colors if (is_octave()) have_colorterm = index(getenv('TERM'), 'color') > 0; if fid == stdout && have_colorterm % hide terminal escapes from Matlab color_ok = eval('"\033[1;32m"'); % green color_err = eval('"\033[1;31m"'); % red color_warn = eval('"\033[1;35m"'); % purple reset = eval('"\033[m"'); end end end doctest-0.8.1/inst/private/doctest_compare.m0000644000000000000000000000460015061220010016016 0ustar function match = doctest_compare(want, got, goterr, normalize_whitespace, ellipsis) %DOCTEST_COMPARE Check if two strings match. % % Returns true if string GOT matches the template string WANT. Basically % WANT and GOT should be identical, except: % % * whitespace at the start/end of each line is trimmed; % * multiple spaces are collapsed (if NORMALIZE_WHITESPACE is true); % * WANT can have "..."; matches anything in GOT (if ELLIPSIS is true); % * WANT can omit "ans = "; % * various other nonsense of unknown current relevance. %% % Copyright (c) 2010 Thomas Grenfell Smith % Copyright (c) 2015 Michael Walter % Copyright (c) 2015-2016 Colin B. Macdonald % SPDX-License-Identifier: BSD-3-Clause % This looks bad, like hardcoding for lower-case "a href" % and a double quote... but that's what MATLAB looks for too. got = regexprep(got, ' io.github.gnu_octave.doctest org.octave.Octave Doctest Find and run example code within documentation

Formatted blocks of example code are extracted from documentation files and executed to confirm their output is correct. This can be part of a testing framework or simply to ensure that documentation stays up-to-date during software development.

development software testing testing documentation https://octave.sourceforge.io/doctest https://github.com/gnu-octave/octave-doctest/issues/new FSFAP BSD-3-Clause The Octave Community octave-maintainers@gnu.org
doctest-0.8.1/test/0000755000000000000000000000000015061220010011015 5ustar doctest-0.8.1/test/.hidden/0000755000000000000000000000000015061220010012326 5ustar doctest-0.8.1/test/.hidden/test_dont_run.m0000644000000000000000000000013615061220010015373 0ustar function test_dont_run() % This is in a dot directory so it should not run % >> a = 5 % a = 6 doctest-0.8.1/test/.oct-config0000644000000000000000000000001715061220010013044 0ustar encoding=utf-8 doctest-0.8.1/test/@test_class/0000755000000000000000000000000015061220010013261 5ustar doctest-0.8.1/test/@test_class/test_class.m0000644000000000000000000000037215061220010015605 0ustar function obj = test_class % % >> disp(class(test_class)) % test_class % % % >> methods test_class % Methods for class test_class: % test_class test_method obj = struct; obj.name = 'Default Name'; obj.age = 42; obj = class(obj, 'test_class'); end doctest-0.8.1/test/@test_class/test_method.m0000644000000000000000000000027315061220010015760 0ustar function result = test_method(obj, varargin) % % >> m = test_class; disp(test_method(m)) % Default Name is 42 years old. result = sprintf('%s is %d years old.', obj.name, obj.age); end doctest-0.8.1/test/@test_classdef/0000755000000000000000000000000015061220010013740 5ustar doctest-0.8.1/test/@test_classdef/amethod.m0000644000000000000000000000013515061220010015536 0ustar function amethod(obj) % This method is stored in a separate file. % >> b = 2 + 2 % b = 4 end doctest-0.8.1/test/@test_classdef/test_classdef.m0000644000000000000000000000157615061220010016752 0ustar classdef test_classdef %TEST_CLASSDEF A test for classdef classes % % Some tests: % >> 6 + 7 % ans = 13 % % >> a = test_classdef() % a = % class name = "default", age = 42 % % % This general help text should be shown for "help test_classdef". % % There are also tests in the methods below. properties name age end methods function obj = test_classdef(n, a) % test_classdef: constructor help text % % not sure how to see this but here's an embedded doctest % % >> a = 13 + 1 % a = 14 if (nargin ~= 2) obj.name = 'default'; obj.age = 42; else obj.name = n; obj.age = a; end end end methods function disp(obj) % disp method help text % >> a = 30 + 2 % a = 32 fprintf('class name = "%s", age = %d\n', obj.name, obj.age) end end end doctest-0.8.1/test/@test_shadow/0000755000000000000000000000000015061220010013441 5ustar doctest-0.8.1/test/@test_shadow/test_shadow.m0000644000000000000000000000022315061220010016140 0ustar function obj = test_shadow() % >> 6 + 7 % ans = 13 obj = struct(); obj.name = 'Do not shadow me bro'; obj = class(obj, 'test_shadow'); end doctest-0.8.1/test/README.md0000644000000000000000000000027115061220010012274 0ustar Part of the Octave-Doctest test-suite. This directory is recursively tested by `doctest`. Some `%!`-style built-in self-tests may be run here as well, check the Makefile for details. doctest-0.8.1/test/bist.m0000644000000000000000000002152215061220010012136 0ustar function bist() % built-in self tests for Doctest % % Doctest mostly tests itself but in some cases we want to ensure % exactly what is being tested and yet we cannot call doctest recursively % https://github.com/gnu-octave/octave-doctest/issues/184 % % Copyright (c) 2019, 2022-2023, 2025 Colin B. Macdonald % SPDX-License-Identifier: BSD-3-Clause end %!error doctest () %!error [a, b, c, d] = doctest ('double') %!assert (doctest ('doctest', '-quiet')) %!assert (~ doctest ('there_is_no_such_file', '-quiet')) %!assert (~ doctest ('@there_is_no_such_class', '-quiet')) %!assert (~ doctest ({'doctest', 'there_is_no_such_file'}, '-quiet')) %!test %! [n, t] = doctest ('doctest', '-quiet'); %! assert (n == t) %! assert (t >= 10) %!test %! [n, t, summ] = doctest ('doctest'); %! assert (n == t) %! assert (t >= 10) %! assert (summ.num_targets == 1) %! assert (summ.num_tests == t) %! assert (summ.num_tests_passed == n) %!test %! % list input %! [n, t1, summ] = doctest ({'doctest'}); %! [n, t2, summ] = doctest ({'doctest' 'doctest'}); %! assert (t2 == 2*t1) %! assert (summ.num_targets == 2) %!test %! % nonrecursion stays out of subdirs %! [n1, t1] = doctest ('test_dir', '-quiet'); %! [n2, t2] = doctest ('test_dir', '-nonrecursive', '-quiet'); %! assert (t2 < t1) %!test %! % maybe not recommended notation for classdef, but works for now... %! [nump, numt, summ] = doctest ('@test_classdef/amethod'); %! assert (nump == 1 && numt == 1) %!xtest %! % maybe not recommended notation for classdef %! [nump, numt, summ] = doctest ('@test_classdef/disp'); %! assert (nump == 1 && numt == 1) %!test %! % https://github.com/gnu-octave/octave-doctest/issues/92 %! % Should have 4 targets and 5 tests %! % * general class help (2 tests) %! % * ctor (1 test) %! % * disp method (1 test) %! % * amethod in external file (1 test) %! if (compare_versions (OCTAVE_VERSION(), '6.0.0', '>=')) %! [nump, numt, summ] = doctest ('test_classdef'); %! assert (nump == numt && numt >= 4) %! assert (summ.num_targets >= 3) %! end %!xtest %! % complicated classdef has correct number of tests and targets %! % https://github.com/gnu-octave/octave-doctest/issues/268 %! % Now on Octave 9, #268 fixed but it fails in new way: %! % https://github.com/gnu-octave/octave-doctest/issues/288 %! % (these issues test separately elsewhere) %! if (compare_versions (OCTAVE_VERSION(), '9.0.0', '>=')) %! [nump, numt, summ] = doctest ('test_classdef'); %! assert (nump == numt && numt == 5) %! assert (summ.num_targets == 4) %! end %!test %! % https://github.com/gnu-octave/octave-doctest/issues/268 %! if (compare_versions (OCTAVE_VERSION(), '9.0.0', '>=')) %! [nump, numt, summ] = doctest ('classdef_infile.classdef_infile'); %! assert (nump == numt && numt == 1) %! assert (summ.num_targets == 1) %! end %!test %! % https://github.com/gnu-octave/octave-doctest/issues/268 %! if (compare_versions (OCTAVE_VERSION(), '9.0.0', '>=')) %! [nump, numt, summ] = doctest ('test_classdef.test_classdef'); %! assert (nump == numt && numt == 1) %! assert (summ.num_targets == 1) %! end %!test %! %% Issue #220, Issue #261, clear and w/o special order or workarounds %! if (compare_versions (OCTAVE_VERSION(), '7.0.0', '>=')) %! clear classes %! [numpass, numtest, summary] = doctest ('test_classdef'); %! assert (numpass == numtest) %! assert (summary.num_targets >= 3) %! end %!test %! %% Issue #220, workarounds for testing classdef are sensitive to %! % the order of tests above. Here we clear first. But we "preload" %! % some methods as a workaround. %! if (compare_versions (OCTAVE_VERSION(), '4.4.0', '>=')) %! clear classes %! if (compare_versions (OCTAVE_VERSION(), '6.0.0', '>=')) %! doc = help ('@test_classdef/amethod'); %! assert (length (doc) > 10) %! % dot notation broken before Octave 6 %! doc = help ('test_classdef.disp'); %! assert (length (doc) > 10) %! end %! % doctest ('test_classdef') %! [numpass, numtest, summary] = doctest ('test_classdef'); %! assert (numpass == numtest) %! if (compare_versions (OCTAVE_VERSION(), '4.4.0', '>=')) %! assert (summary.num_targets_without_tests <= 2) %! end %! if (compare_versions (OCTAVE_VERSION(), '6.0.0', '>=')) %! assert (summary.num_targets_without_tests <= 1) %! end %! % glorious future! Issue #261 %! % if (compare_versions (OCTAVE_VERSION(), 'X.Y.Z', '>=')) %! % assert (summary.num_targets_without_tests == 0) %! % end %! end %!test %! % maybe not recommended notation for classdef, but currently at least no error %! % https://github.com/gnu-octave/octave-doctest/issues/199 %! [nump, numt, summ] = doctest ('@classdef_infile/disp'); %! assert (nump >= 0) %!xtest %! % maybe not recommended notation for classdef %! [nump, numt, summ] = doctest ('@classdef_infile/disp'); %! assert (nump == 1 && numt == 1) %!test %! % https://github.com/gnu-octave/octave-doctest/issues/92 %! % Should have 3 targets and 4 tests %! % * general class help (2 tests) %! % * ctor (1 test) %! % * disp method (1 test) %! if (compare_versions (OCTAVE_VERSION(), '6.0.0', '>=')) %! [nump, numt, summ] = doctest ('classdef_infile'); %! assert (nump == numt && numt >= 3) %! assert (summ.num_targets >= 2) %! end %!test %! % https://github.com/gnu-octave/octave-doctest/issues/268 %! if (compare_versions (OCTAVE_VERSION(), '9.0.0', '>=')) %! [nump, numt, summ] = doctest ('classdef_infile'); %! assert (summ.num_targets == 3) %! assert (nump == numt && numt == 4) %! end %!test %! % monkey-patching methods to existing builtin-objects %! [nump, numt, summ1] = doctest ('logical'); %! % First, there is (at least) the "logical" builtin %! % >= b/c of https://github.com/gnu-octave/octave-doctest/issues/87 %! assert (summ1.num_targets >= 1) %! savepath = addpath ('test_methods_in_subdir'); %! % there should be at least "logical" builtin and "logical.mynewmethod" %! [nump, numt, summ] = doctest ('logical'); %! assert (summ.num_targets >= 2) %! assert (summ.num_targets >= summ1.num_targets) %! assert (nump >= 3 && numt >= 3) %! path(savepath); %!function y = foo (x) %! % >> foo (2) %! % 4 %! % >> foo (10) %! % 20 %! y = 2*x; %!endfunction %!assert (doctest ('foo', '-quiet')) %!test %! [n, t, summ] = doctest ('foo'); %! assert (n == 2) %! assert (t == n) %! assert (summ.num_targets == 1) %!function y = bar (x) %! % >> bar (2) %! % 42 %! % >> bar (3) %! % 3 %! y = x; %!endfunction %!test %! [n, t, summ] = doctest ('bar'); %! assert (n == 1) %! assert (t == 2) %!test %! [n, t, summ] = doctest({}); %! assert (n == 0) %! assert (t == 0) %! assert (summ.num_targets == 0) %!test %! % skip empty targets %! [n, t, summ] = doctest({'', ''}); %! assert (n == 0) %! assert (t == 0) %! assert (summ.num_targets == 0) %! assert (summ.num_targets_with_extraction_errors == 0) %!test %! % skip empty targets %! [n1, t1, summ1] = doctest('doctest'); %! [n2, t2, summ2] = doctest({'', '', 'doctest', ''}); %! assert (n1 == n2) %! assert (t1 == t2) %! assert (summ1.num_targets == summ2.num_targets) %! assert (summ2.num_targets_with_extraction_errors == 0) %!test %! % correct number of tests %! [n, t, summ] = doctest('test_tab_before_prompt'); %! assert (n == 2) %!test %! % correct number of error tests %! [n, t, summ] = doctest('test_error'); %! assert (t == 7) %!test %! % class inside a package %! if (compare_versions (OCTAVE_VERSION(), '6.0.0', '>=')) %! [n, t, summary] = doctest ("containers.Map"); %! assert (n == t) %! assert (summary.num_targets >= 10) % lots of methods %! end %!test %! % classdef.method %! if (compare_versions (OCTAVE_VERSION(), '6.0.0', '>=')) %! [n, t, summary] = doctest ("test_classdef.disp"); %! assert (n == t) %! assert (n == 1) %! end %!test %! % classdef.method, where method is external file %! if ((compare_versions (OCTAVE_VERSION(), '6.0.0', '>=')) && ... %! (compare_versions (OCTAVE_VERSION(), '9.0.0', '<'))) %! [n, t, summary] = doctest ("test_classdef.amethod"); %! assert (n == t) %! assert (n == 1) %! end %!xtest %! % classdef.method, where method is external file %! % https://github.com/gnu-octave/octave-doctest/issues/288 %! [n, t, summary] = doctest ("test_classdef.amethod"); %! assert (n == t) %! assert (n == 1) %!test %! % classdef.method %! if (compare_versions (OCTAVE_VERSION(), '6.0.0', '>=')) %! [n, t, summary] = doctest ("classdef_infile.disp"); %! assert (n == t) %! assert (n == 1) %! end %!test %! % classdef handle subclass delete behaviour %! if (compare_versions (OCTAVE_VERSION(), '9.0.0', '>=')) %! [n, t, summary] = doctest ('cdef_subhandle1'); %! assert (n == t) %! assert (n == 4) %! assert (summary.num_targets_with_extraction_errors == 0) %! end %!test %! % classdef handle subclass delete behaviour %! if (compare_versions (OCTAVE_VERSION(), '9.0.0', '>=')) %! [n, t, summary] = doctest ('cdef_subhandle2'); %! assert (n == t) %! assert (n == 3) %! assert (summary.num_targets_with_extraction_errors == 0) %! end doctest-0.8.1/test/bist_skip_xfail_errors.m.txt0000644000000000000000000000117615061220010016564 0ustar function bist_skip_xfail_errors() % >> a = 42 % a = 42 % % % The directive has an undefined error % >> a = 43 %doctest: +SKIP_IF(truthy) % a = 43 % % % The directive failed; test should not have run % >> a % a = 42 % % % The directive has an undefined error % >> b = 44 %doctest: +XFAIL_IF(undef) % b = 44 % % % The directive has a syntax error % >> b = 45 %doctest: +XFAIL_IF(\m/) % b = 45 % % % An actual passing test % >> c = 46 % c = 46 % % % An actual failing test % >> c = 47 % c = 4200 end %!test %! % some are supposed to fail %! [n, t, summ] = doctest('bist_skip_xfail_errors'); %! assert (n == 3) %! assert (t == 7) doctest-0.8.1/test/cdef_subhandle1.m0000644000000000000000000000050415061220010014201 0ustar classdef cdef_subhandle1 < handle % >> a = 42 % a = 42 properties end methods function obj = cdef_subhandle1() end function delete() % >> a = 43 % a = 43 end function disp(obj) % >> a = 44 % a = 44 % >> a = a + 1 % a = 45 fprintf('hi') end end end doctest-0.8.1/test/cdef_subhandle2.m0000644000000000000000000000040515061220010014202 0ustar classdef cdef_subhandle2 < handle % >> a = 42 % a = 42 properties end methods function obj = cdef_subhandle2() end function disp(obj) % >> a = 43 % a = 43 % >> a = a + 1 % a = 44 fprintf('hi') end end end doctest-0.8.1/test/classdef_infile.m0000644000000000000000000000141215061220010014303 0ustar classdef classdef_infile %CLASSDEF_INFILE A classdef living in a single m-file % % Some tests: % >> 6 + 7 % ans = 13 % % >> a = classdef_infile() % a = % class name = "default", age = 42 % % % This general help text should be shown for "help classdef_infile". % % There are also tests in the methods below. properties name age end methods function obj = classdef_infile(n, a) % constructor % >> a = 13 + 1 % a = 14 if (nargin ~= 2) obj.name = 'default'; obj.age = 42; else obj.name = n; obj.age = a; end end end methods function disp(obj) % >> a = 30 + 2 % a = 32 fprintf('class name = "%s", age = %d\n', obj.name, obj.age) end end end doctest-0.8.1/test/examples/0000755000000000000000000000000015061220010012633 5ustar doctest-0.8.1/test/examples/.oct-config0000644000000000000000000000001715061220010014662 0ustar encoding=utf-8 doctest-0.8.1/test/examples/greet.m0000644000000000000000000000016215061220010014116 0ustar function greet(user) % Returns a greeting. % % >> greet World % % Hello, World! disp(['Hello, ' user '!']); end doctest-0.8.1/test/private/0000755000000000000000000000000015061220010012467 5ustar doctest-0.8.1/test/private/test_in_private_dir.m0000644000000000000000000000006215061220010016700 0ustar function test_in_private_dir() % >> a = 5 % a = 5 doctest-0.8.1/test/test_angle_brackets.m0000644000000000000000000000165415061220010015204 0ustar function s = test_angle_brackets() % https://savannah.gnu.org/bugs/?45084 (Fixed in Octave 4.0) % % Copyright (c) 2015-2016, 2022 Colin B. Macdonald % SPDX-License-Identifier: BSD-3-Clause % % Fails on Octave 3.8 % >> oct38 = DOCTEST_OCTAVE && compare_versions(OCTAVE_VERSION, '4.0.0', '<'); % % % >> disp (test_angle_brackets ()) % doctest: +XFAIL_IF(oct38) % I <3 U % % % Slightly off-topic but newer Matlab have quotes around strings. % % Here's the Octave version, without quotes: % >> s = test_angle_brackets() % doctest: +SKIP_IF(oct38 || DOCTEST_MATLAB) % s = I <3 U % >> s = '

I heart you

' % doctest: +SKIP_IF(oct38 || DOCTEST_MATLAB) % s =

I heart you

% % % On Matlab, we need string indicators in the display: % >> s = test_angle_brackets() % doctest: +SKIP_IF(DOCTEST_OCTAVE) % s = 'I <3 U' % >> s = '

I heart you

' % doctest: +SKIP_IF(DOCTEST_OCTAVE) % s = '

I heart you

' s = 'I <3 U'; doctest-0.8.1/test/test_ans.m0000644000000000000000000000041315061220010013011 0ustar % Copyright (c) 2015 Colin B. Macdonald % SPDX-License-Identifier: BSD-3-Clause % % >> 4 % ans = 4 % % % >> ans % ans = 4 % % % >> 5 % doctest: +SKIP % ans = 5 % % % >> ans % ans = 4 % % % >> 6 % doctest: +XFAIL % ans = 7 % % % >> ans % ans = 6 doctest-0.8.1/test/test_ans.texinfo0000644000000000000000000000056615061220010014242 0ustar @c Copyright (c) 2015 Oliver Heimlich @c SPDX-License-Identifier: BSD-3-Clause @example >> 4 ans = 4 @end example @example >> ans ans = 4 @end example @example >> 5 @c doctest: +SKIP ans = 5 @end example @example >> ans ans = 4 @end example @example >> 6 @c doctest: +XFAIL ans = 7 @end example @example >> ans ans = 6 @end example doctest-0.8.1/test/test_blank_match.m0000644000000000000000000000027415061220010014500 0ustar function s = test_blank_match() % Issue #46 % % >> a = 3 + 4; % ... % % % Copyright (c) 2015 Michael Walter % Copyright (c) 2015 Colin B. Macdonald % SPDX-License-Identifier: BSD-3-Clause doctest-0.8.1/test/test_clear.m0000644000000000000000000000051515061220010013321 0ustar function test_clear() % Easy things first, clearing one variable % >> a = 6; % >> b = 7; % >> clear a % >> b % b = 7 % >> a % ...ndefined ... % % % Harder: % >> clear % >> a % ...ndefined ... % % % >> a = 4 % a = 4 % % % "clear all" clears stuff inside persistent vars % >> clear all % >> a % ...ndefined ... % % % >> a = 5 % a = 5 doctest-0.8.1/test/test_clear_all_first.m0000644000000000000000000000034315061220010015357 0ustar function test_clear_all_first() % If we "clear all" very early, our implementation may break if % subfunctions haven't yet been called. At least on Octave 4.2.1. % >> clear all % >> a % ...ndefined ... % % % >> a = 6 % a = 6 doctest-0.8.1/test/test_clear_isoctave.m0000644000000000000000000000047615061220010015224 0ustar function test_clear_isoctave() % Easy things first, clearing one variable % >> a = 6 % a = 6 % % % >> clear % >> a % ...ndefined ... % % % >> clear all % >> a % ...ndefined ... % % % Make sure these macros are still available after a clear % >> a = 42 % doctest: +XFAIL_IF(DOCTEST_OCTAVE | DOCTEST_MATLAB) % a = 0 doctest-0.8.1/test/test_comments.texinfo0000644000000000000000000000047015061220010015300 0ustar @example @group A = 5 @result{} A = 5 @comment Don't break my test @end group @end example @example @group A = 6 @comment My achy breaky test @result{} A = 6 @end group @end example @example @group A = 7 @*@c I just don't think my @result{} A = 7 @c test would understand @end group @end example doctest-0.8.1/test/test_comments_with_directives.m0000644000000000000000000000044715061220010017340 0ustar function test_comments_with_directives() % >> a = 6 %doctest: +XFAIL % comment (with parenthetical) % b = 5 % % % >> a = 7 % doctest: +XFAIL_IF (true) % comment % b = 5 % % % >> a = 8 % doctest: +XFAIL_IF (false) % comment (with parenthetical) % a = 8 doctest-0.8.1/test/test_compare_backspace.m0000644000000000000000000000114615061220010015656 0ustar function test_compare_backspace() % Matlab appears to emit backspace characters (0x08) for no apparent reason. % This doctest verifies that backspace characters are correctly processed % before comparison. Note a bit of fuss here because Octave needs this escape % sequence in double quotes which Matlab won't parse. % % >> if (exist('OCTAVE_VERSION')) % .. eval('sprintf("Hi, no question mark here?\x08 goodbye")') % .. else % .. disp(sprintf('Hi, no question mark here?\x08 goodbye')) % .. end % % Hi, no question mark here goodbye % % % All of the doctests should pass, and they manipulate this function. % doctest-0.8.1/test/test_compare_hyperlinks.m0000644000000000000000000000043515061220010016132 0ustar function test_compare_hyperlinks() % There are some tricky things that Matlab does to strings, such as adding % hyperlinks to help. We remove those before comparison, as verified by the % following doctest: % % >> disp('Hi there!
foo') % Hi there! foo doctest-0.8.1/test/test_deprecated.m0000644000000000000000000000030215061220010014325 0ustar function test_deprecated() % % Copyright (c) 2019 Colin B. Macdonald % SPDX-License-Identifier: BSD-3-Clause % % "??? " for errors deprecated 2019-10 % >> nosuchfcn(42) % ??? ...ndefined... % % doctest-0.8.1/test/test_diary_style.texinfo0000644000000000000000000000056715061220010016012 0ustar One is allowed to put diary-style tests within texinfo: @example @group >> a = 6 a = 6 @end group @end example @example @group >> % comments should >> # not interfere >> a = 7 a = 7 @end group @end example @example @group @c even these comments >> a = 7 a = 7 @end group @end example Blank lines ok: @example @group >> a = 7 a = 7 @end group @end example doctest-0.8.1/test/test_diary_style_mixed.texinfo0000644000000000000000000000031215061220010017164 0ustar Mixed, with print and result macros (do we want to allow this?) @example @group >> x = 8 @result{} x = 8 >> disp('abc'), s = disp('xyz') @print{} abc @result{} s = xyz @end group @end example doctest-0.8.1/test/test_dir/0000755000000000000000000000000015061220010012632 5ustar doctest-0.8.1/test/test_dir/.oct-config0000644000000000000000000000001715061220010014661 0ustar encoding=utf-8 doctest-0.8.1/test/test_dir/test1.m0000644000000000000000000000005215061220010014045 0ustar function test1() % >> a = 42 % a = 42 end doctest-0.8.1/test/test_dir/test_subdir/0000755000000000000000000000000015061220010015161 5ustar doctest-0.8.1/test/test_dir/test_subdir/.oct-config0000644000000000000000000000001715061220010017210 0ustar encoding=utf-8 doctest-0.8.1/test/test_dir/test_subdir/test2.m0000644000000000000000000000005215061220010016375 0ustar function test2() % >> b = 43 % b = 43 end doctest-0.8.1/test/test_ellipsis.m0000644000000000000000000000032615061220010014057 0ustar function test_ellipsis() % >> disp('...') % doctest: -ELLIPSIS % % ... % % % >> 1 + 2 % % ... % % % >> 1 + 2 % doctest: -ELLIPSIS % doctest: +XFAIL % % ... % % % >> 1 + 2 % doctest: -ELLIPSIS % % ans = 3 % doctest-0.8.1/test/test_ellipsis_match_empty.m0000644000000000000000000000043015061220010016445 0ustar function test_ellipsis_match_empty() % Ellipses should match empty string: % >> disp('abcdef') % abc...def % % % empty at ends: % >> disp('def') % ...def % % >> disp('def') % def... % % % Empty and whitespace: % >> disp('abc def') % abc ...def % % >> disp('abc def') % abc... def doctest-0.8.1/test/test_ellipsis_match_whitespace.m0000644000000000000000000000143115061220010017445 0ustar function test_ellipsis_match_whitespace() % Whitespace in middle % >> disp('abc def') % abc ... def % >> disp('abc def') % abc ... def % >> disp('abc def') % abc...def % % % Should fail: expects something surrounded by whitespace % >> disp('abc def') % doctest: +XFAIL % abc ... def % % % This is ok, because there are two whitespaces in input % >> disp('abc def') % abc ... def % % % Currently, ellipses will match empty the string but we trim begin/end of % lines, so these probably fail because there is nothing to match the space % after/before the "...". Probably ok to change this behaviour. % >> disp(' def') % doctest: +XFAIL % ... def % % >> disp('def ') % doctest: +XFAIL % def ... % % % However, these are ok: % >> disp('def') % ...def % % >> disp('def') % def... doctest-0.8.1/test/test_error.m0000644000000000000000000000201515061220010013361 0ustar function test_error() % The syntax changed a bit between Octave 9 and 10. We define some % variables to make it easier to test both. % % >> OLD_OCTAVE = is_octave() && compare_versions (ver ("octave").Version, "10.0.0", "<"); % >> NEW_OCTAVE = is_octave() && compare_versions (ver ("octave").Version, "10.0.0", ">="); % >> a = 42 % a = 42 % % % >> 3 + (1 + !)) % doctest: +XFAIL_UNLESS(NEW_OCTAVE) % % syntax error % >>> 3 + (1 + !)) % doctest: +XFAIL_UNLESS(NEW_OCTAVE) % ^ % >> a = a + 1 % a = 43 % % % Annoyingly, the doctest directive is still there and % appears in the error mesage. Perhaps we should move these % tests to bist.m. % % >> 4 + (1 + !)) % doctest: +XFAIL_UNLESS(OLD_OCTAVE) % % parse error: % % syntax error % >>> 4 + (1 + !)) % doctest: +XFAIL_UNLESS(OLD_OCTAVE) % ^ % % >> a = a + 1 % a = 44 % % % Caution: the file "bist.m" cares about how many tests are in this test_error.m file! % % Copyright (c) 2019, 2022, 2025 Colin B. Macdonald % SPDX-License-Identifier: BSD-3-Clause doctest-0.8.1/test/test_error.texinfo0000644000000000000000000000217215061220010014605 0ustar Copyright (c) 2019, 2025 Colin B. Macdonald SPDX-License-Identifier: BSD-3-Clause @example @comment doctest: -TEXINFO_SKIP_BLOCKS_WO_OUTPUT OLD = compare_versions (ver ("octave").Version, "10.0.0", "<"); @end example @example 2 + (1 + !)) % doctest: +SKIP_IF(OLD) @error{} syntax error @error{} >>> 2 + (1 + !)) % doctest: +SKIP_IF(OLD) @error{} ^ @end example @example 3 + (1 + !)) % doctest: +SKIP_UNLESS(OLD) @error{} parse error: @error{} @error{} syntax error @error{} >>> 3 + (1 + !)) % doctest: +SKIP_UNLESS(OLD) @error{} ^ @end example @example nosuchfcn(42) @error{} error: 'nosuchfcn' undefined near line 1... @end example @example nosuchfcn(42) @print{} error: 'nosuchfcn' undefined near line 1... @end example @example nosuchfcn(42) @error{} 'nosuchfcn' undefined near line 1... @end example In symbolic I had a failure when a test contained more code after an error. It did not pass without careful treatment of the typography of the texinfo error macro. @example a = 3; b = nosuchfcn(a) @error{} ... undefined... c = exp(a) @result{} c = 20.086 @end example doctest-0.8.1/test/test_format.m0000644000000000000000000000053215061220010013522 0ustar % Default number formatting % >> a = 1.3 % a = 1.3000 % % % If tests change it... % >> format long % >> b = 10/6 % b = 1.66666666...7 % % % ... they should change it back to defaults % >> format() % >> a % a = 1.3000 % % % TODO: should test that we restore the users settings, but probably % Issue #184 prevents doing this within our doctests. doctest-0.8.1/test/test_long_rows.m0000644000000000000000000000031115061220010014236 0ustar function y = test_long_rows() % >> repmat(1, 1, 50) % doctest: +XFAIL_IF(DOCTEST_MATLAB) % % ans = 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 doctest-0.8.1/test/test_long_rows.texinfo0000644000000000000000000000023615061220010015464 0ustar @example repmat(1, 1, 50) @result{} ans = 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 @end example doctest-0.8.1/test/test_methods_in_subdir/0000755000000000000000000000000015061220010015555 5ustar doctest-0.8.1/test/test_methods_in_subdir/.oct-config0000644000000000000000000000001715061220010017604 0ustar encoding=utf-8 doctest-0.8.1/test/test_methods_in_subdir/@logical/0000755000000000000000000000000015061220010017267 5ustar doctest-0.8.1/test/test_methods_in_subdir/@logical/mynewmethod.m0000644000000000000000000000030615061220010022004 0ustar function y = mynewmethod(x) %MYNEWMETHOD: monkey patch something onto class logical % >> a = mynewmethod(true); % >> double(a) % ans = 0 % >> double(islogical(a)) % ans = 1 y = ~ x; end doctest-0.8.1/test/test_multi_directives.m0000644000000000000000000000112615061220010015605 0ustar function test_multi_directives() % Dummy: % >> z = 0; % % % Will fail if we do not skip % >> s = 'a b' % doctest: +SKIP_IF (1 > 0) % doctest: -NORMALIZE_WHITESPACE % .. z = 1; % s = a b % % % and we did skip % >> z % z = 0 % % % % Will fail if we do not skip % >> s = 'a b' % my comment % doctest: -NORMALIZE_WHITESPACE % doctest: +SKIP_IF (true) % other comment % .. z = 2; % s = a b % % % and we did skip: % >> z % z = 0 % % % % >> s = 'a ...23 b' % doctest: -ELLIPSIS % doctest: +NORMALIZE_WHITESPACE % doctest: +XFAIL % .. z = 3; % s = a ... b % % % >> z % z = 3 doctest-0.8.1/test/test_multi_result.texinfo0000644000000000000000000000170715061220010016207 0ustar Current approach works for this block because the last line before a @@result becomes a command: @example @group [a, b] = deal (1, 2) @result{} a = 1 b = 2 c = 3 @result{} c = 3 d = 4, e = 5 @result{} d = 4 e = 5 @end group @end example But here we have trouble: @example @group [a, b] = deal (1, 2) @result{} a = 1 b = 2 c = 3 % I should be a command d = 4 @result{} c = 3 d = 4 @end group @end example A reasonable solution would be to use the intending level to help detect/classify lines as input/output. Here are some challenges for that: @example @group [a, b] = deal (1, 2) @result{} a = 1 b = 2 c = 3 d = 4 @result{} c = 3 d = 4 @end group @end example @example @group [a, b] = deal (1, 2) @result{} a = 1 b = 2 @comment don't mess up because of this outdented comment c = 3 d = 4 @result{} c = 3 d = 4 @end group @end example doctest-0.8.1/test/test_multi_return.texinfo0000644000000000000000000000136215061220010016205 0ustar Here we have no return value but only output: @example disp ('abc') @print{} abc @end example Here it is an return value: @example x = disp ('abc') @result{} x = abc @end example Here we have a combination of both: @example disp ('abc'); x = disp ('def') @print{} abc @result{} x = def @end example Here the warning is not part of the result: @example inv ([1 2; 2 4]) @print{} warning: ...matrix singular to machine precision... @result{} ans = Inf Inf Inf Inf @end example Here we have two results: @example [m, n] = size (zeros (1, 2)) @result{} m = 1 @result{} n = 2 @end example Intermediate results and printing: @example [m, n] = size (zeros (1, 2)), disp ('abc') @result{} m = 1 @result{} n = 2 @print{} abc @end example doctest-0.8.1/test/test_no_docs.m0000644000000000000000000000014215061220010013653 0ustar function test_no_docs() s = 'note this function has no docs: this should not cause an error'; doctest-0.8.1/test/test_not_an_mfile.txt0000644000000000000000000000033315061220010015246 0ustar % hello, I am not an m-file so I should not be doctested % % >> a = 6 % a = 7 % % % This test would fail if doctest finds it when traversing % a directory. If you run doctest on it directly, you should % get an error. doctest-0.8.1/test/test_shadow/0000755000000000000000000000000015061220010013341 5ustar doctest-0.8.1/test/test_shadow.m0000644000000000000000000000064015061220010013517 0ustar % Note this file shadows the class @test_shadow. The class will % take precedence: i.e., `doctest test_shadow` will test the class. % You can always override with `doctest test_shadow.m`. % % >> 2 + 2 % ans = 4 % >> 3 + 3 % ans = 6 % % % Note: in Matlab, help('test_shadow.m') returns the help for % '@test_shadow' even when the .m if explicitly given. So this % test will never run on Matlab. not_the_class = 42 doctest-0.8.1/test/test_shadow/.oct-config0000644000000000000000000000001715061220010015370 0ustar encoding=utf-8 doctest-0.8.1/test/test_shadow/test_in_shadow_dir.m0000644000000000000000000000043615061220010017372 0ustar function test_in_shadow_dir(x) % this test is in a directory shadowed/shadowing both a class % and an m-file. Its probably not entirely well-posed what % should happen here but on Octave at least, you can doctest % all three. % % >> a = 4 % a = 4 % >> a = 5 % a = 5 % >> a = 6 % a = 6 doctest-0.8.1/test/test_skip.m0000644000000000000000000000070715061220010013204 0ustar function test_skip() % This file should have 3 passed tests % % A test that would fail: % >> a = 5 % doctest: +SKIP % b = 7 % % % And a passing one: % >> a = 6 % a = 6 % % % Multiline input: % >> A = [1 2; % .. 3 4] % doctest: +SKIP % A = 42 % % % Put it on any line of multiline input: % >> A = [1 2; % doctest: +SKIP % .. 3 4] % A = 42 % % % Skip means not evaluated % >> a = 6 % a = 6 % >> a = 5 % doctest: +SKIP % >> a % a = 6 doctest-0.8.1/test/test_skip_comments.texinfo0000644000000000000000000000056515061220010016333 0ustar First actually set A (we'll check later that nobody messed it up) @example A = 5 @result{} A = 5 @end example @example A = 10 # doctest: +SKIP @result{} @end example @example A = 10 @c doctest: +SKIP @result{} A = 10 @comment doctest: +SKIP @result{} @end example Ensure A is still 5 (none of the skipped tests ran) @example A @result{} A = 5 @end example doctest-0.8.1/test/test_skip_if.m0000644000000000000000000000110615061220010013654 0ustar function test_skip_if() % This test should have 4 passing tests. % % Set up flags that determine test skipping. % >> my_true_flag = 1; % >> my_false_flag = 0; % % % A test that would fail: % >> a = 5 % doctest: +SKIP_IF(my_true_flag) % b = 7 % % % And a passing one: % >> a = 6 % doctest: +SKIP_IF(my_false_flag) % a = 6 % % % Check that it was indeed not skipped: % >> a % a = 6 % % % Multiline examples (put it on any line) % >> A = [1 2; % .. 3 4] % doctest: +SKIP_IF(my_true_flag) % A = 42 % % >> A = [1 2; % doctest: +SKIP_IF(my_true_flag) % .. 3 4] % A = 42 doctest-0.8.1/test/test_skip_if_multiple.m0000644000000000000000000000067015061220010015574 0ustar function test_skip_if_multiple() % Set up flags that determine test skipping. % >> false_flag = 0; % >> true_flag = 1; % >> z = 3; % % % The following test should not be skipped % >> z = 5 % doctest: +SKIP_IF(false_flag) % z = 5 % % % The following test should be skipped (thanks to the second condition) % >> z = 7 % doctest: +SKIP_IF(false_flag) % doctest: +SKIP_IF(true_flag) % w = 9 % % Check that it was indeed skipped: % >> z % z = 5 doctest-0.8.1/test/test_skip_malformed.texinfo0000644000000000000000000000033715061220010016451 0ustar @example @comment doctest: +SKIP Some verbatim text masquerading as an example (probably should live inside an @@verbatim block, but isn't). Should not raise an extraction error, because it is marked to skip. @end example doctest-0.8.1/test/test_skip_only_one.m0000644000000000000000000000015015061220010015076 0ustar function test_skip_only_one() % A file with just one skipped test: % >> a = 5 % doctest: +SKIP % b = 7 doctest-0.8.1/test/test_skip_unless.m0000644000000000000000000000113415061220010014570 0ustar function test_skip_unless() % This test should have 4 passing tests. % % Set up flags that determine test skipping. % >> my_true_flag = 1; % >> my_false_flag = 0; % % % A test that would fail: % >> a = 5 % doctest: +SKIP_UNLESS(my_false_flag) % b = 7 % % % And a passing one: % >> a = 6 % doctest: +SKIP_UNLESS(my_true_flag) % a = 6 % % % Check that it was indeed not skipped: % >> a % a = 6 % % % Multiline examples (put it on any line) % >> A = [1 2; % .. 3 4] % doctest: +SKIP_UNLESS(my_false_flag) % A = 42 % % >> A = [1 2; % doctest: +SKIP_UNLESS(my_false_flag) % .. 3 4] % A = 42 doctest-0.8.1/test/test_skip_wo_output.texinfo0000644000000000000000000000272415061220010016552 0ustar In the Octave community, the @code{@@example} block is commonly used non-semantically. While we do not endorse this, we do like dem ascii arts: @example _ _ _ __ _ _ __| | ___ ___| |_ ___ ___| |_ / _| |___ _| | / _` |/ _ \ / __| __/ _ \/ __| __| | |_| __\ \ /\ / / | | (_| | (_) | (__| || __/\__ \ |_ | _| |_ \ V V /|_| \__,_|\___/ \___|\__\___||___/\__| |_| \__| \_/\_/ (_) @end example (for later use) @example a = 4 @result{} a = 4 @end example Unfortunately, as there is no @strong{specified} output, this block is skipped too: @example a = 5; assert(false) @end example It really was skipped: @example a @result{} a = 4 @end example ``But wait'' you say, ``I want to test a block with no output!'' First, please patch Octave to use something like @code{@@verbatim} for diagrams, etc. Then patch doctest to remove this ``feature''. Done all that? No? Fine, well because we like you, there is a secret directive for you. It could disappear without notice in any future versions (we don't like you @strong{that} much). Try these: @example @comment doctest: -TEXINFO_SKIP_BLOCKS_WO_OUTPUT a = 5; @end example @example @comment doctest: +XFAIL @comment doctest: -TEXINFO_SKIP_BLOCKS_WO_OUTPUT b = 6; disp('do not skip me bro') @end example (note this test fails because it @strong{does} produce output). And indeed they were not skipped: @example a @result{} a = 5 b @result{} b = 6 @end example doctest-0.8.1/test/test_skip_wo_output_diary.m0000644000000000000000000000052015061220010016512 0ustar function test_skip_wo_output_diary() % % No output is specified here, so this code block would likely be % skipped if this was texinfo. But its not texinfo so this test % is expected to fail (and its a bug if it does not). % >> a = 5; % doctest: +XFAIL % .. disp('hi') % % % check that previous was not skipped % >> a % a = 5 doctest-0.8.1/test/test_skip_wo_output_diary.texinfo0000644000000000000000000000062115061220010017734 0ustar This file has diary-style within texinfo. Potential bugs here because of the empty block skipping feature. First, a block with no specified output: it should fail because it does have output: @example >> a = 5; >> disp('hi'); % doctest: +XFAIL @end example @example >> a a = 5 @end example The first command below should not invoke skipping: @example >> x = 5; >> y = x + 1 y = 6 @end example doctest-0.8.1/test/test_tab_before_prompt.m0000644000000000000000000000041115061220010015717 0ustar function test_tab_before_prompt() % This first test has spaces % >> a = 42 % a = 42 % % >> a = 43 % a = 43 % % % The second one has tabs: there should be two tests. % % Copyright (c) 2019, 2022 Colin B. Macdonald % SPDX-License-Identifier: BSD-3-Clause doctest-0.8.1/test/test_var.texinfo0000644000000000000000000000022615061220010014242 0ustar Make sure var macro can be used: @example @group @var{ABC} = 6 @result{} @var{ABC} = 6 @var{aBc} = 7 @result{} @var{aBc} = 7 @end group @end example doctest-0.8.1/test/test_warning.m0000644000000000000000000000020615061220010013675 0ustar function test_warning() % >> toeplitz ([1 2], [2 3]) % ...arning: ...olumn wins ...diagonal conflict... % ans = % 1 3 % 2 1 doctest-0.8.1/test/test_whitespace.m0000644000000000000000000000202515061220010014365 0ustar function test_whitespace() % >> disp('a b') % doctest: -NORMALIZE_WHITESPACE % a b % % >> disp('a b') % doctest: +NORMALIZE_WHITESPACE % a b % >> disp('a b') % doctest: +NORMALIZE_WHITESPACE % a b % % % Indenting is ok: % % >> disp('a b') % doctest: -NORMALIZE_WHITESPACE % % a b % % % But this should fail: % % >> disp('a b') % doctest: -NORMALIZE_WHITESPACE % doctest: +XFAIL % % a b % % % Multiline: Matlab and Octave format matrices differently but a % column vector is safe to use in cross-platform tests. % % >> A = [1; 2; -3] % doctest: -NORMALIZE_WHITESPACE % A = % 1 % 2 % -3 % % >> A % doctest: -NORMALIZE_WHITESPACE % % A = % % 1 % % 2 % % -3 % % % Matlab and Octave format differently, even for scalars, so % make sure our auto "ans = " bit still works. % % >> 42 % doctest: -NORMALIZE_WHITESPACE % 42 % % % Note: even very simple scalar tests like "x = 5" are difficult to % pass in both Octave and Matlab when using -NORMALIZE_WHITESPACE. doctest-0.8.1/test/test_windows_eol.texinfo0000644000000000000000000000031415061220010016001 0ustar @example @group A = 5 @result{} A = 5 @end group @end example @example @group A = 6 @result{} A = 6 @end group @end example @example @group >> A = 7 A = 7 @end group @end example doctest-0.8.1/test/test_xfail.m0000644000000000000000000000033215061220010013333 0ustar function test_xfail() % Let's initialize our dummy variable a. % >> a = 3 % a = 3 % % % The following test will fail: % >> a = 5 % doctest: +XFAIL % b = 7 % % % Check that it has been executed, though: % >> a % a = 5 doctest-0.8.1/test/test_xfail.texinfo0000644000000000000000000000014115061220010014551 0ustar This test is supposed to fail @example a = 5 @c doctest: +XFAIL @result{} b = 7 @end example doctest-0.8.1/test/test_xfail_if.m0000644000000000000000000000064515061220010014020 0ustar function test_xfail_if() % These flags control our expectations: % >> my_true_flag = 1; % >> my_false_flag = 0; % % % Let's initialize our dummy variable a: % >> a = 3 % a = 3 % % % The following test will fail: % >> a = 5 % doctest: +XFAIL_IF(my_true_flag) % b = 7 % % % Check that it has been executed, though: % >> a % a = 5 % % % This one should succeed, however: % >> a % doctest: +XFAIL_IF(my_false_flag) % a = 5 doctest-0.8.1/test/test_xfail_if_code.m0000644000000000000000000000070315061220010015005 0ustar function test_xfail_if_code() % % Initialize a dummy variable a: % >> a = 3 % a = 3 % % % The following test will fail: % >> a = 5 % doctest: +XFAIL_IF(6 + 0*now() >= 0) % b = 7 % % % Check that it has been executed: % >> a % a = 5 % % % This test succeeds and should not fail: % >> a % doctest: +XFAIL_IF(str2num('17') > 20) % a = 5 % % % There can be a space before the parenthesis % >> a % doctest: +XFAIL_IF (str2num('17') > 20) % a = 5 doctest-0.8.1/test/test_xfail_if_code.texinfo0000644000000000000000000000040615061220010016225 0ustar Dummy variable: @example a = 3 @result{} a = 3 @end example The following test will fail: @example @c doctest: +XFAIL_IF(6 + 0*now () >= 0) a = 5 @result{} b = 7 @end example value is changed (b/c xfail, not skip) @example a @result{} a = 5 @end example doctest-0.8.1/test/test_xfail_if_multiple.m0000644000000000000000000000073415061220010015732 0ustar function test_xfail_if_multiple() % Set up flags that determine test skipping. % >> false_flag = 0; % >> true_flag = 1; % >> z = 3; % % % The following test should succeed % >> z = 5 % doctest: +XFAIL_IF(false_flag) % z = 5 % % % Check that the first test was executed % >> z % z = 5 % % % The following test should be fail % >> z = 7 % doctest: +XFAIL_IF(false_flag) % doctest: +XFAIL_IF(true_flag) % w = 9 % % % Check that the second test was indeed executed % >> z % z = 7 doctest-0.8.1/test/test_xfail_unless.m0000644000000000000000000000066115061220010014731 0ustar function test_xfail_unless() % These flags control our expectations: % >> my_true_flag = 1; % >> my_false_flag = 0; % % % Let's initialize our dummy variable a: % >> a = 3 % a = 3 % % % The following test will fail: % >> a = 5 % doctest: +XFAIL_UNLESS(my_false_flag) % b = 7 % % % Check that it has been executed, though: % >> a % a = 5 % % % This one should succeed, however: % >> a % doctest: +XFAIL_UNLESS(my_true_flag) % a = 5 doctest-0.8.1/test/test_xskip_if_code.texinfo0000644000000000000000000000133115061220010016256 0ustar Dummy variable: @example a = 3 @result{} a = 3 @end example The regex for SKIP_IF argument should not cross newlines! First, note its ok to have syntax errors in skipped tests: @example @c doctest: +SKIP a = 5*(5+1)) @result{} b = 0 @end example But next we have a syntax error, combined with SKIP_IF code. The IF code should not glob the @code{a = } bit: might result in a syntax error if it did. @example @c doctest: +SKIP_IF(6 + 0*now () >= 0) a = 3*(4+1)) @result{} a = 15 @end example As above, but two SKIP_IF's @example @c doctest: +SKIP_IF(6 + 0*now () >= 0) @c doctest: +SKIP_IF(7 + 0*now () >= 0) a = 3*(4+1)) @result{} a = 15 @end example Finally, a is still 3 @example a @result{} a = 3 @end example doctest-0.8.1/test_extra/0000755000000000000000000000000015061220010012220 5ustar doctest-0.8.1/test_extra/README.md0000644000000000000000000000037315061220010013502 0ustar Part of the Octave-Doctest test-suite Contains tests that need dedicated syntax or version-specific tweaks to run. The built-in self tests in `run_tests.m` are run as part of our test suite. This directory will *not* be tested by `doctest` itself. doctest-0.8.1/test_extra/run_tests.m0000644000000000000000000001042715061220010014430 0ustar %% Copyright (c) 2022 Markus Mützel %% Copyright (c) 2022-2024 Colin B. Macdonald %% %% SPDX-License-Identifier: BSD-3-Clause %% %% Redistribution and use in source and binary forms, with or without %% modification, are permitted provided that the following conditions are met: %% %% 1. Redistributions of source code must retain the above copyright notice, %% this list of conditions and the following disclaimer. %% %% 2. Redistributions in binary form must reproduce the above copyright notice, %% this list of conditions and the following disclaimer in the documentation %% and/or other materials provided with the distribution. %% %% 3. Neither the name of the copyright holder nor the names of its %% contributors may be used to endorse or promote products derived from this %% software without specific prior written permission. %% %% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" %% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE %% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE %% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE %% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR %% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF %% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS %% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN %% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) %% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE %% POSSIBILITY OF SUCH DAMAGE. function success = run_tests () success = true; is_oct = exist('OCTAVE_VERSION', 'builtin') ~= 0; if (~ is_oct) save_enc = feature ('DefaultCharacterSet'); meh = feature ('DefaultCharacterSet', 'CP1252'); chdir ('test_encoding') ok1 = doctest ('test_matlab_style_CP1252'); chdir ('..') meh = feature ('DefaultCharacterSet', 'UTF-8'); chdir ('test_encoding_utf8') ok2 = doctest ('test_matlab_style_utf8'); chdir ('..') meh = feature ('DefaultCharacterSet', save_enc); success = success && ok1 && ok2; end % Octave is tested by BIST, no action required here end %!test %! %% test with file that is not encoded in UTF-8 %! % this is a bare minimal test: the file is probably not read correctly %! % until Octave >= 7.0.0. %! path_orig = path (); %! warn_orig = warning ('off', 'octave:get_input:invalid_utf8'); %! unwind_protect %! addpath (canonicalize_file_name ('test_encoding')); %! assert (doctest ('test_CP1252', '-quiet')); %! unwind_protect_cleanup %! path (path_orig) %! warning (warn_orig) %! end %!test %! %% CP1252 to UTF-8 internally, check byte counts %! % A bug in Octave 7 requires that the folder containing the .oct-config file %! % is in the load path (not the current directory). %! if (compare_versions (OCTAVE_VERSION(), '7.0.0', '>=')) %! path_orig = path (); %! unwind_protect %! addpath (canonicalize_file_name ('test_encoding')); %! assert (doctest ('test_bytecount_CP1252', '-quiet')); %! assert (doctest ('test_matlab_style_CP1252', '-quiet')); %! unwind_protect_cleanup %! path (path_orig) %! end %! end %!test %! %% On Octave 8, we can go to the actual directory %! if (compare_versions (OCTAVE_VERSION(), '8.0.0', '>=')) %! d = pwd (); %! unwind_protect %! cd ('test_encoding'); %! assert (doctest ('test_bytecount_CP1252', '-quiet')); %! assert (doctest ('test_matlab_style_CP1252', '-quiet')); %! unwind_protect_cleanup %! cd (d) %! end %! end %!test %! % A bug in Octave 7 requires that the folder containing the .oct-config file %! % is in the load path (not the current directory). %! if (compare_versions (OCTAVE_VERSION(), '7.0.0', '>=')) %! path_orig = path (); %! unwind_protect %! addpath (canonicalize_file_name ('test_encoding_utf8')); %! assert (doctest ('test_matlab_style_utf8', '-quiet')); %! unwind_protect_cleanup %! path (path_orig) %! end %! end %!test %! %% On Octave 8, we can go to the actual directory %! if (compare_versions (OCTAVE_VERSION(), '8.0.0', '>=')) %! d = pwd (); %! unwind_protect %! cd ('test_encoding_utf8'); %! assert (doctest ('test_matlab_style_utf8', '-quiet')); %! unwind_protect_cleanup %! cd (d) %! end %! end doctest-0.8.1/test_extra/test_encoding/0000755000000000000000000000000015061220010015045 5ustar doctest-0.8.1/test_extra/test_encoding/.oct-config0000644000000000000000000000002615061220010017074 0ustar encoding=windows-1252 doctest-0.8.1/test_extra/test_encoding/test_CP1252.m0000644000000000000000000000352515061220010017103 0ustar %% Copyright (c) 2022 Markus Mtzel %% %% SPDX-License-Identifier: BSD-3-Clause %% %% Redistribution and use in source and binary forms, with or without %% modification, are permitted provided that the following conditions are met: %% %% 1. Redistributions of source code must retain the above copyright notice, %% this list of conditions and the following disclaimer. %% %% 2. Redistributions in binary form must reproduce the above copyright notice, %% this list of conditions and the following disclaimer in the documentation %% and/or other materials provided with the distribution. %% %% 3. Neither the name of the copyright holder nor the names of its %% contributors may be used to endorse or promote products derived from this %% software without specific prior written permission. %% %% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" %% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE %% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE %% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE %% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR %% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF %% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS %% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN %% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) %% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE %% POSSIBILITY OF SUCH DAMAGE. %% -*- texinfo -*- %% @deftypefn {} {} test_CP1252 () %% Test function with some non-ASCII characters from CP1252 %% %% %% %% @example %% s = ' ' %% @result{} s = %% @end example %% @end deftypefn function test_CP1252 () help ("test_CP1252"); endfunction doctest-0.8.1/test_extra/test_encoding/test_bytecount_CP1252.m0000644000000000000000000000155015061220010021173 0ustar %% Copyright (c) 2022 Colin B. Macdonald %% %% SPDX-License-Identifier: BSD-3-Clause %% -*- texinfo -*- %% @deftypefn {} {} test_bytecount_CP1252 () %% Test function with some non-ASCII characters from CP1252 %% %% This file is encoded in CP1252. Here is the euro symbol: %% @example %% s = '' %% @result{} s = %% @end example %% %% In CP1252, the euro symbol is encoded as a single byte. %% However, GNU Octave uses @code{utf-8} internally: when we %% load this docstring, it will have three bytes: %% @example %% double(s) %% @result{} %% 226 130 172 %% @end example %% %% Even better, we can look at the bits and compare to known %% values (e.g., see Wikipedia). %% @example %% uint8(s); %% dec2bin(ans) %% @result{} %% 11100010 10000010 10101100 %% @end example %% @end deftypefn function test_bytecount_CP1252 () % no-op endfunction doctest-0.8.1/test_extra/test_encoding/test_matlab_style_CP1252.m0000644000000000000000000000116215061220010021636 0ustar function s = test_matlab_style_CP1252() % Test function with some non-ASCII characters from CP1252 % % This file is encoded in CP1252. Here is the euro symbol: % >> s = ''; % >> disp(s) % % >> disp(test_matlab_style_CP1252()) % % % % In CP1252, the euro symbol is encoded as a single byte. % % Its not our business how this is storied internally (its utf-8 % on Octave, not sure on Matlab). But we can convert explicitly % to utf-8: % >> nums = unicode2native(s, 'utf-8'); % >> double(nums) % 226 130 172 % Copyright (c) 2022-2023 Colin B. Macdonald % SPDX-License-Identifier: BSD-3-Clause s = ''; end doctest-0.8.1/test_extra/test_encoding_utf8/0000755000000000000000000000000015061220010016013 5ustar doctest-0.8.1/test_extra/test_encoding_utf8/.oct-config0000644000000000000000000000001715061220010020042 0ustar encoding=utf-8 doctest-0.8.1/test_extra/test_encoding_utf8/test_matlab_style_utf8.m0000644000000000000000000000106515061220010022660 0ustar function s = test_matlab_style_utf8() % Test function with some non-ASCII characters in UTF-8 % % This file is encoded in UTF-8. Here is the euro symbol: % >> s = '€'; % >> disp(s) % € % >> disp(test_matlab_style_utf8()) % € % % % Its not our business how this is storied internally (its utf-8 % on Octave, not sure on Matlab). But we can convert explicitly % to utf-8: % >> nums = unicode2native(s, 'utf-8'); % >> double(nums) % 226 130 172 % Copyright (c) 2022-2023 Colin B. Macdonald % SPDX-License-Identifier: BSD-3-Clause s = '€'; end