From: Amitai Schlair Date: Sun, 22 Jan 2012 15:53:45 +0000 (-0500) Subject: Merge branch 'cvs' of github.com:schmonz/ikiwiki into cvs X-Git-Tag: 3.20120202~26^2~9 X-Git-Url: http://git.vanrenterghem.biz/git.ikiwiki.info.git/commitdiff_plain/c74571c7582b7ce3111841a0229f84f7059711aa?hp=0b22579368f3f98844e674921f1dc0067ce30ccf Merge branch 'cvs' of github.com:schmonz/ikiwiki into cvs Conflicts: TODO.cvs t/cvs.t --- diff --git a/IkiWiki/Plugin/cvs.pm b/IkiWiki/Plugin/cvs.pm index 71566d212..97a568c0e 100644 --- a/IkiWiki/Plugin/cvs.pm +++ b/IkiWiki/Plugin/cvs.pm @@ -35,10 +35,14 @@ use IkiWiki; use File::chdir; + +# GENERAL PLUGIN API CALLS + sub import { - hook(type => "genwrapper", id => "cvs", call => \&genwrapper); hook(type => "checkconfig", id => "cvs", call => \&checkconfig); hook(type => "getsetup", id => "cvs", call => \&getsetup); + hook(type => "genwrapper", id => "cvs", call => \&genwrapper); + hook(type => "rcs", id => "rcs_update", call => \&rcs_update); hook(type => "rcs", id => "rcs_prepedit", call => \&rcs_prepedit); hook(type => "rcs", id => "rcs_commit", call => \&rcs_commit); @@ -52,17 +56,6 @@ sub import { hook(type => "rcs", id => "rcs_getmtime", call => \&rcs_getmtime); } -sub genwrapper () { - return <&STDOUT"); - open(STDOUT, ">", "/dev/null"); - my $ret = system(@cmd); - open(STDOUT, ">&", $savedout); - - return ($ret == 0) ? 1 : 0; -} -sub cvs_is_controlling { - my $dir=shift; - $dir=$config{srcdir} unless defined($dir); - return (-d "$dir/CVS") ? 1 : 0; -} +# VCS PLUGIN API CALLS sub rcs_update () { return unless cvs_is_controlling; @@ -183,24 +159,6 @@ sub rcs_prepedit ($) { return defined $rev ? $rev : ""; } -sub commitmessage (@) { - my %params=@_; - - if (defined $params{session}) { - if (defined $params{session}->param("name")) { - return "web commit by ". - $params{session}->param("name"). - (length $params{message} ? ": $params{message}" : ""); - } - elsif (defined $params{session}->remote_addr()) { - return "web commit from ". - $params{session}->remote_addr(). - (length $params{message} ? ": $params{message}" : ""); - } - } - return $params{message}; -} - sub rcs_commit (@) { # Tries to commit the page; returns undef on _success_ and # a version of the page with the rcs's conflict markers on failure. @@ -493,4 +451,56 @@ sub rcs_getmtime ($) { error "rcs_getmtime is not implemented for cvs\n"; # TODO } + +# INTERNAL SUPPORT ROUTINES + +sub commitmessage (@) { + my %params=@_; + + if (defined $params{session}) { + if (defined $params{session}->param("name")) { + return "web commit by ". + $params{session}->param("name"). + (length $params{message} ? ": $params{message}" : ""); + } + elsif (defined $params{session}->remote_addr()) { + return "web commit from ". + $params{session}->remote_addr(). + (length $params{message} ? ": $params{message}" : ""); + } + } + return $params{message}; +} + +sub cvs_info ($$) { + my $field=shift; + my $file=shift; + + local $CWD = $config{srcdir}; + + my $info=`cvs status $file`; + my ($ret)=$info=~/^\s*$field:\s*(\S+)/m; + return $ret; +} + +sub cvs_is_controlling { + my $dir=shift; + $dir=$config{srcdir} unless defined($dir); + return (-d "$dir/CVS") ? 1 : 0; +} + +sub cvs_runcvs(@) { + my @cmd = @_; + unshift @cmd, 'cvs', '-Q'; + + local $CWD = $config{srcdir}; + + open(my $savedout, ">&STDOUT"); + open(STDOUT, ">", "/dev/null"); + my $ret = system(@cmd); + open(STDOUT, ">&", $savedout); + + return ($ret == 0) ? 1 : 0; +} + 1 diff --git a/TODO.cvs b/TODO.cvs index 13dc99822..256268558 100644 --- a/TODO.cvs +++ b/TODO.cvs @@ -1,4 +1,8 @@ -* make old tests run in any order * merge old tests into new test subs +* remove code comments when tests are explanatory + +* do a writeup for doc/rcs/details.mdwn +* remove outdated stuff from doc/rcs/cvs.mdwn + * more copyright years? diff --git a/doc/rcs/cvs.mdwn b/doc/rcs/cvs.mdwn index 9beb08ece..a0ee5ab60 100644 --- a/doc/rcs/cvs.mdwn +++ b/doc/rcs/cvs.mdwn @@ -1,13 +1,17 @@ -If you really need to, you can use [[!wikipedia desc="CVS" Concurrent Versions System]] -with ikiwiki. +[[!template id=gitbranch branch=schmonz/cvs author="[[schmonz]]"]] + +If you really need to, you can use [[!wikipedia desc="CVS" Concurrent +Versions System]] with ikiwiki. ### Usage 7. Install [[!cpan File::chdir]], [[!cpan File::ReadBackwards]], -[cvsps](http://www.cobite.com/cvsps/), and -[cvsweb](http://www.freebsd.org/projects/cvsweb.html) or the like. + [cvsps](http://www.cobite.com/cvsps/), and + [cvsweb](http://www.freebsd.org/projects/cvsweb.html) or the like. 7. Adjust CVS-related parameters in your setup file. -Consider creating `$HOME/.cvsrc` if you don't have one already; the plugin doesn't need it, but you yourself might. Here's a good general-purpose one: +Consider creating `$HOME/.cvsrc` if you don't have one already; the +plugin doesn't need it, but you yourself might. Here's a good +general-purpose one: cvs -q checkout -P @@ -17,12 +21,25 @@ Consider creating `$HOME/.cvsrc` if you don't have one already; the plugin doesn ### Implementation details * [[ikiwiki-makerepo]]: - * creates a repository, - * imports `$SRCDIR` into top-level module `ikiwiki` (vendor tag IKIWIKI, release tag PRE_CVS), - * configures the post-commit hook in `CVSROOT/loginfo`. + * creates a repository, + * imports `$SRCDIR` into top-level module `ikiwiki` (vendor tag + IKIWIKI, release tag PRE_CVS), + * configures the post-commit hook in `CVSROOT/loginfo`. ### To do -* Have `ikiwiki-makerepo` set up NetBSD-like `log_accum` and `commit_prep` scripts that coalesce commits into changesets. Reasons: - 7. Obviates the need to scrape the repo's complete history to determine the last N changesets. (Repositories without such records can fall back on the `cvsps` and `File::ReadBackwards` code.) - 7. Arranges for ikiwiki to be run once per changeset, rather than CVS's once per committed file (!), which is a waste at best and bug-inducing at worst. (Currently, on multi-directory commits, only the first directory's changes get mentioned in [[recentchanges|plugins/recentchanges]].) -* Perhaps prevent web edits from attempting to create `.../CVS/foo.mdwn` (and `.../cvs/foo.mdwn` on case-insensitive filesystems); thanks to the CVS metadata directory, the attempt will fail anyway (and much more confusingly) if we don't. +* Expand test coverage and fix bugs. +* Have `ikiwiki-makerepo` set up NetBSD-like `log_accum` and + `commit_prep` scripts that coalesce commits into changesets. Reasons: + 7. Obviates the need to scrape the repo's complete history to + determine the last N changesets. (Repositories without such + records can fall back on the `cvsps` and `File::ReadBackwards` + code.) + 7. Arranges for ikiwiki to be run once per changeset, rather + than CVS's once per committed file (!), which is a waste at + best and bug-inducing at worst. (Currently, on multi-directory + commits, only the first directory's changes get mentioned + in [[recentchanges|plugins/recentchanges]].) +* Perhaps prevent web edits from attempting to create `.../CVS/foo.mdwn` + (and `.../cvs/foo.mdwn` on case-insensitive filesystems); thanks + to the CVS metadata directory, the attempt will fail anyway (and + much more confusingly) if we don't. diff --git a/t/cvs.t b/t/cvs.t index a8f1254ef..390e9af47 100755 --- a/t/cvs.t +++ b/t/cvs.t @@ -1,72 +1,16 @@ #!/usr/bin/perl use warnings; use strict; -use Test::More; my $total_tests = 10; +use Test::More; my $total_tests = 9; use IkiWiki; +my $default_test_methods = '^test_*'; my $dir = "/tmp/ikiwiki-test-cvs.$$"; -sub _plan { - my $can_plan = shift; - - my $cvs = `which cvs`; chomp $cvs; - my $cvsps = `which cvsps`; chomp $cvsps; - return plan(skip_all => 'cvs or cvsps not available') - unless -x $cvs && -x $cvsps; - - foreach my $module (qw(File::ReadBackwards File::MimeInfo)) { - eval qq{use $module}; - if ($@) { - return plan(skip_all => "$module not available"); - } - } - - return plan(skip_all => "can't create $dir: $!") - unless mkdir($dir); - - return unless $can_plan; - - return plan(tests => $total_tests); -} - -sub _startup { - my $can_plan = shift; - - _plan($can_plan); - _generate_minimal_config(); - _create_test_repo(); -} - -sub _shutdown { - my $had_plan = shift; - - system "rm -rf $dir"; - done_testing() unless $had_plan; -} - -sub _generate_minimal_config { - %config = IkiWiki::defaultconfig(); - $config{rcs} = "cvs"; - $config{srcdir} = "$dir/src"; - $config{cvsrepo} = "$dir/repo"; - $config{cvspath} = "ikiwiki"; - IkiWiki::loadplugins(); - IkiWiki::checkconfig(); -} - -sub _create_test_repo { - my $cvs = "cvs -d $config{cvsrepo}"; - my $dn = ">/dev/null"; - system "$cvs init $dn"; - system "mkdir $dir/$config{cvspath} $dn"; - system "cd $dir/$config{cvspath} && " - . "$cvs import -m import $config{cvspath} VENDOR RELEASE $dn"; - system "rm -rf $dir/$config{cvspath} $dn"; - system "$cvs co -d $config{srcdir} $config{cvspath} $dn"; -} +# TESTS FOR GENERAL META-BEHAVIOR sub test_web_add_and_commit { - my $message = "Added the first page"; + my $message = "Add a page via VCS API"; writefile('test1.mdwn', $config{srcdir}, readfile("t/test1.mdwn")); IkiWiki::rcs_add("test1.mdwn"); IkiWiki::rcs_commit( @@ -106,7 +50,7 @@ sub test_web_add_and_commit { } sub test_manual_add_and_commit { - my $message = "Added the second page"; + my $message = "Add a page via CVS directly"; writefile('test2.mdwn', $config{srcdir}, readfile("t/test2.mdwn")); system "cd $config{srcdir}" . " && cvs add test2.mdwn >/dev/null 2>&1"; @@ -116,8 +60,8 @@ sub test_manual_add_and_commit { my @changes = IkiWiki::rcs_recentchanges(3); is( $#changes, - 1, - q{2 total commits}, + 0, + q{1 total commit}, ); is( $changes[0]{message}[0]{"line"}, @@ -129,11 +73,6 @@ sub test_manual_add_and_commit { "test2", q{first pagename from most recent commit matches}, ); - is( - $changes[1]{pages}[0]{"page"}, - "test1", - q{first pagename from second-most-recent commit matches}, - ); # CVS commits run ikiwiki once for every committed file (!) # - commit_prep alone should fix this @@ -149,32 +88,6 @@ sub test_chdir_magic { # when are we bothering with "local $CWD" and when aren't we? } -sub test_genwrapper { - # testable directly? affects rcs_add, but are we exercising this? -} - -sub test_checkconfig { - # undef cvspath, expect "ikiwiki" - # define cvspath normally, get it back - # define cvspath in a subdir, get it back? - # define cvspath with extra slashes, get sanitized version back - # - yoink test_extra_path_slashes - # undef cvs_wrapper, expect $config{wrappers} same size as before - - my $initial_cvspath = $config{cvspath}; - $config{cvspath} = "/ikiwiki//"; - IkiWiki::checkconfig(); - is( - $config{cvspath}, - $initial_cvspath, - q{rcs_recentchanges assumes checkconfig has sanitized cvspath}, - ); -} - -sub test_getsetup { - # anything worth testing? -} - sub test_cvs_info { # inspect "Repository revision" (used in code) # inspect "Sticky Options" (used in tests to verify existence of "-kb") @@ -228,6 +141,38 @@ sub test_cvs_is_controlling { # - else, die } + +# TESTS FOR GENERAL PLUGIN API CALLS + +sub test_checkconfig { + # undef cvspath, expect "ikiwiki" + # define cvspath normally, get it back + # define cvspath in a subdir, get it back? + # define cvspath with extra slashes, get sanitized version back + # - yoink test_extra_path_slashes + # undef cvs_wrapper, expect $config{wrappers} same size as before + + my $initial_cvspath = $config{cvspath}; + $config{cvspath} = "/ikiwiki//"; + IkiWiki::checkconfig(); + is( + $config{cvspath}, + $initial_cvspath, + q{rcs_recentchanges assumes checkconfig has sanitized cvspath}, + ); +} + +sub test_getsetup { + # anything worth testing? +} + +sub test_genwrapper { + # testable directly? affects rcs_add, but are we exercising this? +} + + +# TESTS FOR VCS PLUGIN API CALLS + sub test_rcs_update { # can it assume we're under CVS control? or must it check? # anything else worth testing? @@ -374,25 +319,53 @@ sub test_rcs_revert { } sub main { - my $default_test_methods = '^test_*'; my $test_methods = defined $ENV{TEST_METHOD} ? $ENV{TEST_METHOD} : $default_test_methods; _startup($test_methods eq $default_test_methods); - $_->() foreach get_test_subs($test_methods); + _runtests(_get_matching_test_subs($test_methods)); _shutdown($test_methods eq $default_test_methods); } main(); -################################################################################ -# don't want a dependency on Test::Class; do want a couple of its features -sub get_test_subs { - my $re = shift; - no strict 'refs'; - return map { \&{*$_} } grep { /$re/ } list_module('main'); +# INTERNAL SUPPORT ROUTINES + +sub _plan_for_test_more { + my $can_plan = shift; + + foreach my $program (qw( + cvs + cvsps + )) { + my $program_path = `which $program`; + chomp $program_path; + return plan(skip_all => "$program not available") + unless -x $program_path; + } + + foreach my $module (qw( + File::chdir + File::MimeInfo + Date::Parse + File::Temp + File::ReadBackwards + )) { + eval qq{use $module}; + return plan(skip_all => "$module not available") + if $@; + } + + return plan(skip_all => "can't create $dir: $!") + unless mkdir($dir); + return plan(skip_all => "can't remove $dir: $!") + unless rmdir($dir); + + return unless $can_plan; + + return plan(tests => $total_tests); } # http://stackoverflow.com/questions/607282/whats-the-best-way-to-discover-all-subroutines-a-perl-module-has @@ -413,3 +386,64 @@ sub list_module { defined &{"$module\::$_"} and in_package(\&{*$_}, $module) } keys %{"$module\::"}; } + + +# support for xUnit-style testing, a la Test::Class + +sub _startup { + my $can_plan = shift; + _plan_for_test_more($can_plan); + _generate_test_config(); +} + +sub _shutdown { + my $had_plan = shift; + done_testing() unless $had_plan; +} + +sub _setup { + _generate_test_repo(); +} + +sub _teardown { + system "rm -rf $dir"; +} + +sub _runtests { + my @coderefs = (@_); + for (@coderefs) { + _setup(); + $_->(); + _teardown(); + } +} + +sub _get_matching_test_subs { + my $re = shift; + no strict 'refs'; + return map { \&{*$_} } grep { /$re/ } sort(list_module('main')); +} + +sub _generate_test_config { + %config = IkiWiki::defaultconfig(); + $config{rcs} = "cvs"; + $config{srcdir} = "$dir/src"; + $config{cvsrepo} = "$dir/repo"; + $config{cvspath} = "ikiwiki"; + IkiWiki::loadplugins(); + IkiWiki::checkconfig(); +} + +sub _generate_test_repo { + die "can't create $dir: $!" + unless mkdir($dir); + + my $cvs = "cvs -d $config{cvsrepo}"; + my $dn = ">/dev/null"; + system "$cvs init $dn"; + system "mkdir $dir/$config{cvspath} $dn"; + system "cd $dir/$config{cvspath} && " + . "$cvs import -m import $config{cvspath} VENDOR RELEASE $dn"; + system "rm -rf $dir/$config{cvspath} $dn"; + system "$cvs co -d $config{srcdir} $config{cvspath} $dn"; +}