diff options
-rw-r--r-- | BUILD.txt | 51 | ||||
-rw-r--r-- | cmake/FindXapian.cmake | 42 | ||||
-rw-r--r-- | testing/Makefile | 3 | ||||
-rw-r--r-- | testing/README.txt (renamed from testing/README) | 34 | ||||
-rwxr-xr-x | testing/runtests.pl | 252 | ||||
-rw-r--r-- | testing/runtests.py | 201 |
6 files changed, 311 insertions, 272 deletions
diff --git a/BUILD.txt b/BUILD.txt new file mode 100644 index 0000000..8c6388c --- /dev/null +++ b/BUILD.txt @@ -0,0 +1,51 @@ +Doxygen uses cmake (http://www.cmake.org/) to build executables for various platforms. + +The first step is to create a build directory where the output should be stored. +Doxygen directory can be fully build outside of the source tree. + +The second step is to invoke cmake from within the build directory with the desired generator. + +For Linux/Unix systems do the following + + mkdir build + cd build + cmake -G "Unix Makefiles" path/to/root/of/doxygen/source/tree + make + +This also works for MacOSX, but if XCode is installed you can also generate an XCode project file + + cmake -G XCode path/to/root/of/doxygen/source/tree + +For Windows one can generate a Visual Studio project using + + cmake -G "Visual Studio 12 2013" path\to\root\of\doxygen\source\tree + +(this is for Visual Studio 12, there are typically also generators for other versions of +Visual Studio or other compiler environments like MinGW) + +Doxygen's cmake configuration provides a number of options: +- build_wizard Build the GUI frontend for doxygen. +- build_app Example showing how to embed doxygen in an application. +- build_xmlparser Example showing how to parse doxygen's XML output. +- build_search Build external search tools (doxysearch and doxyindexer) +- build_doc Build user manual +- use_sqlite3 Add support for sqlite3 output [experimental]. +- use_libclang Add support for libclang parsing. +- win_static Link with /MT in stead of /MD on windows + +An option can be turned on, by adding -D<option>=ON as a command line option, this can be +done when generating the initial build files, but also afterwards, i.e. to enable building +of the documentation after an initial cmake -G run, do + + cmake -Dbuild_doc=ON path/to/root/of/doxygen/source/tree + +To turn the option off use + + cmake -Dbuild_doc=OFF path/to/root/of/doxygen/source/tree + +To see the current value is of the various options, you can run + + cmake -L path/to/root/of/doxygen/source/tree + +The build target for building the documentation is 'doc' and the build target for +the regression tests is 'tests' diff --git a/cmake/FindXapian.cmake b/cmake/FindXapian.cmake new file mode 100644 index 0000000..6109d7f --- /dev/null +++ b/cmake/FindXapian.cmake @@ -0,0 +1,42 @@ +# Find Xapian search engine library +# +# XAPIAN_FOUND - system has Xapian +# XAPIAN_INCLUDE_DIR - the Xapian include directory +# XAPIAN_LIBRARIES - the libraries needed to use Xapian +# +# Copyright © 2010 Harald Sitter <apachelogger@ubuntu.com> +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +if(XAPIAN_INCLUDE_DIR AND XAPIAN_LIBRARIES) + # Already in cache, be silent + set(Xapian_FIND_QUIETLY TRUE) +endif(XAPIAN_INCLUDE_DIR AND XAPIAN_LIBRARIES) + +FIND_PATH(XAPIAN_INCLUDE_DIR xapian/version.h) + +FIND_LIBRARY(XAPIAN_LIBRARIES NAMES xapian) + +IF(XAPIAN_INCLUDE_DIR AND XAPIAN_LIBRARIES) + SET(XAPIAN_FOUND TRUE) +ELSE(XAPIAN_INCLUDE_DIR AND XAPIAN_LIBRARIES) + SET(XAPIAN_FOUND FALSE) +ENDIF(XAPIAN_INCLUDE_DIR AND XAPIAN_LIBRARIES) + +IF(XAPIAN_FOUND) + IF(NOT Xapian_FIND_QUIETLY) + MESSAGE(STATUS "Found Xapian: ${XAPIAN_LIBRARIES}") + ENDIF(NOT Xapian_FIND_QUIETLY) +ELSE(XAPIAN_FOUND) + IF(Xapian_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find Xapian") + ENDIF(Xapian_FIND_REQUIRED) + IF(NOT Xapian_FIND_QUIETLY) + MESSAGE(STATUS "Could not find Xapian") + ENDIF(NOT Xapian_FIND_QUIETLY) +ENDIF(XAPIAN_FOUND) + +# show the XAPIAN_INCLUDE_DIR and XAPIAN_LIBRARIES variables only in the advanced view +MARK_AS_ADVANCED(XAPIAN_INCLUDE_DIR XAPIAN_LIBRARIES) + diff --git a/testing/Makefile b/testing/Makefile deleted file mode 100644 index f6beb40..0000000 --- a/testing/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -tests: - @python runtests.py --doxygen ../build/bin/doxygen - diff --git a/testing/README b/testing/README.txt index 993ff48..ee3de59 100644 --- a/testing/README +++ b/testing/README.txt @@ -1,18 +1,18 @@ Doxygen regession test suite ============================ -This directory contains a set of regression tests. Each test consists of a -file starting with a 3 digit number and a corresponding directory whose name -has the same 3 digit number. The directory contains one or more reference -files that are compared against the XML output produced by doxygen. If the -result is the same, there is no regression and the test passes. If there is a +This directory contains a set of regression tests. Each test consists of a +file starting with a 3 digit number and a corresponding directory whose name +has the same 3 digit number. The directory contains one or more reference +files that are compared against the XML output produced by doxygen. If the +result is the same, there is no regression and the test passes. If there is a difference the test fails and the difference (in diff -u format) will be shown. -The runtest.pl script responsible for running the tests takes a number of +The runtest.py script responsible for running the tests takes a number of optional parameters: --id n: run test with number n only (the option may be specified +-id n: run test with number n only (the option may be specified multiple times) default is to run all tests. --updateref: update the reference files. Should be used in combination +-updateref: update the reference files. Should be used in combination with -id to update the reference file(s) for the given test. -all: can be used in combination with -updateref to update the reference files for all tests. @@ -20,29 +20,29 @@ optional parameters: -xmllint exe: run the specified xmllint executable. The runtest.pl has the following dependenies on 3rd party tools: -- perl to run the script +- python to run the script - xmllint to normalize the XML output - diff to show the differences in case a test fails - + Each test file can have a number of special comment lines that are extracted by the runtest.pl script and take the form: -// <identifier>: 'argument' +// <identifier>: 'argument' Where <identifier> can be one of: - objective: 'argument' provides the objective for the test (i.e. its purpose) -- check: 'argument' names a file that is generated by doxygen, which should +- check: 'argument' names a file that is generated by doxygen, which should be compared against the reference. - config: 'argument' is a line that is added to the default Doxyfile used to run doxygen on the test file. Example to run all tests: - perl runtest.pl + python runtest.py Example to run a test - perl runtest.pl -id 10 + python runtest.py -id 10 Example to update the reference files for a test - perl runtest.pl -updateref -id 10 + python runtest.py -updateref -id 10 -There is also a Makefile, which can be used to run all tests by simply -invoking make. +There is also a CMakeLists.txt, which can be used from the build directory +to run all tests by simply invoking 'make tests'. diff --git a/testing/runtests.pl b/testing/runtests.pl deleted file mode 100755 index 0e1938d..0000000 --- a/testing/runtests.pl +++ /dev/null @@ -1,252 +0,0 @@ -#!/usr/bin/perl - -# perl script to execute doxygen's regression test suite. -# -# Copyright (C) 1997-2014 by Dimitri van Heesch. -# -# Permission to use, copy, modify, and distribute this software and its -# documentation under the terms of the GNU General Public License is hereby -# granted. No representations are made about the suitability of this software -# for any purpose. It is provided "as is" without express or implied warranty. -# See the GNU General Public License for more details. -# -# Documents produced by Doxygen are derivative works derived from the -# input used in their production; they are not affected by this license. - -use strict; -use warnings; - -use Getopt::Long; -use Test::More; -use File::Path qw(make_path remove_tree); -use File::Copy qw(copy); - -my $Test = Test::Builder->new; -my $opt_doxygen_exe = 'doxygen'; -my $opt_xmllint_exe = 'xmllint'; -my $opt_updateref = ''; -my @opt_test_ids; -my $opt_all = ''; - -GetOptions( 'updateref' => \$opt_updateref, - 'doxygen=s' => \$opt_doxygen_exe, - 'xmllint=s' => \$opt_xmllint_exe, - 'id=i' => \@opt_test_ids, - 'all' => \$opt_all - ); - -sub read_two_files { - my $first = shift; - my $second = shift; - my $filter = shift; - my $success = 1; - my @errors; - - unless (open FIRST, "$first") { - $success = 0; - push @errors, "$first absent"; - } - unless (open SECOND, "$second") { - $success = 0; - push @errors, "$second absent"; - } - return ($success, @errors) unless $success; - - my $first_lines = join "",<FIRST>; - my $second_lines = join "",<SECOND>; - - close FIRST; - close SECOND; - - return ($success, $first_lines, $second_lines); -} - -sub compare_ok { - my $got_file = shift; - my $expected_file = shift; - my $name = shift; - my @read_result = read_two_files($got_file, $expected_file); - my $files_exist = shift @read_result; - - if ($files_exist) { - my ($got, $expected) = @read_result; - my $diff = `diff -u $got_file $expected_file`; - my $failed = length $diff; - return ($failed,"Difference between generated output and reference:\n$diff"); - } - else { - return (1,join "\n", @read_result); - } -} - -sub chop_volatile { - my $line = shift; - $line =~ s/version="\d\.\d+\.\d+(\.\d+)?(\-\d+)?"/version=""/g; # strip version - $line =~ s/file=".*\/(.*)"/file="$1"/g; # strip location - return $line; -} - -sub get_config { - my $file = shift; - my %config; - open F,"<$file"; - while (<F>) { - if (/\/\/\s*(\S+):\s*(.*)$/) { - my $key = $1; - my $val = $2; - chomp $val; - $config{$key} = [] unless defined $config{$key}; - push @{$config{$key}},$val; - } - } - return %config; -} - -sub perform_test { - my $test_file = shift; - my %config = get_config($test_file); - my $test_name = "[$test_file]: $config{'objective'}[0]"; - my $test_id = $test_file; - $test_id =~ s/^(\d+).*$/$1/; - my $test_out = "test_output_${test_id}"; - - if (scalar($config{'check'})==0) { - $Test->ok(0, $test_name); - $Test->diag("Test doesn't specify any files to check"); - return; - } - - # prepare test environment - remove_tree("$test_out"); - make_path("$test_out"); - copy("Doxyfile","$test_out"); - open(F,">>$test_out/Doxyfile"); - print F "INPUT = $test_file\n"; - print F "XML_OUTPUT = $test_out/out\n"; - foreach my $cfg (@{$config{'config'}}) { - print F "$cfg\n"; - } - close(F); - - # run doxygen - if (system("$opt_doxygen_exe $test_out/Doxyfile")!=0) { - $Test->ok(0, $test_name); - $Test->diag("Failed to run doxygen"); - return; - } - - # look for files to check against the reference - foreach my $fn (@{$config{'check'}}) { - if (!-f "$test_out/out/$fn") { - $Test->ok(0, $test_name); - $Test->diag("Non-existing file $test_out/out/$fn after 'check:' statement"); - return; - } - # run xmllint on the output file - my @lines = `$opt_xmllint_exe --format --noblanks --nowarning $test_out/out/$fn`; - if (scalar(@lines)>0 && open(F,">$test_out/$fn")) { - foreach my $line (@lines) { - print F chop_volatile($line); - } - close(F); - } else { - $Test->ok(0, $test_name); - $Test->diag("Failed to run xmllint on the doxygen output file $test_out/out/$fn"); - } - my ($failed,$msg) = compare_ok("$test_out/$fn","$test_id/$fn",$test_name); - if ($failed) { - $Test->ok(0, $test_name); - $Test->diag($msg); - return; - } - } - - # test passed - remove_tree("$test_out"); - $Test->ok(1, $test_name); -} - -sub update_test { - my $test_file = shift; - my %config = get_config($test_file); - my $test_name = "[$test_file]: $config{'objective'}[0]"; - my $test_id = $test_file; - $test_id =~ s/^(\d+).*$/$1/; - my $test_out = $test_id; - - # prepare reference environment - remove_tree("$test_out"); - make_path("$test_out"); - copy("Doxyfile","$test_out"); - open(F,">>$test_out/Doxyfile"); - print F "INPUT = $test_file\n"; - print F "XML_OUTPUT = $test_out/out\n"; - foreach my $cfg (@{$config{'config'}}) { - print F "$cfg\n"; - } - close(F); - - print "Updating reference for $test_name\n"; - - # run doxygen - if (system("$opt_doxygen_exe $test_out/Doxyfile")!=0) { - print("Error: failed to run doxygen"); - return; - } - my $err=0; - - # look for files to prepare as reference - foreach my $fn (@{$config{'check'}}) { - if (!-f "$test_out/out/$fn") { - printf("Error: Non-existing file $test_out/out/$fn after 'check:' statement\n"); - $err=1; - } - # run xmllint on the output file - if (!$err) { - my @lines = `$opt_xmllint_exe --format --noblanks --nowarning $test_out/out/$fn`; - if (scalar(@lines)>0 && open(F,">$test_out/$fn")) { - foreach my $line (@lines) { - print F chop_volatile($line); - } - close(F); - } else { - printf("Error: Failed to run xmllint on the doxygen output file $test_out/out/$fn\n"); - $err=1; - } - } - } - - if (!$err) { - # clean-up - remove_tree("$test_out/out"); - unlink("$test_out/Doxyfile"); - } -} - -# get the tests -my @tests; -if (scalar(@opt_test_ids)==0 && $opt_updateref && !$opt_all) { - printf("Error: updateref option requires -id to update a test or -all to update all\n"); - exit(1); -} -if (scalar(@opt_test_ids)>0) { - foreach my $t (@opt_test_ids) { - push @tests, glob("${t}_* 0${t}_* 00${t}_*"); - } -} else { - @tests = glob('[0-9][0-9][0-9]_*'); -} - -if ($opt_updateref) { - # update reference - foreach my $test (@tests) { - update_test($test); - } -} else { - # run tests - plan tests => scalar(@tests); - foreach my $test (@tests) { - perform_test($test); - } -} - diff --git a/testing/runtests.py b/testing/runtests.py new file mode 100644 index 0000000..d0e18dc --- /dev/null +++ b/testing/runtests.py @@ -0,0 +1,201 @@ +#!/usr/bin/python + +from __future__ import print_function +import argparse, glob, itertools, re, shutil, os, sys + +config_reg = re.compile('.*\/\/\s*(?P<name>\S+):\s*(?P<value>.*)$') + +class Tester: + def __init__(self,args,test): + self.args = args + self.test = test + self.update = args.updateref + self.config = self.get_config() + self.test_name = '[%s]: %s' % (self.test,self.config['objective'][0]) + self.test_id = self.test.split('_')[0] + if self.update: + self.test_out = self.args.inputdir+'/'+self.test_id + else: + self.test_out = self.args.outputdir+'/test_output_'+self.test_id + self.prepare_test() + + def compare_ok(self,got_file,expected_file,name): + if not os.path.isfile(got_file): + return (True,'%s absent' % got_file) + elif not os.path.isfile(expected_file): + return (True,'%s absent' % expected_file) + else: + diff = os.popen('diff -u %s %s' % (got_file,expected_file)).read() + if diff: + return (True,'Difference between generated output and reference:\n%s' % diff) + return (False,'') + + def get_config(self): + config = {} + with open(self.args.inputdir+'/'+self.test,'r') as f: + for line in f.readlines(): + m = config_reg.match(line) + if m: + key = m.group('name') + value = m.group('value') + if (key=='config'): + value = value.replace('$INPUTDIR',self.args.inputdir) + #print('key=%s value=%s' % (key,value)) + if key in config: + config[key].append(value) + else: + config[key] = [value] + return config + + def prepare_test(self): + # prepare test environment + shutil.rmtree(self.test_out,ignore_errors=True) + os.mkdir(self.test_out) + shutil.copy(self.args.inputdir+'/Doxyfile',self.test_out) + with open(self.test_out+'/Doxyfile','a') as f: + print('INPUT=%s/%s' % (self.args.inputdir,self.test), file=f) + print('STRIP_FROM_PATH=%s' % self.args.inputdir, file=f) + print('XML_OUTPUT=%s/out' % self.test_out, file=f) + print('EXAMPLE_PATH=%s' % self.args.inputdir, file=f) + if 'config' in self.config: + for option in self.config['config']: + print(option, file=f) + + if 'check' not in self.config or not self.config['check']: + print('Test doesn\'t specify any files to check') + sys.exit(1) + + # run doxygen + if os.system('%s %s/Doxyfile 2>/dev/null' % (self.args.doxygen,self.test_out))!=0: + print('Error: failed to run %s on %s/Doxyfile' % (self.args.doxygen,self.test_out)); + sys.exit(1) + + # update the reference data for this test + def update_test(self,testmgr): + print('Updating reference for %s' % self.test_name) + + if 'check' in self.config: + for check in self.config['check']: + check_file='%s/out/%s' % (self.test_out,check) + # check if the file we need to check is actually generated + if not os.path.isfile(check_file): + print('Non-existing file %s after \'check:\' statement' % check_file) + return + # convert output to canonical form + data = os.popen('%s --format --noblanks --nowarning %s' % (self.args.xmllint,check_file)).read() + if data: + # strip version + data = re.sub(r'xsd" version="[0-9.-]+"','xsd" version=""',data).rstrip('\n') + else: + print('Failed to run %s on the doxygen output file %s' % (self.args.xmllint,self.test_out)) + return + out_file='%s/%s' % (self.test_out,check) + with open(out_file,'w') as f: + print(data,file=f) + shutil.rmtree(self.test_out+'/out',ignore_errors=True) + os.remove(self.test_out+'/Doxyfile') + + # check the relevant files of a doxygen run with the reference material + def perform_test(self,testmgr): + # look for files to check against the reference + if 'check' in self.config: + for check in self.config['check']: + check_file='%s/out/%s' % (self.test_out,check) + # check if the file we need to check is actually generated + if not os.path.isfile(check_file): + testmgr.ok(False,self.test_name,msg='Non-existing file %s after \'check:\' statement' % check_file) + return + # convert output to canonical form + data = os.popen('%s --format --noblanks --nowarning %s' % (self.args.xmllint,check_file)).read() + if data: + # strip version + data = re.sub(r'xsd" version="[0-9.-]+"','xsd" version=""',data).rstrip('\n') + else: + testmgr.ok(False,self.test_name,msg='Failed to run %s on the doxygen output file %s' % (self.args.xmllint,self.test_out)) + return + out_file='%s/%s' % (self.test_out,check) + with open(out_file,'w') as f: + print(data,file=f) + ref_file='%s/%s/%s' % (self.args.inputdir,self.test_id,check) + (failed,msg) = self.compare_ok(out_file,ref_file,self.test_name) + if failed: + testmgr.ok(False,self.test_name,msg) + return + shutil.rmtree(self.test_out,ignore_errors=True) + testmgr.ok(True,self.test_name) + + def run(self,testmgr): + if self.update: + self.update_test(testmgr) + else: + self.perform_test(testmgr) + +class TestManager: + def __init__(self,args,tests): + self.args = args + self.tests = tests + self.num_tests = len(tests) + self.count=1 + self.passed=0 + print('1..%d' % self.num_tests) + + def ok(self,result,test_name,msg='Ok'): + if result: + print('ok %s - %s' % (self.count,test_name)) + self.passed = self.passed + 1 + else: + print('not ok %s - %s' % (self.count,test_name)) + print('-------------------------------------') + print(msg) + print('-------------------------------------') + self.count = self.count + 1 + + def result(self): + if self.passed==self.num_tests: + print('All tests passed!') + else: + print('%d out of %s tests failed' % (self.num_tests-self.passed,self.num_tests)) + return 0 if self.passed==self.num_tests else 1 + + def perform_tests(self): + for test in self.tests: + tester = Tester(self.args,test) + tester.run(self) + return 0 if self.args.updateref else self.result() + +def main(): + # argument handling + parser = argparse.ArgumentParser(description='run doxygen tests') + parser.add_argument('--updateref',help='update the reference data for a test',action="store_true") + parser.add_argument('--doxygen',nargs='?',default='doxygen',help='path/name of the doxygen executable') + parser.add_argument('--xmllint',nargs='?',default='xmllint',help='path/name of the xmllint executable') + parser.add_argument('--id',nargs='+',dest='ids',action='append',type=int,help='id of the test to perform') + parser.add_argument('--all',help='perform all tests',action="store_true") + parser.add_argument('--inputdir',nargs='?',default='.',help='input directory containing the tests') + parser.add_argument('--outputdir',nargs='?',default='.',help='output directory to write the doxygen output to') + args = parser.parse_args() + + # sanity check + if (not args.updateref is None) and (args.ids is None) and (args.all is None): + parser.error('--updateref requires either --id or --all') + + starting_directory = os.getcwd() + os.chdir(args.inputdir) + # find the tests to run + if args.ids: # test ids are given by user + tests = [] + for id in list(itertools.chain.from_iterable(args.ids)): + tests.append(glob.glob('%s_*'%id)) + tests.append(glob.glob('0%s_*'%id)) + tests.append(glob.glob('00%s_*'%id)) + tests = list(itertools.chain.from_iterable(tests)) + else: # find all tests + tests = glob.glob('[0-9][0-9][0-9]_*') + os.chdir(starting_directory) + + # create test manager to run the tests + testManager = TestManager(args,tests) + sys.exit(testManager.perform_tests()) + +if __name__ == '__main__': + main() |