diff --git a/inst/doctest.m b/inst/doctest.m index 2f85e98..1801e51 100644 --- a/inst/doctest.m +++ b/inst/doctest.m @@ -250,7 +250,7 @@ %% @seealso{test} %% @end deftypefn -function varargout = doctest(what, varargin) +function varargout = doctest(targets, varargin) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Process parameters. @@ -265,8 +265,8 @@ end % if given a single object, wrap it in a cell array -if ~iscell(what) - what = {what}; +if ~iscell(targets) + targets = {targets}; end % input parsing for options and directives @@ -346,8 +346,8 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Collect and run tests %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -for i=1:numel(what) - summary = doctest_collect(what{i}, directives, summary, recursive, verbose, 0, fid); +for i=1:numel(targets) + summary = doctest_collect(targets{i}, directives, summary, recursive, verbose, 0, fid); end diff --git a/inst/private/doctest_collect.m b/inst/private/doctest_collect.m index 223dac1..6194da6 100644 --- a/inst/private/doctest_collect.m +++ b/inst/private/doctest_collect.m @@ -1,7 +1,7 @@ -function summary = doctest_collect(what, directives, summary, recursive, verbose, depth, fid) +function summary = doctest_collect(w, 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: +% 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; @@ -19,16 +19,20 @@ % 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(what, 'class')" works in Octave. - [~, ~, ext] = fileparts(what); + % 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 (what) == 3) % .oct explicitly + elseif (strcmp (ext, '.oct') && exist (w) == 3) % .oct explicitly type = 'octfile'; - elseif (exist (what) == 3) % .oct/.mex - [~, what, ~] = fileparts (what); % strip extension if present + elseif (exist (w) == 3) % .oct/.mex + [~, w, ~] = fileparts (w); % strip extension if present type = 'function'; % then access like any function else type = 'unknown'; @@ -38,10 +42,10 @@ % What about classdef in oct file above? Should we do this even if % type is 'octfile'? if (strcmp (type, 'unknown')) - if (~ isempty (what) && strcmp (what(1), '@')) - temp = what(2:end); + if (~ isempty (w) && strcmp (w(1), '@')) + temp = w(2:end); else - temp = what; + temp = w; end try temp = methods(temp); @@ -52,26 +56,26 @@ end if (strcmp (type, 'unknown')) - if (exist(what, 'dir')) + if (exist(w, 'dir')) type = 'dir'; - elseif (exist(what, 'file') || exist(what, 'builtin') || exist(what) == 103) + elseif (exist(w, 'file') || exist(w, 'builtin') || exist(w) == 103) type = 'function'; else type = 'unknown'; end end else % Matlab - if (strcmp(what(1), '@')) && ~isempty(methods(what(2:end))) + if (strcmp(w(1), '@')) && ~isempty(methods(w(2:end))) % covers "doctest @class", but not "doctest @class/method" type = 'class'; - elseif ~isempty(methods(what)) + elseif ~isempty(methods(w)) % covers "doctest class" type = 'class'; - elseif (exist(what, 'dir')) + elseif (exist(w, 'dir')) type = 'dir'; - elseif exist(what, 'file') || exist(what, 'builtin'); + elseif exist(w, 'file') || exist(w, 'builtin'); type = 'function'; - elseif ~isempty(help(what)) + elseif ~isempty(help(w)) % covers "doctest class.method" and "doctest class/method" type = 'function' else @@ -85,23 +89,23 @@ % Deal with directories if (strcmp(type, 'dir')) - if (strcmp(what, '.')) + 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(what(end), filesep())) + if (strcmp(w(end), filesep())) slashchar = ''; else slashchar = filesep(); end if (verbose) - fprintf(fid, '%s%s%s\n', spaces, what, slashchar); + fprintf(fid, '%s%s%s\n', spaces, w, slashchar); end end - oldcwd = chdir(what); + oldcwd = chdir(w); files = dir('.'); for i=1:numel(files) f = files(i).name; @@ -142,23 +146,23 @@ % TARGETS(i).depth How "deep" in a recursive traversal are we if strcmp(type, 'function') - target = collect_targets_function(what); + target = collect_targets_function(w); target.depth = depth; targets = [target]; elseif strcmp(type, 'class') - targets = collect_targets_class(what, depth); + targets = collect_targets_class(w, depth); elseif strcmp (type, 'octfile') - targets = collect_targets_octfile (what, depth); + targets = collect_targets_octfile (w, depth); elseif strcmp(type, 'texinfo') target = struct(); - target.name = what; + target.name = w; target.link = ''; target.depth = depth; - [target.docstring, target.error] = parse_texinfo(fileread(what)); + [target.docstring, target.error] = parse_texinfo(fileread(w)); targets = [target]; else target = struct(); - target.name = what; + target.name = w; target.link = ''; target.depth = depth; target.docstring = ''; @@ -242,31 +246,31 @@ -function target = collect_targets_function(what) +function target = collect_targets_function(w) target = struct(); - target.name = what; + target.name = w; if is_octave() target.link = ''; else - target.link = sprintf('%s', which(what), what); + target.link = sprintf('%s', which(w), w); end [target.docstring, target.error] = extract_docstring(target.name); end -function targets = collect_targets_class(what, depth) - if (strcmp(what(1), '@')) +function targets = collect_targets_class(w, depth) + if (strcmp(w(1), '@')) % Octave methods('@foo') gives java error, Matlab just says "No methods" - what = what(2:end); + w = w(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); + meths = methods (w); for i=1:numel (meths) - if (~ strcmp (meths{i}, what)) % skip @obj/obj - name = sprintf ('@%s%s%s', what, filesep (), meths{i}); + 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 @@ -275,26 +279,26 @@ % 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; + target.name = w; if is_octave() target.link = ''; else - target.link = sprintf('%s', which(what), what); + 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(what); + meths = methods(w); for i=1:numel(meths) target = struct(); if is_octave() - target.name = sprintf('@%s%s%s', what, filesep(), meths{i}); + target.name = sprintf('@%s%s%s', w, filesep(), meths{i}); target.link = ''; else - target.name = sprintf('%s.%s', what, meths{i}); - target.link = sprintf('%s', which(what), meths{i}, target.name); + 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); diff --git a/test/bist.m b/test/bist.m index 7653ce2..1641be6 100644 --- a/test/bist.m +++ b/test/bist.m @@ -131,3 +131,26 @@ function bist() %! [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)