doctest-0.8.2/0000755000000000000000000000000015207454466010071 5ustar doctest-0.8.2/CONTRIBUTORS0000644000000000000000000000036315207454466011753 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.2/COPYING0000644000000000000000000000304615207454466011127 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.2/DESCRIPTION0000644000000000000000000000113015207454466011572 0ustar Name: doctest Version: 0.8.2 Date: 2026-06-02 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.2/INDEX0000644000000000000000000000006015207454466010657 0ustar doctest >> Documentation tests testing doctest doctest-0.8.2/Makefile0000644000000000000000000001222615207454466011534 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) hash: $(OCTAVE_RELEASE_TARBALL) $(HTML_TARBALL) @md5sum $^ @sha256sum $^ release: hash @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.2/NEWS0000644000000000000000000001132415207454466010571 0ustar doctest 0.8.2 (2026-06-02) ========================== * Fix for Octave 11. 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.2/README.md0000644000000000000000000000247715207454466011362 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.2/inst/0000755000000000000000000000000015207454466011046 5ustar doctest-0.8.2/inst/.oct-config0000644000000000000000000000001715207454466013075 0ustar encoding=utf-8 doctest-0.8.2/inst/doctest.m0000644000000000000000000002774315207454466012706 0ustar %% Copyright (c) 2010 Thomas Grenfell Smith %% Copyright (c) 2011, 2013-2016 Michael Walter %% Copyright (c) 2015-2019, 2023, 2025-2026 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.2: 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.2/inst/private/0000755000000000000000000000000015207454466012520 5ustar doctest-0.8.2/inst/private/doctest_collect.m0000644000000000000000000004633115207454466016057 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.2/inst/private/doctest_colors.m0000644000000000000000000000145615207454466015732 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.2/inst/private/doctest_compare.m0000644000000000000000000000460015207454466016051 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.2/test/0000755000000000000000000000000015207454466011050 5ustar doctest-0.8.2/test/.hidden/0000755000000000000000000000000015207454466012361 5ustar doctest-0.8.2/test/.hidden/test_dont_run.m0000644000000000000000000000013615207454466015426 0ustar function test_dont_run() % This is in a dot directory so it should not run % >> a = 5 % a = 6 doctest-0.8.2/test/.oct-config0000644000000000000000000000001715207454466013077 0ustar encoding=utf-8 doctest-0.8.2/test/@test_class/0000755000000000000000000000000015207454466013314 5ustar doctest-0.8.2/test/@test_class/test_class.m0000644000000000000000000000037215207454466015640 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.2/test/@test_class/test_method.m0000644000000000000000000000027315207454466016013 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.2/test/@test_classdef/0000755000000000000000000000000015207454466013773 5ustar doctest-0.8.2/test/@test_classdef/amethod.m0000644000000000000000000000013515207454466015571 0ustar function amethod(obj) % This method is stored in a separate file. % >> b = 2 + 2 % b = 4 end doctest-0.8.2/test/@test_classdef/test_classdef.m0000644000000000000000000000157615207454466017005 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.2/test/@test_shadow/0000755000000000000000000000000015207454466013474 5ustar doctest-0.8.2/test/@test_shadow/test_shadow.m0000644000000000000000000000022315207454466016173 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.2/test/README.md0000644000000000000000000000027115207454466012327 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.2/test/bist.m0000644000000000000000000002152215207454466012171 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.2/test/bist_skip_xfail_errors.m.txt0000644000000000000000000000117615207454466016617 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.2/test/cdef_subhandle1.m0000644000000000000000000000050415207454466014234 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.2/test/cdef_subhandle2.m0000644000000000000000000000040515207454466014235 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.2/test/classdef_infile.m0000644000000000000000000000141215207454466014336 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.2/test/examples/0000755000000000000000000000000015207454466012666 5ustar doctest-0.8.2/test/examples/.oct-config0000644000000000000000000000001715207454466014715 0ustar encoding=utf-8 doctest-0.8.2/test/examples/greet.m0000644000000000000000000000016215207454466014151 0ustar function greet(user) % Returns a greeting. % % >> greet World % % Hello, World! disp(['Hello, ' user '!']); end doctest-0.8.2/test/private/0000755000000000000000000000000015207454466012522 5ustar doctest-0.8.2/test/private/test_in_private_dir.m0000644000000000000000000000006215207454466016733 0ustar function test_in_private_dir() % >> a = 5 % a = 5 doctest-0.8.2/test/test_angle_brackets.m0000644000000000000000000000165415207454466015237 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.2/test/test_ans.m0000644000000000000000000000041315207454466013044 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.2/test/test_ans.texinfo0000644000000000000000000000056615207454466014275 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.2/test/test_blank_match.m0000644000000000000000000000027415207454466014533 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.2/test/test_clear.m0000644000000000000000000000051515207454466013354 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.2/test/test_clear_all_first.m0000644000000000000000000000034315207454466015412 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.2/test/test_clear_isoctave.m0000644000000000000000000000047615207454466015257 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.2/test/test_comments.texinfo0000644000000000000000000000047015207454466015333 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.2/test/test_comments_with_directives.m0000644000000000000000000000044715207454466017373 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.2/test/test_compare_backspace.m0000644000000000000000000000114615207454466015711 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.2/test/test_compare_hyperlinks.m0000644000000000000000000000043515207454466016165 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.2/test/test_deprecated.m0000644000000000000000000000030215207454466014360 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.2/test/test_diary_style.texinfo0000644000000000000000000000056715207454466016045 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.2/test/test_diary_style_mixed.texinfo0000644000000000000000000000031215207454466017217 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.2/test/test_dir/0000755000000000000000000000000015207454466012665 5ustar doctest-0.8.2/test/test_dir/.oct-config0000644000000000000000000000001715207454466014714 0ustar encoding=utf-8 doctest-0.8.2/test/test_dir/test1.m0000644000000000000000000000005215207454466014100 0ustar function test1() % >> a = 42 % a = 42 end doctest-0.8.2/test/test_dir/test_subdir/0000755000000000000000000000000015207454466015214 5ustar doctest-0.8.2/test/test_dir/test_subdir/.oct-config0000644000000000000000000000001715207454466017243 0ustar encoding=utf-8 doctest-0.8.2/test/test_dir/test_subdir/test2.m0000644000000000000000000000005215207454466016430 0ustar function test2() % >> b = 43 % b = 43 end doctest-0.8.2/test/test_ellipsis.m0000644000000000000000000000032615207454466014112 0ustar function test_ellipsis() % >> disp('...') % doctest: -ELLIPSIS % % ... % % % >> 1 + 2 % % ... % % % >> 1 + 2 % doctest: -ELLIPSIS % doctest: +XFAIL % % ... % % % >> 1 + 2 % doctest: -ELLIPSIS % % ans = 3 % doctest-0.8.2/test/test_ellipsis_match_empty.m0000644000000000000000000000043015207454466016500 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.2/test/test_ellipsis_match_whitespace.m0000644000000000000000000000143115207454466017500 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.2/test/test_error.m0000644000000000000000000000201515207454466013414 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.2/test/test_error.texinfo0000644000000000000000000000217215207454466014640 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.2/test/test_format.m0000644000000000000000000000053215207454466013555 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.2/test/test_long_rows.m0000644000000000000000000000031115207454466014271 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.2/test/test_long_rows.texinfo0000644000000000000000000000023615207454466015517 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.2/test/test_methods_in_subdir/0000755000000000000000000000000015207454466015610 5ustar doctest-0.8.2/test/test_methods_in_subdir/.oct-config0000644000000000000000000000001715207454466017637 0ustar encoding=utf-8 doctest-0.8.2/test/test_methods_in_subdir/@logical/0000755000000000000000000000000015207454466017322 5ustar doctest-0.8.2/test/test_methods_in_subdir/@logical/mynewmethod.m0000644000000000000000000000030615207454466022037 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.2/test/test_multi_directives.m0000644000000000000000000000112615207454466015640 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.2/test/test_multi_result.texinfo0000644000000000000000000000170715207454466016242 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.2/test/test_multi_return.texinfo0000644000000000000000000000136215207454466016240 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.2/test/test_no_docs.m0000644000000000000000000000014215207454466013706 0ustar function test_no_docs() s = 'note this function has no docs: this should not cause an error'; doctest-0.8.2/test/test_not_an_mfile.txt0000644000000000000000000000033315207454466015301 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.2/test/test_shadow/0000755000000000000000000000000015207454466013374 5ustar doctest-0.8.2/test/test_shadow.m0000644000000000000000000000064015207454466013552 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.2/test/test_shadow/.oct-config0000644000000000000000000000001715207454466015423 0ustar encoding=utf-8 doctest-0.8.2/test/test_shadow/test_in_shadow_dir.m0000644000000000000000000000043615207454466017425 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.2/test/test_skip.m0000644000000000000000000000070715207454466013237 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.2/test/test_skip_comments.texinfo0000644000000000000000000000056515207454466016366 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.2/test/test_skip_if.m0000644000000000000000000000110615207454466013707 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.2/test/test_skip_if_multiple.m0000644000000000000000000000067015207454466015627 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.2/test/test_skip_malformed.texinfo0000644000000000000000000000033715207454466016504 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.2/test/test_skip_only_one.m0000644000000000000000000000015015207454466015131 0ustar function test_skip_only_one() % A file with just one skipped test: % >> a = 5 % doctest: +SKIP % b = 7 doctest-0.8.2/test/test_skip_unless.m0000644000000000000000000000113415207454466014623 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.2/test/test_skip_wo_output.texinfo0000644000000000000000000000272415207454466016605 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.2/test/test_skip_wo_output_diary.m0000644000000000000000000000052015207454466016545 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.2/test/test_skip_wo_output_diary.texinfo0000644000000000000000000000062115207454466017767 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.2/test/test_tab_before_prompt.m0000644000000000000000000000041115207454466015752 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.2/test/test_var.texinfo0000644000000000000000000000022615207454466014275 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.2/test/test_warning.m0000644000000000000000000000020615207454466013730 0ustar function test_warning() % >> toeplitz ([1 2], [2 3]) % ...arning: ...olumn wins ...diagonal conflict... % ans = % 1 3 % 2 1 doctest-0.8.2/test/test_whitespace.m0000644000000000000000000000202515207454466014420 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.2/test/test_windows_eol.texinfo0000644000000000000000000000031415207454466016034 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.2/test/test_xfail.m0000644000000000000000000000033215207454466013366 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.2/test/test_xfail.texinfo0000644000000000000000000000014115207454466014604 0ustar This test is supposed to fail @example a = 5 @c doctest: +XFAIL @result{} b = 7 @end example doctest-0.8.2/test/test_xfail_if.m0000644000000000000000000000064515207454466014053 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.2/test/test_xfail_if_code.m0000644000000000000000000000070315207454466015040 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.2/test/test_xfail_if_code.texinfo0000644000000000000000000000040615207454466016260 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.2/test/test_xfail_if_multiple.m0000644000000000000000000000073415207454466015765 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.2/test/test_xfail_unless.m0000644000000000000000000000066115207454466014764 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.2/test/test_xskip_if_code.texinfo0000644000000000000000000000133115207454466016311 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.2/test_extra/0000755000000000000000000000000015207454466012253 5ustar doctest-0.8.2/test_extra/README.md0000644000000000000000000000037315207454466013535 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.2/test_extra/run_tests.m0000644000000000000000000001042715207454466014463 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.2/test_extra/test_encoding/0000755000000000000000000000000015207454466015100 5ustar doctest-0.8.2/test_extra/test_encoding/.oct-config0000644000000000000000000000002615207454466017127 0ustar encoding=windows-1252 doctest-0.8.2/test_extra/test_encoding/test_CP1252.m0000644000000000000000000000352515207454466017136 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.2/test_extra/test_encoding/test_bytecount_CP1252.m0000644000000000000000000000155015207454466021226 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.2/test_extra/test_encoding/test_matlab_style_CP1252.m0000644000000000000000000000116215207454466021671 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.2/test_extra/test_encoding_utf8/0000755000000000000000000000000015207454466016046 5ustar doctest-0.8.2/test_extra/test_encoding_utf8/.oct-config0000644000000000000000000000001715207454466020075 0ustar encoding=utf-8 doctest-0.8.2/test_extra/test_encoding_utf8/test_matlab_style_utf8.m0000644000000000000000000000106515207454466022713 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