doctest-0.7.0/0000755000000000000000000000000013445450536010062 5ustar doctest-0.7.0/CONTRIBUTORS0000644000000000000000000000027213445450536011743 0ustar Authors and Contributors ======================== Thomas Smith Michael Walter Colin B. Macdonald Oliver Heimlich (Please contact the developers if your name should be here but isn't!) doctest-0.7.0/COPYING0000644000000000000000000000304613445450536011120 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.7.0/DESCRIPTION0000644000000000000000000000111713445450536011570 0ustar Name: doctest Version: 0.7.0 Date: 2018-03-23 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://octave.sourceforge.io/doctest License: BSD-3-Clause doctest-0.7.0/INDEX0000644000000000000000000000006013445450536010650 0ustar doctest >> Documentation tests testing doctest doctest-0.7.0/NEWS0000644000000000000000000000726013445450536010566 0ustar 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.7.0/README.md0000644000000000000000000000271213445450536011343 0ustar Doctest [![Build Status](https://travis-ci.org/catch22/octave-doctest.svg?branch=master)](https://travis-ci.org/catch22/octave-doctest) ======= The [Octave-Forge Doctest](http://octave.sourceforge.net/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 greeting = greet(user) % Returns a greeting. % % >> greet World % % Hello, World! greeting = ['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/catch22/octave-doctest/wiki/WhoIsUsingDoctest) that is using Doctest. doctest-0.7.0/inst/0000755000000000000000000000000013445450536011037 5ustar doctest-0.7.0/inst/doctest.m0000644000000000000000000002702613445450536012671 0ustar %% Copyright (c) 2010 Thomas Grenfell Smith %% Copyright (c) 2011, 2013-2016 Michael Walter %% Copyright (c) 2015-2019 Colin B. Macdonald %% %% 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 %% >> 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) %% ??? ...ndefined ... %% @end group %% @end example %% (Note use of wildcards here; MATLAB spells this 'Undefined', while Octave %% uses 'undefined'). %% %% However, currently this does not work if the code emits other output %% @strong{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 %% >> "shiny Octave feature" % doctest: +XFAIL_IF(DOCTEST_MATLAB) %% ans = shiny Octave feature %% @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') % doctest: -NORMALIZE_WHITESPACE %% 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. %% %% @seealso{test} %% @end deftypefn function varargout = doctest(what, 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(what) what = {what}; 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/catch22/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.7.0: 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(what) summary = doctest_collect(what{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.7.0/inst/private/0000755000000000000000000000000013445450536012511 5ustar doctest-0.7.0/inst/private/doctest_collect.m0000644000000000000000000004411013445450536016041 0ustar function summary = doctest_collect(what, directives, summary, recursive, verbose, depth, fid) %DOCTEST_COLLECT Find and run doctests. % % The parameter WHAT 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 Colin B. Macdonald % Copyright (c) 2015 Oliver Heimlich % Copyright (C) 2018 Mike Miller % This is Free Software, BSD-3-Clause, see doctest.m for details. % TODO: methods('logical') octave/matlab differ: which behaviour do we want? % TODO: what about builtin "test" versus dir "test/"? Do we prefer dir? % determine type of target if is_octave() % Note: ripe for refactoring once "exist(what, 'class')" works in Octave. [~, ~, ext] = fileparts(what); if any(strcmpi(ext, {'.texinfo' '.texi' '.txi' '.tex'})) type = 'texinfo'; elseif (strcmp (ext, '.oct') && exist (what) == 3) % .oct explicitly type = 'octfile'; elseif (exist (what) == 3) % .oct/.mex [~, what, ~] = fileparts (what); % strip extension if present type = 'function'; % then access like any function elseif (exist(what, 'file') && ~exist(what, 'dir')) || exist(what, 'builtin') if (exist(['@' what], 'dir')) % special case, e.g., @logical is class, logical is builtin type = 'class'; else type = 'function'; end elseif (strcmp(what(1), '@')) % comes after 'file' above for "doctest @class/method" type = 'class'; elseif (exist(what, 'dir')) type = 'dir'; elseif exist(what) == 2 || exist(what) == 103 % Notes: % * exist('@class', 'dir') only works if pwd is the parent of % '@class', having it in the path is not sufficient. % * Return 2 on Octave 3.8 and 103 on Octave 4. type = 'class'; else % classdef classes are not detected by any of the above try temp = methods(what); type = 'class'; catch type = 'unknown'; end end else % Matlab if (strcmp(what(1), '@')) && ~isempty(methods(what(2:end))) % covers "doctest @class", but not "doctest @class/method" type = 'class'; elseif ~isempty(methods(what)) % covers "doctest class" type = 'class'; elseif (exist(what, 'dir')) type = 'dir'; elseif exist(what, 'file') || exist(what, 'builtin'); type = 'function'; elseif ~isempty(help(what)) % covers "doctest class.method" and "doctest class/method" 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(what, '.')) if (depth == 0) % cheap hack to not indent when calling "doctest ." depth = -1; end else spaces = repmat(' ', 1, 2*depth); if (strcmp(what(end), filesep())) slashchar = ''; else slashchar = filesep(); end if (verbose) fprintf(fid, '%s%s%s\n', spaces, what, slashchar); end end oldcwd = chdir(what); 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(what); target.depth = depth; targets = [target]; elseif strcmp(type, 'class') targets = collect_targets_class(what, depth); elseif strcmp (type, 'octfile') targets = collect_targets_octfile (what, depth); elseif strcmp(type, 'texinfo') target = struct(); target.name = what; target.link = ''; target.depth = depth; [target.docstring, target.error] = parse_texinfo(fileread(what)); targets = [target]; else target = struct(); target.name = what; 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(what) target = struct(); target.name = what; if is_octave() target.link = ''; else target.link = sprintf('%s', which(what), what); end [target.docstring, target.error] = extract_docstring(target.name); end function targets = collect_targets_class(what, depth) if (strcmp(what(1), '@')) % Octave methods('@foo') gives java error, Matlab just says "No methods" what = what(2:end); end % TODO: workaround github.com/catch22/octave-doctest/issues/135 by % accessing all non-constructor method help text *before* "help obj" if (is_octave ()) meths = methods (what); for i=1:numel (meths) if (~ strcmp (meths{i}, what)) % skip @obj/obj name = sprintf ('@%s%s%s', what, 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 = what; if is_octave() target.link = ''; else target.link = sprintf('%s', which(what), what); end target.depth = depth; [target.docstring, target.error] = extract_docstring(target.name); targets = target; % Next, add targets for all class methods meths = methods(what); for i=1:numel(meths) target = struct(); if is_octave() target.name = sprintf('@%s%s%s', what, filesep(), meths{i}); target.link = ''; else target.name = sprintf('%s.%s', what, meths{i}); target.link = sprintf('%s', which(what), 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') % looks like "doctest test_no_docs.m" gets us here: octave bug? if (regexp(name,'\.m$')) 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*)(?:⇒|=>|⊣|-\|)', '$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*(⇒|=>|⊣|-\|)', '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*(⇒|=>|⊣|-\|)', '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*)(?:⇒|=>|⊣|-\|)', ... '$1', ... 'once', 'lineanchors'); T{i} = strjoin (L, '\n'); end docstring = strjoin (T, '\n'); end doctest-0.7.0/inst/private/doctest_colors.m0000644000000000000000000000151013445450536015712 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 % This is Free Software, BSD-3-Clause, see doctest.m for details. % 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.7.0/inst/private/doctest_compare.m0000644000000000000000000000402513445450536016043 0ustar function match = doctest_compare(want, got, 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 % This is Free Software, BSD-3-Clause, see doctest.m for details. % 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, '> )' ... % ">> " '(.*(?:\n *\.\. .*)*)\n' ... % rest of line + ".. " lines '((?:(?:^ *$\n)?(?!\s*>>).*\S.*\n)*)']; % the output tests = []; test_matches = regexp(docstring, TEST_RE, 'tokens'); for i=1:length(test_matches) % each block should be split into source and desired output source = test_matches{i}{1}; tests(i).want = test_matches{i}{2}; % replace initial '..' by ' ' in subsequent lines lines = strsplit(source, '\n'); source = lines{1}; for j = 2:length(lines) T = regexp(lines{j}, '^\s*(\.\.)(.*)$', 'tokens'); assert(length(T) == 1); T = T{1}; assert(length(T) == 2); source = sprintf('%s\n %s', source, T{2}); end tests(i).source = source; % set default options tests(i).normalize_whitespace = defaults.normalize_whitespace; tests(i).ellipsis = defaults.ellipsis; tests(i).skip = {}; tests(i).xfail = {}; % find and process directives re = [ ... '[#%]\s*doctest:\s+' ... % e.g., "# doctest: " '([\+\-]\w+)' ... % token for cmd, e.g., "+XSKIP_IF" '(\s*\(' ... % token for code, starting with "(" '[^#%\n]+' ... % no newlines, no comments in code '\))?']; % ")" of code, at most one code arg directive_matches = regexp(tests(i).source, re, 'tokens'); for j = 1:length(directive_matches) directive = directive_matches{j}{1}; if (strcmp('+SKIP_IF', directive) || strcmp('+SKIP_UNLESS', directive) || strcmp('+XFAIL_IF', directive) || strcmp('+XFAIL_UNLESS', directive)) if length(directive_matches{j}) == 2 condition = directive_matches{j}{2}; else error('doctest: syntax error, expected %s(varname)', directive); end end if strcmp('NORMALIZE_WHITESPACE', directive(2:end)) tests(i).normalize_whitespace = strcmp(directive(1), '+'); elseif strcmp('ELLIPSIS', directive(2:end)) tests(i).ellipsis = strcmp(directive(1), '+'); elseif strcmp('+SKIP', directive) tests(i).skip{end + 1} = 'true'; elseif strcmp('+SKIP_IF', directive) tests(i).skip{end + 1} = condition; elseif strcmp('+SKIP_UNLESS', directive) tests(i).skip{end + 1} = sprintf('~(%s)', condition); elseif strcmp('+XFAIL', directive) tests(i).xfail{end + 1} = 'true'; elseif strcmp('+XFAIL_IF', directive) tests(i).xfail{end + 1} = condition; elseif strcmp('+XFAIL_UNLESS', directive) tests(i).xfail{end + 1} = sprintf('~(%s)', condition); else warning('Doctest:unexpected-directive', 'doctest: ignoring unexpected directive %s', directive); end end end results = doctest_run_tests(tests); end doctest-0.7.0/inst/private/doctest_run_tests.m0000644000000000000000000001075713445450536016454 0ustar function DOCTEST__results = doctest_run_tests(DOCTEST__tests) %DOCTEST_RUN_TESTS Used internally by doctest. % % Usage: % doctest_run_tests(tests) % Carefully evaluate each test in the "tests" structure in % a common newly-created clean namespace (specifically, this % functions workspace). % % The input is a structure with various fields including "tests.source", % the code to be run and "tests.want" the expected output. Various % other flags such as "tests.xfail" and "tests.ellipsis" effect how % the test is run and how the test output is compared. % % The return value is documented in "doctest_run_docstring". %% % Copyright (c) 2010 Thomas Grenfell Smith % Copyright (c) 2011, 2015 Michael Walter % Copyright (c) 2015-2017, 2019 Colin B. Macdonald % License: BSD-3-Clause, see doctest.m for details % Implementation note: all internal variables should start with % "DOCTEST__" as (1) these will necessarily be exposed to the tests % and (2) should not overwrite variables used by ongoing tests. % do not split long rows (TODO: how to do this on MATLAB?) if is_octave() split_long_rows(0, 'local'); end % initialize data store (used to preserve state across iterations % in the presence of "clear" and "clear all"s in tests) doctest_datastore('set_tests', DOCTEST__tests); for DOCTEST__i = 1:numel(DOCTEST__tests) % from the second iteration on, the only local variable that we can % rely on being present is DOCTEST__i doctest_datastore('set_current_index', DOCTEST__i); DOCTEST__current_test = doctest_datastore('get_current_test'); % define test-global constants (these are accessible by the tests) DOCTEST_OCTAVE = is_octave(); DOCTEST_MATLAB = ~DOCTEST_OCTAVE; % determine whether test should be skipped % (careful about Octave bug #46397 to not change the current value of “ans”) try eval (strcat ('DOCTEST__current_test.skip = ', ... doctest_join_conditions(DOCTEST__current_test.skip), ... ';')); catch DOCTEST__exception DOCTEST__current_test.skip = true; DOCTEST__current_test.xfail = []; % don't know (yet) % hack: put the error message into "got" DOCTEST__current_test.got = strcat('There was a problem executing +SKIP directive:', ... sprintf('\n'), ... doctest_format_exception(DOCTEST__exception)); DOCTEST__current_test.passed = false; end % determine whether test is expected to fail % (careful about Octave bug #46397 to not change the current value of “ans”) try eval (strcat ('DOCTEST__current_test.xfail = ', ... doctest_join_conditions(DOCTEST__current_test.xfail), ... ';')); catch DOCTEST__exception DOCTEST__current_test.skip = true; % test is not going to run DOCTEST__current_test.xfail = []; % cannot say % hack: put the error message into "got" DOCTEST__current_test.got = strcat('problem executing +XFAIL directive:', ... sprintf('\n'), ... doctest_format_exception(DOCTEST__exception)); DOCTEST__current_test.passed = false; end doctest_datastore('set_current_test', DOCTEST__current_test); if (DOCTEST__current_test.skip) continue end % run the test code try DOCTEST__got = evalc(DOCTEST__current_test.source); catch DOCTEST__exception DOCTEST__got = doctest_format_exception(DOCTEST__exception); end % at this point, we can only rely on the DOCTEST__got variable % being available DOCTEST__current_test = doctest_datastore('get_current_test'); DOCTEST__current_test.got = DOCTEST__got; % determine if test has passed DOCTEST__current_test.passed = doctest_compare(DOCTEST__current_test.want, DOCTEST__current_test.got, DOCTEST__current_test.normalize_whitespace, DOCTEST__current_test.ellipsis); if DOCTEST__current_test.xfail DOCTEST__current_test.passed = ~DOCTEST__current_test.passed; end doctest_datastore('set_current_test', DOCTEST__current_test); end % retrieve all tests from data store tests = doctest_datastore('get_tests'); doctest_datastore('clear_and_munlock'); % unwrap from cell-array, discarding skips %DOCTEST__results = cell2mat(tests); % fails b/c they have different fields DOCTEST__results = []; for j=1:numel(tests) if (~any(tests{j}.skip) || isfield(tests{j}, 'passed')) % skipped but pass was false, may be a directive so keep DOCTEST__results = [DOCTEST__results tests{j}]; end end end doctest-0.7.0/inst/private/is_octave.m0000644000000000000000000000125513445450536014646 0ustar function r = is_octave() %IS_OCTAVE True if we are running Octave, false for Matlab. %% % Copyright (c) 2015 Colin B. Macdonald % This is Free Software, BSD-3-Clause, see doctest.m for details. % Timings for different implementations, 10000 calls % % test Matlab Octave % ---------------------------- % try-catch 1.2s 0.18s % if-exist 0.14s 0.22s % dummy 0.13s 0.13s % % Conclusions: "if-exist" only twice as slow as "dummy" (always return % true), so no need to bother with a persistent variable. r = exist('OCTAVE_VERSION', 'builtin') ~= 0; %try % OCTAVE_VERSION; % r = true; %catch % r = false; %end end doctest-0.7.0/octave-doctest.metainfo.xml0000644000000000000000000000254013445450536015332 0ustar octave-doctest www.octave.org-octave.desktop 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/catch22/octave-doctest/issues/new FSFAP BSD-3-Clause Octave-Forge Community octave-maintainers@gnu.org
doctest-0.7.0/test/0000755000000000000000000000000013445450536011041 5ustar doctest-0.7.0/test/@test_class/0000755000000000000000000000000013445450536013305 5ustar doctest-0.7.0/test/@test_class/test_class.m0000644000000000000000000000037213445450536015631 0ustar function obj = test_class % % >> class(test_class) % ans = 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.7.0/test/@test_class/test_method.m0000644000000000000000000000027313445450536016004 0ustar function result = test_method(obj, varargin) % % >> m = test_class; test_method(m) % ans = Default Name is 42 years old. result = sprintf('%s is %d years old.', obj.name, obj.age); end doctest-0.7.0/test/@test_classdef/0000755000000000000000000000000013445450536013764 5ustar doctest-0.7.0/test/@test_classdef/amethod.m0000644000000000000000000000013513445450536015562 0ustar function amethod(obj) % This method is stored in a separate file. % >> b = 2 + 2 % b = 4 end doctest-0.7.0/test/@test_classdef/test_classdef.m0000644000000000000000000000136713445450536016774 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) % 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.7.0/test/@test_shadow/0000755000000000000000000000000013445450536013465 5ustar doctest-0.7.0/test/@test_shadow/test_shadow.m0000644000000000000000000000022313445450536016164 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.7.0/test/bist.m0000644000000000000000000000605113445450536012162 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 cannat call doctest recursively % https://github.com/catch22/octave-doctest/issues/184 end %!error doctest () %!error [a, b, c, d] = doctest ('double') %!assert (doctest ('doctest', '-quiet')) %!assert (~ doctest ('there_is_no_such_file', '-quiet')) %!assert (~ doctest ({'doctest', 'there_is_no_such_file'}, '-quiet')) %!error %! % TODO: maybe this should be EXTRACTION_ERROR, not raise an error... %! doctest @there_is_no_such_class -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 %! [nump, numt, summ] = doctest ('@test_classdef/amethod'); %! assert (nump == 1 && numt == 1) %!xtest %! % https://github.com/catch22/octave-doctest/issues/92 %! [nump, numt, summ] = doctest ('@test_classdef/disp'); %! assert (nump == 1 && numt == 1) %!xtest %! % https://github.com/catch22/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) %! [nump, numt, summ] = doctest ('test_classdef'); %! assert (nump == 5 && numt == 5) %! assert (summ.num_targets == 4) %!xtest %! % Currently cannot even run %! % https://github.com/catch22/octave-doctest/issues/199 %! [nump, numt, summ] = doctest ('@classdef_infile/disp'); %! assert (nump >= 0) %!xtest %! % https://github.com/catch22/octave-doctest/issues/92 %! [nump, numt, summ] = doctest ('@classdef_infile/disp'); %! assert (nump == 1 && numt == 1) %!xtest %! % https://github.com/catch22/octave-doctest/issues/92 %! % Should have 3 targets and 4 tests %! % * general class help (2 tests) %! % * ctor (1 test) %! % * disp method (1 test) %! [nump, numt, summ] = doctest ('classdef_infile'); %! assert (nump == 4 && numt == 4) %! assert (summ.num_targets == 3) %!xtest %! % 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/catch22/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); doctest-0.7.0/test/bist_skip_xfail_errors.m.txt0000644000000000000000000000117613445450536016610 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.7.0/test/classdef_infile.m0000644000000000000000000000141213445450536014327 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.7.0/test/examples/0000755000000000000000000000000013445450536012657 5ustar doctest-0.7.0/test/examples/greet.m0000644000000000000000000000020213445450536014135 0ustar function greeting = greet(user) % Returns a greeting. % % >> greet World % % Hello, World! greeting = ['Hello, ' user '!']; end doctest-0.7.0/test/private/0000755000000000000000000000000013445450536012513 5ustar doctest-0.7.0/test/private/test_in_private_dir.m0000644000000000000000000000006213445450536016724 0ustar function test_in_private_dir() % >> a = 5 % a = 5 doctest-0.7.0/test/test_angle_brackets.m0000644000000000000000000000056513445450536015230 0ustar function s = test_angle_brackets() % https://savannah.gnu.org/bugs/?45084 (Fixed in Octave 4.0) % % Fails on 3.8 % >> oct38 = DOCTEST_OCTAVE && compare_versions(OCTAVE_VERSION, '4.0.0', '<'); % % % >> s = test_angle_brackets() % doctest: +XFAIL_IF(oct38) % s = I <3 U % >> s = '

I heart you

' % doctest: +XFAIL_IF(oct38) % s =

I heart you

s = 'I <3 U'; doctest-0.7.0/test/test_ans.m0000644000000000000000000000027113445450536013037 0ustar % >> 4 % ans = 4 % % % >> ans % ans = 4 % % % >> 5 % doctest: +SKIP % ans = 5 % % % >> ans % ans = 4 % % % >> 6 % doctest: +XFAIL % ans = 7 % % % >> ans % ans = 6 doctest-0.7.0/test/test_ans.texinfo0000644000000000000000000000044613445450536014263 0ustar @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.7.0/test/test_blank_match.m0000644000000000000000000000010413445450536014514 0ustar function s = test_blank_match() % Issue #46 % % >> a = 3 + 4; % ... doctest-0.7.0/test/test_clear.m0000644000000000000000000000053113445450536013343 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.7.0/test/test_clear_all_first.m0000644000000000000000000000034713445450536015407 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.7.0/test/test_clear_isoctave.m0000644000000000000000000000050613445450536015242 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.7.0/test/test_comments.texinfo0000644000000000000000000000047013445450536015324 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.7.0/test/test_comments_with_directives.m0000644000000000000000000000044713445450536017364 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.7.0/test/test_compare_backspace.m0000644000000000000000000000115213445450536015677 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 % .. sprintf('Hi, no question mark here?\x08 goodbye') % .. end % % ans = % % Hi, no question mark here goodbye % % % All of the doctests should pass, and they manipulate this function. % doctest-0.7.0/test/test_compare_hyperlinks.m0000644000000000000000000000043513445450536016156 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.7.0/test/test_diary_style.texinfo0000644000000000000000000000056713445450536016036 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.7.0/test/test_diary_style_mixed.texinfo0000644000000000000000000000031213445450536017210 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.7.0/test/test_dir/0000755000000000000000000000000013445450536012656 5ustar doctest-0.7.0/test/test_dir/test1.m0000644000000000000000000000005213445450536014071 0ustar function test1() % >> a = 42 % a = 42 end doctest-0.7.0/test/test_dir/test_subdir/0000755000000000000000000000000013445450536015205 5ustar doctest-0.7.0/test/test_dir/test_subdir/test2.m0000644000000000000000000000005213445450536016421 0ustar function test2() % >> b = 43 % b = 43 end doctest-0.7.0/test/test_ellipsis.m0000644000000000000000000000032013445450536014075 0ustar function test_ellipsis() % >> '...' % doctest: -ELLIPSIS % % ... % % % >> 1 + 2 % % ... % % % >> 1 + 2 % doctest: -ELLIPSIS % doctest: +XFAIL % % ... % % % >> 1 + 2 % doctest: -ELLIPSIS % % ans = 3 % doctest-0.7.0/test/test_ellipsis_match_empty.m0000644000000000000000000000043013445450536016471 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.7.0/test/test_ellipsis_match_whitespace.m0000644000000000000000000000143113445450536017471 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.7.0/test/test_format.m0000644000000000000000000000053213445450536013546 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.7.0/test/test_long_rows.m0000644000000000000000000000031113445450536014262 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.7.0/test/test_long_rows.texinfo0000644000000000000000000000023613445450536015510 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.7.0/test/test_methods_in_subdir/0000755000000000000000000000000013445450536015601 5ustar doctest-0.7.0/test/test_methods_in_subdir/@logical/0000755000000000000000000000000013445450536017313 5ustar doctest-0.7.0/test/test_methods_in_subdir/@logical/mynewmethod.m0000644000000000000000000000030313445450536022025 0ustar function y = mynewmethod(x) %MYNEWMETHOD: monkey patch something onto class logical % >> a = 42 % a = 42 % >> mynewmethod(true) % ans = 0 % >> islogical(ans) % ans = 1 y = ~ x; end doctest-0.7.0/test/test_multi_directives.m0000644000000000000000000000112613445450536015631 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.7.0/test/test_multi_result.texinfo0000644000000000000000000000170713445450536016233 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.7.0/test/test_multi_return.texinfo0000644000000000000000000000136213445450536016231 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.7.0/test/test_no_docs.m0000644000000000000000000000014213445450536013677 0ustar function test_no_docs() s = 'note this function has no docs: this should not cause an error'; doctest-0.7.0/test/test_not_an_mfile.txt0000644000000000000000000000033313445450536015272 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.7.0/test/test_shadow/0000755000000000000000000000000013445450536013365 5ustar doctest-0.7.0/test/test_shadow.m0000644000000000000000000000064013445450536013543 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.7.0/test/test_shadow/test_in_shadow_dir.m0000644000000000000000000000043613445450536017416 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.7.0/test/test_skip.m0000644000000000000000000000070713445450536013230 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.7.0/test/test_skip_comments.texinfo0000644000000000000000000000056513445450536016357 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.7.0/test/test_skip_if.m0000644000000000000000000000110613445450536013700 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.7.0/test/test_skip_if_multiple.m0000644000000000000000000000067013445450536015620 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.7.0/test/test_skip_malformed.texinfo0000644000000000000000000000033713445450536016475 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.7.0/test/test_skip_only_one.m0000644000000000000000000000015013445450536015122 0ustar function test_skip_only_one() % A file with just one skipped test: % >> a = 5 % doctest: +SKIP % b = 7 doctest-0.7.0/test/test_skip_unless.m0000644000000000000000000000113413445450536014614 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.7.0/test/test_skip_wo_output.texinfo0000644000000000000000000000272413445450536016576 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.7.0/test/test_skip_wo_output_diary.m0000644000000000000000000000052013445450536016536 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.7.0/test/test_skip_wo_output_diary.texinfo0000644000000000000000000000062113445450536017760 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.7.0/test/test_var.texinfo0000644000000000000000000000022613445450536014266 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.7.0/test/test_warning.m0000644000000000000000000000020613445450536013721 0ustar function test_warning() % >> toeplitz ([1 2], [2 3]) % ...arning: ...olumn wins ...diagonal conflict... % ans = % 1 3 % 2 1 doctest-0.7.0/test/test_whitespace.m0000644000000000000000000000202513445450536014411 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.7.0/test/test_windows_eol.texinfo0000644000000000000000000000031413445450536016025 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.7.0/test/test_xfail.m0000644000000000000000000000033213445450536013357 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.7.0/test/test_xfail.texinfo0000644000000000000000000000014113445450536014575 0ustar This test is supposed to fail @example a = 5 @c doctest: +XFAIL @result{} b = 7 @end example doctest-0.7.0/test/test_xfail_if.m0000644000000000000000000000064513445450536014044 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.7.0/test/test_xfail_if_code.m0000644000000000000000000000070313445450536015031 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.7.0/test/test_xfail_if_code.texinfo0000644000000000000000000000040613445450536016251 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.7.0/test/test_xfail_if_multiple.m0000644000000000000000000000073413445450536015756 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.7.0/test/test_xfail_unless.m0000644000000000000000000000066113445450536014755 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.7.0/test/test_xskip_if_code.texinfo0000644000000000000000000000133113445450536016302 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