├── .ackrc ├── .github └── workflows │ └── testsuite.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Changes ├── DESIGN.md ├── DEVELOPERS.md ├── LICENSE.md ├── MANIFEST ├── Makefile.PL ├── README.md ├── RELEASE-NOTES.md ├── ack ├── cpanfile ├── dev ├── Cookbook.pm ├── IGNORE.md ├── RELEASE-INSTRUCTIONS.md ├── bench.pl ├── crank-mutex ├── display-option-coverage.pl ├── docker │ ├── Dockerfile │ ├── docker-compose.yml │ └── docker-entrypoint.sh ├── find-minimum-dep-versions.pl ├── generate-completion-scripts.pl ├── generate-rgb-codes.pl ├── issues-scrub.pl ├── linecount ├── linecount-fork ├── multi-process.md ├── questions.md ├── record-options ├── timings.pl └── walk ├── garage ├── ack-1.96 ├── ack-2.22 ├── ack-2.24 ├── ack-v3.0.0 ├── ack-v3.0.1 ├── ack-v3.0.2 ├── ack-v3.0.3 ├── ack-v3.1.0 ├── ack-v3.1.1 ├── ack-v3.1.2 ├── ack-v3.1.3 ├── ack-v3.2.0 ├── ack-v3.3.0 ├── ack-v3.3.1 ├── ack-v3.4.0 ├── ack-v3.5.0 ├── ack-v3.6.0 ├── ack-v3.7.0 ├── ack-v3.8.0 ├── ack-v3.8.1 ├── ack-v3.8.2 └── ack-v3.9.0 ├── lib └── App │ ├── Ack.pm │ └── Ack │ ├── ConfigDefault.pm │ ├── ConfigFinder.pm │ ├── ConfigLoader.pm │ ├── File.pm │ ├── Files.pm │ ├── Filter.pm │ └── Filter │ ├── Collection.pm │ ├── Default.pm │ ├── Extension.pm │ ├── ExtensionGroup.pm │ ├── FirstLineMatch.pm │ ├── Inverse.pm │ ├── Is.pm │ ├── IsGroup.pm │ ├── IsPath.pm │ ├── IsPathGroup.pm │ ├── Match.pm │ └── MatchGroup.pm ├── perlcriticrc ├── perltidyrc ├── squash ├── stack ├── t ├── 00-load.t ├── FilterTest.pm ├── Util.pm ├── ack-1.yaml ├── ack-Q.t ├── ack-Q.yaml ├── ack-c.yaml ├── ack-color.t ├── ack-column.yaml ├── ack-create-ackrc.t ├── ack-dump.t ├── ack-f.yaml ├── ack-files-from.t ├── ack-g.t ├── ack-g.yaml ├── ack-group.t ├── ack-h.yaml ├── ack-help-types.t ├── ack-help.t ├── ack-i.yaml ├── ack-ignore-dir.t ├── ack-ignore-file.t ├── ack-k.yaml ├── ack-l.yaml ├── ack-m.yaml ├── ack-man.t ├── ack-match.t ├── ack-n.t ├── ack-o.yaml ├── ack-output-color.t ├── ack-output.t ├── ack-output.yaml ├── ack-pager.t ├── ack-passthru.t ├── ack-print0.t ├── ack-proximate.yaml ├── ack-s.t ├── ack-s.yaml ├── ack-show-types.t ├── ack-type-del.t ├── ack-type.t ├── ack-underline.t ├── ack-v.yaml ├── ack-version.t ├── ack-w.t ├── ack-w.yaml ├── ack-x.t ├── anchored.yaml ├── bad-ackrc-opt.t ├── basic.yaml ├── boolean.t ├── boolean.yaml ├── build_regex.t ├── command-line-files.t ├── config-backwards-compat.t ├── config-finder.t ├── config-loader.t ├── context-with-newlines.t ├── context.t ├── context.yaml ├── default-filter.t ├── double-hyphen.yaml ├── empty-lines.t ├── etc │ ├── buttonhook.xml.xxx │ ├── shebang.empty.xxx │ ├── shebang.foobar.xxx │ ├── shebang.php.xxx │ ├── shebang.pl.xxx │ ├── shebang.py.xxx │ ├── shebang.rb.xxx │ └── shebang.sh.xxx ├── exit-code.t ├── ext-filter.t ├── file-filter-inverted.yaml ├── file-iterator.t ├── file-permission.t ├── filetype-detection.yaml ├── filetypes.t ├── filter.t ├── firstlinematch-filter.t ├── forbidden-options.t ├── from-stdin.t ├── highlighting.t ├── home │ └── .ackrc ├── illegal-regex.t ├── incomplete-last-line.t ├── interactive.t ├── invalid-ackrc.t ├── invalid-options.t ├── is-filter.t ├── line-endings.t ├── longopts.t ├── lowercase.t ├── match-filter.t ├── multiple-captures.t ├── mutex-options.t ├── mutex.yaml ├── named-pipes.t ├── needs-line-scan.t ├── noackrc.t ├── noenv.t ├── prescan-line-boundaries.t ├── process-substitution.t ├── range.t ├── range │ ├── america-the-beautiful.html │ ├── anchors-aweigh.html │ ├── johnny-rebeck.txt │ ├── rangefile.pm │ └── stars-and-stripes-forever.html ├── run_yaml_test.py ├── runtests.pl ├── swamp │ ├── 0 │ ├── #emacs-workfile.pl# │ ├── CMakeLists.txt │ ├── Makefile │ ├── Makefile.PL │ ├── MasterPage.master │ ├── Rakefile │ ├── Sample.ascx │ ├── Sample.asmx │ ├── blib │ │ ├── ignore.pm │ │ └── ignore.pod │ ├── c-header.h │ ├── c-source.c │ ├── constitution-100k.pl │ ├── crystallography-weenies.f │ ├── example.R │ ├── favicon.ico │ ├── file.bar │ ├── file.foo │ ├── foo_test.py │ ├── fresh.css │ ├── fresh.css.min │ ├── fresh.min.css │ ├── groceries │ │ ├── CVS │ │ │ ├── fruit │ │ │ ├── junk │ │ │ └── meat │ │ ├── RCS │ │ │ ├── fruit │ │ │ ├── junk │ │ │ └── meat │ │ ├── another_subdir │ │ │ ├── CVS │ │ │ │ ├── fruit │ │ │ │ ├── junk │ │ │ │ └── meat │ │ │ ├── RCS │ │ │ │ ├── fruit │ │ │ │ ├── junk │ │ │ │ └── meat │ │ │ ├── fruit │ │ │ ├── junk │ │ │ └── meat │ │ ├── dir.d │ │ │ ├── CVS │ │ │ │ ├── fruit │ │ │ │ ├── junk │ │ │ │ └── meat │ │ │ ├── RCS │ │ │ │ ├── fruit │ │ │ │ ├── junk │ │ │ │ └── meat │ │ │ ├── fruit │ │ │ ├── junk │ │ │ └── meat │ │ ├── fruit │ │ ├── junk │ │ ├── meat │ │ └── subdir │ │ │ ├── fruit │ │ │ ├── junk │ │ │ └── meat │ ├── html.htm │ ├── html.html │ ├── incomplete-last-line.txt │ ├── javascript.js │ ├── lua-shebang-test │ ├── minified.js.min │ ├── minified.min.js │ ├── moose-andy.jpg │ ├── not-an-#emacs-workfile# │ ├── notaMakefile │ ├── notaRakefile │ ├── notes.md │ ├── options-crlf.pl │ ├── options.pl │ ├── options.pl.bak │ ├── perl-test.t │ ├── perl-without-extension │ ├── perl.cgi │ ├── perl.handler.pod │ ├── perl.pl │ ├── perl.pm │ ├── perl.pod │ ├── perl.tar.gz │ ├── perltoot.jpg │ ├── pipe-stress-freaks.F │ ├── sample.asp │ ├── sample.aspx │ ├── sample.rake │ ├── service.svc │ ├── solution8.tar │ ├── stuff.cmake │ ├── swamp │ │ └── ignoreme.txt │ ├── test.py │ └── test_foo.py ├── test-pager ├── text │ ├── amontillado.txt │ ├── bill-of-rights.txt │ ├── constitution.txt │ ├── gettysburg.txt │ ├── movies.txt │ ├── number.txt │ ├── numbered-text.txt │ ├── ozymandias.txt │ └── raven.txt ├── trailing-whitespace.t ├── yaml.t └── zero.yaml ├── tack ├── tags └── xt ├── coding-standards.t ├── coresubs.t ├── man.t └── pod.t /.ackrc: -------------------------------------------------------------------------------- 1 | --ignore-dir=garage/ 2 | --ignore-file=is:ack-standalone 3 | --ignore-file=is:tags 4 | -------------------------------------------------------------------------------- /.github/workflows/testsuite.yml: -------------------------------------------------------------------------------- 1 | name: testsuite 2 | 3 | on: 4 | push: 5 | branches: 6 | - "*" 7 | tags-ignore: 8 | - "*" 9 | pull_request: 10 | 11 | jobs: 12 | # Builds a list of Perl versions to work with. 13 | perl-versions: 14 | runs-on: ubuntu-latest 15 | name: List Perl versions 16 | outputs: 17 | perl-versions: ${{ steps.action.outputs.perl-versions }} 18 | steps: 19 | - id: action 20 | uses: perl-actions/perl-versions@v1 21 | with: 22 | since-perl: v5.10 23 | with-devel: true 24 | 25 | linux: 26 | runs-on: ubuntu-latest 27 | name: "Perl ${{ matrix.perl-version }}" 28 | needs: [perl-versions] 29 | 30 | env: 31 | PERL_USE_UNSAFE_INC: 0 32 | AUTHOR_TESTING: 1 33 | AUTOMATED_TESTING: 1 34 | RELEASE_TESTING: 1 35 | PERL_CARTON_PATH: $GITHUB_WORKSPACE/local 36 | 37 | strategy: 38 | fail-fast: false 39 | matrix: 40 | perl-version: ${{ fromJson (needs.perl-versions.outputs.perl-versions) }} 41 | 42 | container: perldocker/perl-tester:${{ matrix.perl-version }} 43 | 44 | steps: 45 | - uses: actions/checkout@v4 46 | - run: perl -V 47 | - name: Install dependencies 48 | uses: perl-actions/install-with-cpanm@v1 49 | with: 50 | cpanfile: "cpanfile" 51 | sudo: false 52 | - run: perl -MFile::Next -e1 53 | - run: perl Makefile.PL 54 | - run: make 55 | - run: make test 56 | 57 | macOS: 58 | needs: [perl-versions] 59 | runs-on: macOS-latest 60 | 61 | env: 62 | PERL_USE_UNSAFE_INC: 0 63 | AUTHOR_TESTING: 1 64 | AUTOMATED_TESTING: 1 65 | RELEASE_TESTING: 1 66 | PERL_CARTON_PATH: $GITHUB_WORKSPACE/local 67 | 68 | steps: 69 | - uses: actions/checkout@v4 70 | - run: brew install perl 71 | - run: perl -V 72 | - name: Install dependencies 73 | uses: perl-actions/install-with-cpanm@v1 74 | with: 75 | cpanfile: "cpanfile" 76 | sudo: false 77 | - run: perl -MFile::Next -e1 78 | - run: perl Makefile.PL 79 | - run: make 80 | - run: make test 81 | 82 | windows: 83 | runs-on: windows-latest 84 | name: "windows" 85 | needs: [perl-versions] 86 | env: 87 | PERL_USE_UNSAFE_INC: 0 88 | AUTHOR_TESTING: 1 89 | AUTOMATED_TESTING: 1 90 | RELEASE_TESTING: 1 91 | PERL_CARTON_PATH: $GITHUB_WORKSPACE/local 92 | 93 | steps: 94 | - uses: actions/checkout@v4 95 | - run: perl -V 96 | - name: Install dependencies 97 | uses: perl-actions/install-with-cpanm@v1 98 | with: 99 | cpanfile: "cpanfile" 100 | sudo: false 101 | - run: perl -MFile::Next -e1 102 | - run: perl Makefile.PL 103 | - run: make 104 | - run: make test 105 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /blib/ 2 | /.build/ 3 | _build/ 4 | cover_db/ 5 | inc/ 6 | Build 7 | !Build/ 8 | Build.bat 9 | .last_cover_stats 10 | /Makefile 11 | /Makefile.old 12 | /MANIFEST.bak 13 | /META.yml 14 | /META.json 15 | /MYMETA.* 16 | nytprof.out 17 | /pm_to_blib 18 | *.o 19 | *.bs 20 | /_eumm/ 21 | ack-standalone 22 | *~ 23 | __pycache__ 24 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute to ack 2 | 3 | ## Requesting new filetypes 4 | 5 | We get many requests for new filetypes. Not all filetypes will necessarily be added to ack. 6 | 7 | If you'd like to suggest a new filetype for ack, please create an issue that includes: 8 | 9 | * The name of the filetype 10 | * What the definition of the filetype would be, taken from the `.ackrc` file that you've been using. 11 | * URL of a web page that discusses what the filetype is 12 | * What the file contains, and why it should be a filetype 13 | * How widely used this filetype will be. 14 | 15 | Please do not create a pull request for it. 16 | -------------------------------------------------------------------------------- /DESIGN.md: -------------------------------------------------------------------------------- 1 | # ack3 design 2 | 3 | Here we try to document ack's design, including design decisions 4 | that have already been made, so that we don't keep rehashing them. 5 | 6 | # Features that have been suggested but will not happen. 7 | 8 | * ack will not do replacing of text. It is not a replacement for sed. 9 | 10 | * ack will not edit files or invoke editors. 11 | 12 | * ack will not sort files by default. 13 | 14 | Sorting the filenames requires reading in the entire directory of 15 | filenames before searching, and this can be a performance hit. In fact, 16 | it can kill ack entirely on directories with absurdly large numbers of 17 | files in them. 18 | 19 | # Design choices that are inviolate. 20 | 21 | * ack must run purely as Perl 5.10.1 using only core modules and File::Next. 22 | 23 | No other modules may be used. That includes Moose or any Moose variants. 24 | 25 | * We don't use the Perl smartmatch operator. 26 | 27 | * ack must be able to be distributed through CPAN, using the App::Ack:: module tree. 28 | 29 | * ack must be able to be built as a single-file standalone file. 30 | 31 | This file is intended to be able to be copied & pasted if necessary. 32 | It will not contain any characters out the range of `[ -~]`. 33 | See `xt/coding-standards.t`. 34 | 35 | * ack must be cross-platform. Specifically, it must run on Windows. 36 | 37 | * ack must not use any external tools. 38 | 39 | * The detection of whether a file is text or not is decided by Perl's `-T` operator. 40 | 41 | * Filetype detection is only through user-defined rules. ack will not 42 | shell out to `file` or any similar utilities. 43 | 44 | * ack must be able to be run under taint mode. 45 | 46 | * Use Perl's default file-handling as far as dealing with files of 47 | different encodings 48 | 49 | * ack must be able to be configured entirely from the command-line. 50 | ackrc files are merely collections of command-line switches. 51 | 52 | 53 | # Design questions that have been investigated 54 | 55 | * Would ack be faster if we used the integer pragma? 56 | 57 | It seems not. Brian M. Carlson investigated this and reported his 58 | findings here: https://github.com/petdance/ack2/issues/398 59 | 60 | There seemed to be no effect. 61 | 62 | * Why not make ack use /usr/bin/env in the shebang instead of /usr/bin/perl? 63 | 64 | The Perl toolchain takes care of the shebang at install time. 65 | 66 | # Guiding principles 67 | 68 | * When deciding on ack's behavior, try to be grep-compatible if possible. 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ack 3 2 | 3 | ack is a code-searching tool, similar to grep but optimized for 4 | programmers searching large trees of source code. It is highly 5 | portable and runs on any platform that runs Perl. 6 | 7 | ack is written and maintained by Andy Lester (andy@petdance.com). 8 | 9 | * [Project home page](https://beyondgrep.com/) 10 | * [Code home page](https://github.com/beyondgrep/ack3) 11 | * [Issue tracker](https://github.com/beyondgrep/ack3/issues) 12 | * Mailing lists 13 | * [ack-announce](https://groups.google.com/d/forum/ack-announce), announcements-only 14 | * [ack-users](https://groups.google.com/d/forum/ack-users), for users of ack 15 | * [ack-dev](https://groups.google.com/d/forum/ack-dev), for ack development 16 | * [Build status ![Build Status](https://github.com/beyondgrep/ack3/workflows/testsuite/badge.svg?branch=dev)](https://github.com/beyondgrep/ack3/actions?query=workflow%3Atestsuite+branch%3Adev) 17 | * [CPAN Testers](https://cpantesters.org/distro/A/ack.html) 18 | 19 | # Building 20 | 21 | ack requires Perl 5.10.1 or higher, and it requires the 22 | [File::Next](https://metacpan.org/pod/File::Next) module to be installed. 23 | 24 | ## Checking prerequisites 25 | 26 | To check ack's dependencies, run this command in the shell: 27 | 28 | perl -MFile::Next -E'say "ack is ready to build!"' 29 | 30 | If everything is OK, you'll see: 31 | 32 | ack is ready to build! 33 | 34 | If your installation of Perl is outdated, you'll see an error like this: 35 | 36 | Unrecognized switch: -Esay "ack is ready to build!" (-h will show valid options). 37 | 38 | If you don't have File::Next installed, you'll see an error like this: 39 | 40 | Can't locate File/Next.pm in @INC (@INC contains: /home/andy/... 41 | BEGIN failed--compilation aborted. 42 | 43 | and you'll need to install File::Next yourself: 44 | 45 | # Install File::Next dependency 46 | perl -MCPAN -e install File::Next 47 | 48 | ## Building ack 49 | 50 | If you've got a recent enough version of Perl and you have File::Next 51 | installed, you can build ack. 52 | 53 | # Required 54 | perl Makefile.PL 55 | make 56 | make test 57 | sudo make install # For a system-wide installation 58 | # - or - 59 | make ack-standalone 60 | cp ack-standalone ~/bin/ack3 # For a personal installation 61 | 62 | # Development 63 | 64 | * [How to contribute](CONTRIBUTING.md) 65 | * [Developer's Guide](DEVELOPERS.md) 66 | * [Design Guide](DESIGN.md) 67 | 68 | # Community 69 | 70 | See the [Community](https://beyondgrep.com/community/) page. 71 | 72 | Please follow the [Code of Conduct](CODE_OF_CONDUCT.md) in all your interactions with our project. 73 | 74 | # License 75 | 76 | Copyright 2005-2025 Andy Lester. 77 | 78 | This program is free software; you can redistribute it and/or modify 79 | it under the terms of the 80 | [Artistic License v2.0](https://www.perlfoundation.org/artistic_license_2_0). 81 | See also the LICENSE.md file that comes with the ack distribution. 82 | 83 | # Support 84 | 85 | [ack](https://github.com/beyondgrep/ack3) and [beyondgrep.com](https://beyondgrep.com) are supported by [DigitalOcean](https://m.do.co/c/6a437192f552). 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /RELEASE-NOTES.md: -------------------------------------------------------------------------------- 1 | # Release notes for ack 3.000 2 | 3 | # New features 4 | 5 | ack 3 is a greplike tool optimized for searching large code trees. 6 | 7 | Improvements over ack 2 include: 8 | 9 | * Improved `-w` means more accurate searching for words. 10 | 11 | * `-w` option will warn if your pattern does not lend itself to 12 | word matching. 13 | 14 | * `-i`, `-I` and `--smart-case` 15 | 16 | * `--proximate=N` option 17 | 18 | * `--underline` option for showing matches without color coding. 19 | 20 | * Added `--pod` and `--markdown`. 21 | 22 | * Added `GNUmakefile` to the list of makefile specs. 23 | 24 | * Added `-S` as a synonym for `--smart-case`. 25 | 26 | * Added many more checks for mutually exclusive command-line options. 27 | 28 | * Added a filetype for SVG files. 29 | 30 | # Bug fixes 31 | 32 | * Column numbers were not getting colorized in the output. Added 33 | `--color-colno` option and `ACK_COLOR_COLNO` environment variable. 34 | 35 | * A pattern that wanted whitespace at the end could match the 36 | linefeed at the end of a line. This is no longer possible. 37 | 38 | # Incompatibilities with ack 2 39 | 40 | ## ack 3 requires Perl 5.10.1 41 | 42 | ack 2 only needed Perl 5.8.8. This shouldn't be a problem since 5.10.1 43 | has been out since 2009. 44 | 45 | ## -w is fussier 46 | 47 | ack 3 will not allow you to use `-w` with a pattern that doesn't begin or 48 | end with a word character. 49 | 50 | ## ack 3 no longer highlights capture groups. 51 | 52 | ack 2 would highlight your capture groups. For example, 53 | 54 | ack '(set|get)_foo_(name|id)' 55 | 56 | would highlight the `set` or `get`, and the `name` or `id`, but not the 57 | full `set_user_id` that was matched. 58 | 59 | This feature was too confusing and has been removed. Now, the entire 60 | matching string is highlighted. 61 | 62 | ## ack 3's --output allows fewer special variables 63 | 64 | In ack 2, you could put any kind of Perl code in the `--output` 65 | option and it would get `eval`uated at run time, which would let 66 | you do tricky stuff like this gem from Mark Fowler 67 | (http://www.perladvent.org/2014/2014-12-21.html): 68 | 69 | ack --output='$&: @{[ eval "use LWP::Simple; 1" && length LWP::Simple::get($&) ]} bytes' \ 70 | 'https?://\S+' list.txt 71 | http://google.com/: 19529 bytes 72 | http://metacpan.org/: 7560 bytes 73 | http://www.perladvent.org/: 5562 bytes 74 | 75 | This has been a security problem in the past, and so in ack 3 we 76 | no longer `eval` the contents of `--output`. You're now restricted 77 | to the following variables: `$1` thru `$9`, `$_`, `$.`, `$&`, ``$` ``, 78 | `$'` and `$+`. You can also embed `\t`, `\n` and `\r` , 79 | and `$f` as stand-in for `$filename` in `ack2 --output` . 80 | 81 | ## ack 3 no longer uses the `ACK_OPTIONS` environment variable 82 | 83 | The `ACK_OPTIONS` variable was used to supply command line arguments to 84 | ack invocations. This has been removed. Use an ackrc file instead. 85 | 86 | ## ack 3 no longer has the `--lines` option 87 | 88 | The `--lines` option let you select a specific range of lines to print, and 89 | not match. There are other better ways to do this (`sed`, for example). 90 | 91 | ## The `--man` option no longer uses perldoc 92 | 93 | The `--man` option would use the external `perldoc` program for displaying 94 | documentation. However, this was causing portability problems since some 95 | users might not have it installed. The downside is that the text rendering 96 | is not quite as pretty. 97 | -------------------------------------------------------------------------------- /cpanfile: -------------------------------------------------------------------------------- 1 | # Validate with cpanfile-dump 2 | # https://metacpan.org/release/Module-CPANfile 3 | # https://metacpan.org/pod/distribution/Module-CPANfile/lib/cpanfile.pod 4 | 5 | requires 'Cwd' => '3.00'; 6 | requires 'File::Basename' => '1.00015'; 7 | requires 'File::Next' => '1.18'; 8 | requires 'File::Spec' => '3.00'; 9 | requires 'Getopt::Long' => '2.38'; 10 | requires 'if' => 0; 11 | requires 'List::Util' => 0; 12 | requires 'parent' => 0; 13 | requires 'Pod::Perldoc' => '3.20'; # Starting with 3.20, default output is Pod::Perldoc::ToTerm instead of ::ToMan 14 | requires 'Pod::Text' => 0; # Used to render pod by Pod::Usage. 15 | requires 'Pod::Usage' => '1.26'; 16 | requires 'Term::ANSIColor' => '1.10'; 17 | requires 'Text::ParseWords' => '3.1'; 18 | requires 'version' => 0; 19 | 20 | if ( $^O eq 'MSWin32' ) { 21 | requires 'Win32::ShellQuote' => '0.002001'; 22 | } 23 | 24 | on 'test' => sub { 25 | requires 'File::Temp' => '0.19', # For newdir() 26 | requires 'Scalar::Util' => 0; 27 | requires 'Test::Harness' => '2.50'; # Something reasonably newish 28 | requires 'Test::More' => '0.98'; # For subtest() 29 | requires 'YAML::PP' => 0; 30 | 31 | if ( $^O ne 'MSWin32' ) { 32 | requires 'IO::Pty' => 0; 33 | } 34 | }; 35 | 36 | # vi:et:sw=4 ts=4 ft=perl 37 | -------------------------------------------------------------------------------- /dev/IGNORE.md: -------------------------------------------------------------------------------- 1 | # Create --ignore-path 2 | 3 | Users have been dissatisfied with `--ignore-dir` and `--ignore-file`. Here are some common "bug reports" 4 | 5 | - `--ignore-dir=foo/bar` doesn't ignore `foo/bar` at all (GH ack2#291) 6 | - `--ignore-dir=foo` ignores all directories named `foo`, not just the one at the root of the search (GH ack2#216) 7 | - `--ignore-dir=foo foo -f` doesn't search `foo/` (GH ack2#492) 8 | - No way to ignore a file in a folder (GH ack2#479) 9 | - `--ignore-dir` doesn't implement all filters (GH ack2#42) 10 | - What's the right behavior for `--ignore-dir=foo`, `--noignore-dir=bar`, `--ignore-dir=baz`, wrt. `foo/bar/baz/file.txt` 11 | 12 | Also consider GH ack2#330, and consider that you could be anywhere in a 13 | project but still source the `--ignore-dir=./foo` rule from an ackrc a 14 | few directories above. Are we preparing to teach ack about the notion 15 | of a project root? Also, consider `--noignore-dir` 16 | 17 | # Questions to consider 18 | 19 | * `--ignore-path` will ignore paths as well as directories. But then 20 | relative to what? Current directory? That means an --ignore-path` 21 | in an .ackrc isn't useful. 22 | 23 | * Do we make the relativeness based on where the directive is? Is an 24 | `--ignore-path` in a project-level .ackrc relative to that file? And an 25 | `--ignore-path` from the command-line is relative to Cwd? 26 | 27 | * If I have `--ignore-dir=test` in my .ackrc, and I do `ack $term test`, 28 | what happens? 29 | -------------------------------------------------------------------------------- /dev/RELEASE-INSTRUCTIONS.md: -------------------------------------------------------------------------------- 1 | # How to make a release of ack 2 | 3 | ## Update all the code 4 | 5 | * Update the version number in `ack` and `lib/App/Ack.pm` 6 | 7 | * Update copyright year if necessary. 8 | 9 | * Make sure `Changes.pod` is up-to-date and has a version number in the top. 10 | 11 | * Make a clean build and test the distro: 12 | 13 | perl Makefile.PL 14 | make disttest 15 | 16 | * Push to GitHub to force a check of the branch. 17 | * Check the status at https://github.com/beyondgrep/ack3/actions 18 | 19 | ## Make a release 20 | 21 | * Merge to main and build 22 | 23 | ``` 24 | git co main 25 | git merge dev 26 | perl Makefile.PL 27 | make tardist 28 | ``` 29 | 30 | * Upload the tarball to PAUSE 31 | 32 | * Copy to garage 33 | 34 | ``` 35 | make ack-standalone 36 | mv ack-standalone garage/ack-v3.x.y 37 | git add garage/ack-v3.x.y 38 | git commit -m'Added 3.x.y to the garage' 39 | ``` 40 | 41 | ## Update the website 42 | 43 | This is all in the beyondgrep/website project. 44 | 45 | * Copy the ack-standalone and changelog 46 | 47 | ``` 48 | cd ~/website 49 | cp ~/ack3/ack-standalone static/ack-v3.x.y 50 | cp ~/ack3/Changes static/changes.txt 51 | ``` 52 | 53 | * Update version in `crank` 54 | 55 | * Update version and date in `tt/vars.tt`. 56 | 57 | * Regenerate the help text 58 | 59 | ``` 60 | perl static/ack-v3.x.y > tt/ack-help.txt 61 | ``` 62 | 63 | * Regenerate the help types 64 | 65 | ``` 66 | perl static/ack-v3.x.y --help-types > tt/ack-help-types.txt 67 | ``` 68 | 69 | * Regenerate the HTML help page 70 | 71 | ``` 72 | pod2html static/ack-v3.x.y > static/documentation/ack-v3.x.y-man.html 73 | ``` 74 | 75 | * ack for the old version number just to be sure there aren't others. 76 | 77 | * Make and install 78 | 79 | ``` 80 | make 81 | make test 82 | make rsync 83 | ``` 84 | 85 | * Commit all changes. Some files will be updated, and some will be new. 86 | 87 | 88 | ## GitHub cleanup 89 | 90 | * Close the milestone in GitHub 91 | 92 | * Create a new milestone in GitHub for the next version 93 | -------------------------------------------------------------------------------- /dev/bench.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | use 5.010; 6 | 7 | use Benchmark qw( timethese ); 8 | 9 | my $x = 'this is a line of text and it is about this long I guess'; 10 | 11 | my $n = 5_000_000; 12 | timethese( $n, 13 | { 14 | none => sub { $x =~ /\w+date/ }, 15 | w => sub { $x =~ /\b\w+date\b/ }, 16 | wi => sub { $x =~ /\b\w+date\b/i }, 17 | i => sub { $x =~ /\w+date/i }, 18 | } 19 | ); 20 | -------------------------------------------------------------------------------- /dev/crank-mutex: -------------------------------------------------------------------------------- 1 | #!/bin/env perl 2 | 3 | use 5.010; 4 | use strict; 5 | use warnings; 6 | 7 | use Data::Dumper 2.160; # For the TrailingComma option. 8 | 9 | 10 | sub invalid_combinations { 11 | my @context = qw( -A -B -C -p ); 12 | my @pretty = qw( --heading --group --break ); 13 | my @filename = qw( -h -H --with-filename --no-filename ); 14 | my @file_lists = qw( -f -g -l -L ); 15 | my @output = qw( -o --output ); 16 | my @fg = qw( -f -g ); 17 | my @case = qw( -i -I --smart-case ); 18 | 19 | return ( 20 | [qw(--and)] => [qw( --or )], 21 | [qw(-l)] => [@context, @file_lists, @pretty, @filename, @output, qw(--passthru --column --show-types)], 22 | [qw(-L)] => [@context, @file_lists, @pretty, @filename, @output, qw(--passthru --column --show-types -c -v)], 23 | [@output] => [@context, @file_lists, @output, qw(-c --column --show-types)], 24 | [qw(--passthru)] => [@context, @file_lists, @output, qw(-c --column -m -1 -v)], 25 | [qw(--match)] => [qw(-f -g)], 26 | [qw(-m)] => [@fg, qw(-1 -c)], 27 | [qw(-h)] => [@file_lists], 28 | [qw(-H)] => [@file_lists], 29 | [qw(-c)] => [@context, @pretty, @fg, qw(--column)], 30 | [qw(--column)] => [@file_lists], 31 | [@context] => [@file_lists], 32 | [qw(-f)] => [qw(-v), @case], 33 | [@fg] => [@fg, @pretty, qw(-x -c -u --files-from)], 34 | [qw(-p)] => [@context, @file_lists, qw(--passthru -c)], 35 | [qw(-v)] => [qw(--column -o --output --passthru)], 36 | [qw(-u)] => [@fg, qw(--output)], 37 | [qw(-x)] => [qw( --files-from )], 38 | ); 39 | } 40 | 41 | my %mutex; 42 | my @combos = invalid_combinations(); 43 | 44 | while ( my ($lefts,$rights) = splice( @combos, 0, 2 ) ) { 45 | for my $left ( @{$lefts} ) { 46 | $left =~ s/^-+//; 47 | 48 | for my $right ( @{$rights} ) { 49 | $right =~ s/^-+//; 50 | $mutex{$left}->{$right} = 1; 51 | $mutex{$right}->{$left} = 1; 52 | } 53 | } 54 | } 55 | 56 | $Data::Dumper::Indent = 1; 57 | $Data::Dumper::Trailingcomma = 1; 58 | $Data::Dumper::Sortkeys = 1; 59 | $Data::Dumper::Quotekeys = 0; 60 | 61 | my $out = Dumper( \%mutex ); 62 | $out =~ s/ / /g; 63 | $out =~ s/^/ /gsm; # Indent a tabstop 64 | $out =~ s/\$VAR1 =/return/sm; 65 | say $out; 66 | 67 | exit 0; 68 | -------------------------------------------------------------------------------- /dev/display-option-coverage.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | use lib 'blib/lib'; 6 | 7 | use App::Ack::ConfigLoader (); 8 | 9 | if ( ! -e 'opts.coverage' ) { 10 | die <<'END_DIE'; 11 | Coverage file 'opts.coverage' not found! 12 | If you'd like to generate this file, please 13 | set the ACK_OPTION_COVERAGE environment variable 14 | to a truthy value and run 'make test'. 15 | END_DIE 16 | } 17 | 18 | my @extra_options = ( 19 | 'type-add=s', 20 | 'type-set=s', 21 | 'dump', 22 | ); 23 | 24 | my $arg_spec = App::Ack::ConfigLoader::get_arg_spec({}); 25 | foreach my $option ( @extra_options ) { 26 | $arg_spec->{ $option } = 1; 27 | } 28 | 29 | my $fh; 30 | open $fh, '<', 'opts.coverage' or die $!; 31 | 32 | while ( <$fh> ) { 33 | chomp; 34 | 35 | delete $arg_spec->{$_}; 36 | } 37 | 38 | close $fh; 39 | 40 | if ( keys %{$arg_spec} ) { 41 | print "The following options were not used in the test suite:\n\n"; 42 | print " $_\n" foreach sort keys %{$arg_spec}; 43 | } 44 | else { 45 | print "All options were used in the test suite\n"; 46 | } 47 | -------------------------------------------------------------------------------- /dev/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM perl:5.30 2 | 3 | RUN mkdir -p /app 4 | WORKDIR /app 5 | 6 | RUN perl -MCPAN -e "install File::Next" 7 | RUN perl -MCPAN -e "install Test::Pod" 8 | -------------------------------------------------------------------------------- /dev/docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2.0' 2 | services: 3 | app: 4 | build: . 5 | volumes: 6 | - ../../:/app 7 | entrypoint: /app/dev/docker/docker-entrypoint.sh 8 | -------------------------------------------------------------------------------- /dev/docker/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | if [ ! -f Makefile ] 5 | then 6 | perl Makefile.PL 7 | make 8 | make test 9 | fi 10 | 11 | exec "$@" 12 | -------------------------------------------------------------------------------- /dev/issues-scrub.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | # This script is intended for Ack maintainers; we *really* like our items 4 | # in the issue tracker to be properly labelled and have milestones. So 5 | # this script finds any that don't satisfy both criteria and lets the 6 | # maintainer know. 7 | 8 | use strict; 9 | use warnings; 10 | use feature 'say'; 11 | 12 | use Net::GitHub; 13 | 14 | sub retrieve_issues { 15 | my $gh = Net::GitHub->new; 16 | $gh->set_default_user_repo('petdance', 'ack3'); 17 | my $issue = $gh->issue; 18 | my @issues = $issue->repos_issues( { state => 'open', per_page => 100 }); 19 | 20 | while($issue->has_next_page) { 21 | push @issues, $issue->next_page; 22 | } 23 | 24 | return @issues; 25 | } 26 | 27 | my @issues = retrieve_issues(); 28 | 29 | foreach my $issue (@issues) { 30 | my $is_pull_request = defined($issue->{'pull_request'}{'html_url'}); 31 | 32 | next if $is_pull_request; 33 | 34 | my $labels = $issue->{'labels'}; 35 | my $milestone = $issue->{'milestone'}; 36 | my $url = $issue->{'html_url'}; 37 | my $number = $issue->{'number'}; 38 | 39 | say "Issue #$number has no labels! ($url)" unless @{$labels}; 40 | say "Issue #$number has no milestone! ($url)" unless $milestone; 41 | } 42 | -------------------------------------------------------------------------------- /dev/linecount: -------------------------------------------------------------------------------- 1 | #!/bin/env perl 2 | 3 | # Counts the number of lines in each .txt file. 4 | 5 | use 5.10.1; 6 | use warnings; 7 | use strict; 8 | 9 | use File::Next; 10 | 11 | my $start = shift or die; 12 | my $total = 0; 13 | my $iter = File::Next::files( { descend_filter => sub { $_ ne '.git' }, file_filter => sub { /\.txt/ } }, $start ); 14 | while ( my $file = $iter->() ) { 15 | my $n = 0; 16 | open( my $fh, '<', $file ) or die "Can't open $file: $!\n"; 17 | while ( <$fh> ) { 18 | ++$n; 19 | } 20 | close $fh; 21 | printf "%5d: %s\n", $n, $file; 22 | $total += $n; 23 | } 24 | say "$total lines of text in $start"; 25 | -------------------------------------------------------------------------------- /dev/linecount-fork: -------------------------------------------------------------------------------- 1 | #!/bin/env perl 2 | 3 | # Counts the number of lines in each .txt file. 4 | 5 | use 5.10.1; 6 | use warnings; 7 | use strict; 8 | 9 | use File::Next; 10 | 11 | my $start = shift or die; 12 | 13 | use IO::Handle; 14 | pipe(FINDER, COUNTER); 15 | COUNTER->autoflush(1); 16 | 17 | my $child_pid; 18 | if ($child_pid = fork()) { 19 | close COUNTER; 20 | 21 | my $total = 0; 22 | while ( my $file = ) { 23 | my $n = 0; 24 | chomp $file; 25 | open( my $fh, '<', $file ) or die "Can't open $file: $!\n"; 26 | while ( <$fh> ) { 27 | ++$n; 28 | } 29 | close $fh; 30 | printf "%5d: %s\n", $n, $file; 31 | $total += $n; 32 | } 33 | waitpid $child_pid, 0; 34 | say "$total lines of text in $start"; 35 | } 36 | else { 37 | die "cannot fork: $!" unless defined $child_pid; 38 | close FINDER; 39 | my $iter = File::Next::files( { descend_filter => sub { $_ ne '.git' }, file_filter => sub { /\.txt/ } }, $start ); 40 | while ( my $file = $iter->() ) { 41 | say COUNTER $file; 42 | } 43 | warn "############ Child is done looking #############\n"; 44 | exit 0; 45 | } 46 | -------------------------------------------------------------------------------- /dev/multi-process.md: -------------------------------------------------------------------------------- 1 | From Rob Hoelz: 2 | 3 | I did some additional testing of ack using multiple workers - 4 | starting with a ondemand forking model, and then moving to a pre-fork 5 | model. The former didn't provide too much of a benefit, and the 6 | latter was somewhat complicated. At first I thought maybe I could 7 | have a shared work queue pipe, but I realized that each child process 8 | would probably buffer inputs, so the work would get divied up oddly. 9 | I then had the thought to maintain a list of available children for 10 | work - children would send a SIGUSR1 to their parent when done, and 11 | the parent would re-register the corresponding child in the 12 | availability list. A good idea in theory, but it turns out that 13 | even though `si_pid` is documented as occurring in `siginfo_t`, it 14 | either isn't available on Linux, or the Perl POSIX module doesn't 15 | pass it through. I ended up opting for a simple list of children 16 | and iterating through them in a round-robin fashion. A possible 17 | improvement could be random selection, perhaps weighted by the sizes 18 | of the files that each child has handled thus far. 19 | 20 | The multi-process model didn't show large benefits - although my 21 | first experiment didn't actually perform any work. When I added 22 | experiments to actually test against regexes, that's when I saw 23 | about a 200% speedup running with 8 jobs (8 being the number of 24 | cores in my machine). I tried 16 (figuring one job could be waiting 25 | on I/O while the other used the CPU), but actually saw a slowdown 26 | as a result. This might be different on HDDs, though. 27 | 28 | Another big improvement was to use sysread with a 128K buffer (which 29 | is the default cat uses) rather than Perl's diamond operator - this 30 | combined with the multi-process stuff lead to a 400% speedup. 31 | -------------------------------------------------------------------------------- /dev/record-options: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use blib; 7 | use App::Ack::ConfigLoader (); 8 | use Getopt::Long; 9 | 10 | my $option_coverage_file = 'opts.coverage'; 11 | 12 | my @extra_options = ( 13 | 'type-add=s', 14 | 'type-set=s', 15 | 'dump', 16 | ); 17 | 18 | my $fh; 19 | open $fh, '>>', $option_coverage_file or die $!; 20 | 21 | my $arg_spec = App::Ack::ConfigLoader::get_arg_spec({}, undef); 22 | foreach my $k (keys %{$arg_spec}, @extra_options) { 23 | $arg_spec->{$k} = sub { 24 | print { $fh } "$k\n"; 25 | }; 26 | } 27 | Getopt::Long::Configure('pass_through'); 28 | GetOptions(%{$arg_spec}); 29 | 30 | close $fh; 31 | -------------------------------------------------------------------------------- /dev/walk: -------------------------------------------------------------------------------- 1 | #!/bin/env perl 2 | 3 | use warnings; 4 | use strict; 5 | use 5.010; 6 | 7 | use File::Next; 8 | 9 | my $start = shift or die 'Specify a directory to walk'; 10 | 11 | my $iter = File::Next::files( 12 | { 13 | descend_filter => sub { $_ ne '.svn' }, 14 | sort_files => 0, 15 | }, 16 | $start 17 | ); 18 | 19 | my %seen; 20 | while ( my $file = $iter->() ) { 21 | # ++$seen{$file}; 22 | say $file; 23 | } 24 | -------------------------------------------------------------------------------- /lib/App/Ack/Files.pm: -------------------------------------------------------------------------------- 1 | package App::Ack::Files; 2 | 3 | use App::Ack (); 4 | use App::Ack::File (); 5 | 6 | use File::Next 1.16 (); 7 | 8 | use warnings; 9 | use strict; 10 | use 5.010; 11 | 12 | =head1 NAME 13 | 14 | App::Ack::Files 15 | 16 | =head1 SYNOPSIS 17 | 18 | A factory object for creating a stream of L objects. 19 | 20 | =head1 METHODS 21 | 22 | =head2 from_argv( \%opt, \@starting_points ) 23 | 24 | Return an iterator that does the file finding for us. 25 | 26 | =cut 27 | 28 | sub from_argv { 29 | my $class = shift; 30 | my $opt = shift; 31 | my $start = shift; 32 | 33 | my $self = bless {}, $class; 34 | 35 | my $descend_filter = $opt->{descend_filter}; 36 | 37 | if ( $opt->{n} ) { 38 | $descend_filter = sub { 39 | return 0; 40 | }; 41 | } 42 | 43 | $self->{iter} = 44 | File::Next::files( { 45 | file_filter => $opt->{file_filter}, 46 | descend_filter => $descend_filter, 47 | error_handler => _generate_error_handler(), 48 | warning_handler => sub {}, 49 | sort_files => $opt->{sort_files}, 50 | follow_symlinks => $opt->{follow}, 51 | }, @{$start} ); 52 | 53 | return $self; 54 | } 55 | 56 | =head2 from_file( \%opt, $filename ) 57 | 58 | Return an iterator that reads the list of files to search from a 59 | given file. If I<$filename> is '-', then it reads from STDIN. 60 | 61 | =cut 62 | 63 | sub from_file { 64 | my $class = shift; 65 | my $opt = shift; 66 | my $file = shift; 67 | 68 | my $error_handler = _generate_error_handler(); 69 | my $iter = 70 | File::Next::from_file( { 71 | error_handler => $error_handler, 72 | warning_handler => $error_handler, 73 | sort_files => $opt->{sort_files}, 74 | }, $file ) or return undef; 75 | 76 | return bless { 77 | iter => $iter, 78 | }, $class; 79 | } 80 | 81 | 82 | =head2 from_stdin() 83 | 84 | This is for reading input lines from STDIN, not the list of files 85 | from STDIN. 86 | 87 | =cut 88 | 89 | 90 | sub from_stdin { 91 | my $class = shift; 92 | 93 | my $self = bless {}, $class; 94 | 95 | $self->{iter} = sub { 96 | state $has_been_called = 0; 97 | 98 | if ( !$has_been_called ) { 99 | $has_been_called = 1; 100 | return '-'; 101 | } 102 | return; 103 | }; 104 | 105 | return $self; 106 | } 107 | 108 | 109 | sub next { 110 | my $self = shift; 111 | 112 | my $file = $self->{iter}->(); 113 | 114 | return unless defined($file); 115 | 116 | return App::Ack::File->new( $file ); 117 | } 118 | 119 | 120 | sub _generate_error_handler { 121 | if ( $App::Ack::report_bad_filenames ) { 122 | return sub { 123 | my $msg = shift; 124 | App::Ack::warn( $msg ); 125 | }; 126 | } 127 | else { 128 | return sub {}; 129 | } 130 | } 131 | 132 | 1; 133 | -------------------------------------------------------------------------------- /lib/App/Ack/Filter.pm: -------------------------------------------------------------------------------- 1 | package App::Ack::Filter; 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use App::Ack (); 7 | use App::Ack::Filter::Inverse (); 8 | 9 | my %filter_types; 10 | 11 | =head1 NAME 12 | 13 | App::Ack::Filter - Filter objects to filter files 14 | 15 | =head1 DESCRIPTION 16 | 17 | An abstract superclass that represents objects that can filter 18 | C objects. App::Ack::Filter implementations are 19 | responsible for filtering filenames to be searched. 20 | 21 | =head1 SYNOPSIS 22 | 23 | # filter implementation 24 | package MyFilter; 25 | 26 | use strict; 27 | use warnings; 28 | use parent 'App::Ack::Filter'; 29 | 30 | sub filter { 31 | my ( $self, $file ) = @_; 32 | } 33 | 34 | BEGIN { 35 | App::Ack::Filter->register_filter('mine' => __PACKAGE__); 36 | } 37 | 38 | 1; 39 | 40 | # users 41 | App::Ack::Filter->create_filter('mine', @args); 42 | 43 | 44 | =head1 METHODS 45 | 46 | =head2 App::Ack::Filter->create_filter($type, @args) 47 | 48 | Creates a filter implementation, registered as C<$type>. C<@args> 49 | are provided as additional arguments to the implementation's constructor. 50 | 51 | =cut 52 | 53 | sub create_filter { 54 | my ( undef, $type, @args ) = @_; 55 | 56 | if ( my $package = $filter_types{$type} ) { 57 | return $package->new(@args); 58 | } 59 | my $allowed_types = join( ', ', sort keys %filter_types ); 60 | App::Ack::die( "Unknown filter type '$type'. Type must be one of: $allowed_types." ); 61 | } 62 | 63 | =head2 App::Ack::Filter->register_filter($type, $package) 64 | 65 | Registers a filter implementation package C<$package> under 66 | the name C<$type>. 67 | 68 | =cut 69 | 70 | sub register_filter { 71 | my ( undef, $type, $package ) = @_; 72 | 73 | $filter_types{$type} = $package; 74 | 75 | return; 76 | } 77 | 78 | =head2 $filter->filter( $file ) 79 | 80 | Must be implemented by filter implementations. Returns 81 | true if the filter passes, false otherwise. This method 82 | must B alter the passed-in C<$file> object. 83 | 84 | =head2 $filter->invert() 85 | 86 | Returns a filter whose L method returns the opposite of this filter. 87 | 88 | =cut 89 | 90 | sub invert { 91 | my ( $self ) = @_; 92 | 93 | return App::Ack::Filter::Inverse->new( $self ); 94 | } 95 | 96 | =head2 $filter->is_inverted() 97 | 98 | Returns true if this filter is an inverted filter; false otherwise. 99 | 100 | =cut 101 | 102 | sub is_inverted { 103 | return 0; 104 | } 105 | 106 | =head2 $filter->to_string 107 | 108 | Converts the filter to a string. This method is also 109 | called implicitly by stringification. 110 | 111 | =cut 112 | 113 | sub to_string { 114 | return '(unimplemented to_string)'; 115 | } 116 | 117 | =head2 $filter->inspect 118 | 119 | Prints a human-readable debugging string for this filter. Useful for, 120 | you guessed it, debugging. 121 | 122 | =cut 123 | 124 | sub inspect { 125 | my ( $self ) = @_; 126 | 127 | return ref($self); 128 | } 129 | 130 | 1; 131 | -------------------------------------------------------------------------------- /lib/App/Ack/Filter/Collection.pm: -------------------------------------------------------------------------------- 1 | package App::Ack::Filter::Collection; 2 | 3 | =head1 NAME 4 | 5 | App::Ack::Filter::Collection 6 | 7 | =head1 DESCRIPTION 8 | 9 | The Ack::Filter::Collection class can contain filters and internally sort 10 | them into groups. The groups can then be optimized for faster filtering. 11 | 12 | Filters are grouped and replaced by a fast hash lookup. This leads to 13 | improved performance when many such filters are active, like when using 14 | the C<--known> command line option. 15 | 16 | =cut 17 | 18 | use strict; 19 | use warnings; 20 | use parent 'App::Ack::Filter'; 21 | 22 | sub new { 23 | my ( $class ) = @_; 24 | 25 | return bless { 26 | groups => {}, 27 | ungrouped => [], 28 | }, $class; 29 | } 30 | 31 | sub filter { 32 | my ( $self, $file ) = @_; 33 | 34 | for my $group (values %{$self->{groups}}) { 35 | return 1 if $group->filter($file); 36 | } 37 | 38 | for my $filter (@{$self->{ungrouped}}) { 39 | return 1 if $filter->filter($file); 40 | } 41 | 42 | return 0; 43 | } 44 | 45 | sub add { 46 | my ( $self, $filter ) = @_; 47 | 48 | if (exists $filter->{'groupname'}) { 49 | my $group = ($self->{groups}->{$filter->{groupname}} ||= $filter->create_group()); 50 | $group->add($filter); 51 | } 52 | else { 53 | push @{$self->{'ungrouped'}}, $filter; 54 | } 55 | 56 | return; 57 | } 58 | 59 | sub inspect { 60 | my ( $self ) = @_; 61 | 62 | return ref($self) . " - $self"; 63 | } 64 | 65 | sub to_string { 66 | my ( $self ) = @_; 67 | 68 | return join(', ', map { "($_)" } @{$self->{ungrouped}}); 69 | } 70 | 71 | 1; 72 | -------------------------------------------------------------------------------- /lib/App/Ack/Filter/Default.pm: -------------------------------------------------------------------------------- 1 | package App::Ack::Filter::Default; 2 | 3 | =head1 NAME 4 | 5 | App::Ack::Filter::Default 6 | 7 | =head1 DESCRIPTION 8 | 9 | The class that implements the filter that ack uses by 10 | default if you don't specify any filters on the command line. 11 | 12 | =cut 13 | 14 | use strict; 15 | use warnings; 16 | use parent 'App::Ack::Filter'; 17 | 18 | sub new { 19 | my ( $class ) = @_; 20 | 21 | return bless {}, $class; 22 | } 23 | 24 | sub filter { 25 | my ( undef, $file ) = @_; 26 | 27 | return -T $file->name; 28 | } 29 | 30 | 1; 31 | -------------------------------------------------------------------------------- /lib/App/Ack/Filter/Extension.pm: -------------------------------------------------------------------------------- 1 | package App::Ack::Filter::Extension; 2 | 3 | =head1 NAME 4 | 5 | App::Ack::Filter::Extension 6 | 7 | =head1 DESCRIPTION 8 | 9 | Implements filters based on extensions. 10 | 11 | =cut 12 | 13 | use strict; 14 | use warnings; 15 | use parent 'App::Ack::Filter'; 16 | 17 | use App::Ack::Filter (); 18 | use App::Ack::Filter::ExtensionGroup (); 19 | 20 | sub new { 21 | my ( $class, @extensions ) = @_; 22 | 23 | my $exts = join('|', map { "\Q$_\E"} @extensions); 24 | my $re = qr/[.](?:$exts)$/i; 25 | 26 | return bless { 27 | extensions => \@extensions, 28 | regex => $re, 29 | groupname => 'ExtensionGroup', 30 | }, $class; 31 | } 32 | 33 | sub create_group { 34 | return App::Ack::Filter::ExtensionGroup->new(); 35 | } 36 | 37 | sub filter { 38 | my ( $self, $file ) = @_; 39 | 40 | return $file->name =~ /$self->{regex}/; 41 | } 42 | 43 | sub inspect { 44 | my ( $self ) = @_; 45 | 46 | return ref($self) . ' - ' . $self->{regex}; 47 | } 48 | 49 | sub to_string { 50 | my ( $self ) = @_; 51 | 52 | return join( ' ', map { ".$_" } @{$self->{extensions}} ); 53 | } 54 | 55 | BEGIN { 56 | App::Ack::Filter->register_filter(ext => __PACKAGE__); 57 | } 58 | 59 | 1; 60 | -------------------------------------------------------------------------------- /lib/App/Ack/Filter/ExtensionGroup.pm: -------------------------------------------------------------------------------- 1 | package App::Ack::Filter::ExtensionGroup; 2 | 3 | =head1 NAME 4 | 5 | App::Ack::Filter::ExtensionGroup 6 | 7 | =head1 DESCRIPTION 8 | 9 | The App::Ack::Filter::ExtensionGroup class optimizes multiple ::Extension 10 | calls into one container. See App::Ack::Filter::IsGroup for details. 11 | 12 | =cut 13 | 14 | use strict; 15 | use warnings; 16 | use parent 'App::Ack::Filter'; 17 | 18 | sub new { 19 | my ( $class ) = @_; 20 | 21 | return bless { 22 | data => {}, 23 | }, $class; 24 | } 25 | 26 | sub add { 27 | my ( $self, $filter ) = @_; 28 | 29 | foreach my $ext (@{$filter->{extensions}}) { 30 | $self->{data}->{lc $ext} = 1; 31 | } 32 | 33 | return; 34 | } 35 | 36 | sub filter { 37 | my ( $self, $file ) = @_; 38 | 39 | if ($file->name =~ /[.]([^.]*)$/) { 40 | return exists $self->{'data'}->{lc $1}; 41 | } 42 | 43 | return 0; 44 | } 45 | 46 | sub inspect { 47 | my ( $self ) = @_; 48 | 49 | return ref($self) . " - $self"; 50 | } 51 | 52 | sub to_string { 53 | my ( $self ) = @_; 54 | 55 | return join(' ', map { ".$_" } sort keys %{$self->{data}}); 56 | } 57 | 58 | 1; 59 | -------------------------------------------------------------------------------- /lib/App/Ack/Filter/FirstLineMatch.pm: -------------------------------------------------------------------------------- 1 | package App::Ack::Filter::FirstLineMatch; 2 | 3 | =head1 NAME 4 | 5 | App::Ack::Filter::FirstLineMatch 6 | 7 | =head1 DESCRIPTION 8 | 9 | The class that implements filtering files by their first line. 10 | 11 | =cut 12 | 13 | 14 | use strict; 15 | use warnings; 16 | use parent 'App::Ack::Filter'; 17 | 18 | sub new { 19 | my ( $class, $re ) = @_; 20 | 21 | $re =~ s{^/|/$}{}g; # XXX validate? 22 | $re = qr{$re}i; 23 | 24 | return bless { 25 | regex => $re, 26 | }, $class; 27 | } 28 | 29 | # This test reads the first 250 characters of a file, then just uses the 30 | # first line found in that. This prevents reading something like an entire 31 | # .min.js file (which might be only one "line" long) into memory. 32 | 33 | sub filter { 34 | my ( $self, $file ) = @_; 35 | 36 | return $file->firstliney =~ /$self->{regex}/; 37 | } 38 | 39 | sub inspect { 40 | my ( $self ) = @_; 41 | 42 | 43 | return ref($self) . ' - ' . $self->{regex}; 44 | } 45 | 46 | sub to_string { 47 | my ( $self ) = @_; 48 | 49 | (my $re = $self->{regex}) =~ s{\([^:]*:(.*)\)$}{$1}; 50 | 51 | return "First line matches /$re/"; 52 | } 53 | 54 | BEGIN { 55 | App::Ack::Filter->register_filter(firstlinematch => __PACKAGE__); 56 | } 57 | 58 | 1; 59 | -------------------------------------------------------------------------------- /lib/App/Ack/Filter/Inverse.pm: -------------------------------------------------------------------------------- 1 | package App::Ack::Filter::Inverse; 2 | 3 | =head1 NAME 4 | 5 | App::Ack::Filter::Inverse 6 | 7 | =head1 DESCRIPTION 8 | 9 | The filter class that inverts another filter. 10 | 11 | =cut 12 | 13 | 14 | use strict; 15 | use warnings; 16 | use parent 'App::Ack::Filter'; 17 | 18 | sub new { 19 | my ( $class, $filter ) = @_; 20 | 21 | return bless { 22 | filter => $filter, 23 | }, $class; 24 | } 25 | 26 | sub filter { 27 | my ( $self, $file ) = @_; 28 | 29 | return !$self->{filter}->filter( $file ); 30 | } 31 | 32 | sub invert { 33 | my $self = shift; 34 | 35 | return $self->{'filter'}; 36 | } 37 | 38 | sub is_inverted { 39 | return 1; 40 | } 41 | 42 | sub inspect { 43 | my ( $self ) = @_; 44 | 45 | my $filter = $self->{'filter'}; 46 | 47 | return "!$filter"; 48 | } 49 | 50 | 1; 51 | -------------------------------------------------------------------------------- /lib/App/Ack/Filter/Is.pm: -------------------------------------------------------------------------------- 1 | package App::Ack::Filter::Is; 2 | 3 | =head1 NAME 4 | 5 | App::Ack::Filter::Is 6 | 7 | =head1 DESCRIPTION 8 | 9 | Filters based on exact filename match. 10 | 11 | =cut 12 | 13 | use strict; 14 | use warnings; 15 | use parent 'App::Ack::Filter'; 16 | 17 | use File::Spec 3.00 (); 18 | use App::Ack::Filter::IsGroup (); 19 | 20 | sub new { 21 | my ( $class, $filename ) = @_; 22 | 23 | return bless { 24 | filename => $filename, 25 | groupname => 'IsGroup', 26 | }, $class; 27 | } 28 | 29 | sub create_group { 30 | return App::Ack::Filter::IsGroup->new(); 31 | } 32 | 33 | sub filter { 34 | my ( $self, $file ) = @_; 35 | 36 | return (File::Spec->splitpath($file->name))[2] eq $self->{filename}; 37 | } 38 | 39 | sub inspect { 40 | my ( $self ) = @_; 41 | 42 | return ref($self) . ' - ' . $self->{filename}; 43 | } 44 | 45 | sub to_string { 46 | my ( $self ) = @_; 47 | 48 | return $self->{filename}; 49 | } 50 | 51 | BEGIN { 52 | App::Ack::Filter->register_filter(is => __PACKAGE__); 53 | } 54 | 55 | 1; 56 | -------------------------------------------------------------------------------- /lib/App/Ack/Filter/IsGroup.pm: -------------------------------------------------------------------------------- 1 | package App::Ack::Filter::IsGroup; 2 | 3 | =head1 NAME 4 | 5 | App::Ack::Filter::IsGroup 6 | 7 | =head1 DESCRIPTION 8 | 9 | The App::Ack::Filter::IsGroup class optimizes multiple 10 | App::Ack::Filter::Is calls into one container. 11 | 12 | Let's say you have 100 C<--type-add=is:...> filters. 13 | 14 | You could have 15 | 16 | my @filters = map { make_is_filter($_) } 1..100; 17 | 18 | and then do 19 | 20 | if ( any { $_->filter($rsrc) } @filters ) { ... } 21 | 22 | but that's slow, because of of method lookup overhead, function call 23 | overhead, etc. So ::Is filters know how to organize themselves into an 24 | ::IsGroup filter. 25 | 26 | =cut 27 | 28 | use strict; 29 | use warnings; 30 | use parent 'App::Ack::Filter'; 31 | 32 | sub new { 33 | my ( $class ) = @_; 34 | 35 | return bless { 36 | data => {}, 37 | }, $class; 38 | } 39 | 40 | sub add { 41 | my ( $self, $filter ) = @_; 42 | 43 | $self->{data}->{ $filter->{filename} } = 1; 44 | 45 | return; 46 | } 47 | 48 | sub filter { 49 | my ( $self, $file ) = @_; 50 | 51 | return exists $self->{data}->{ $file->basename }; 52 | } 53 | 54 | sub inspect { 55 | my ( $self ) = @_; 56 | 57 | return ref($self) . " - $self"; 58 | } 59 | 60 | sub to_string { 61 | my ( $self ) = @_; 62 | 63 | return join(' ', keys %{$self->{data}}); 64 | } 65 | 66 | 1; 67 | -------------------------------------------------------------------------------- /lib/App/Ack/Filter/IsPath.pm: -------------------------------------------------------------------------------- 1 | package App::Ack::Filter::IsPath; 2 | 3 | =head1 NAME 4 | 5 | App::Ack::Filter::IsPath 6 | 7 | =head1 DESCRIPTION 8 | 9 | Filters based on path. 10 | 11 | =cut 12 | 13 | use strict; 14 | use warnings; 15 | use parent 'App::Ack::Filter'; 16 | 17 | use App::Ack::Filter::IsPathGroup (); 18 | 19 | sub new { 20 | my ( $class, $filename ) = @_; 21 | 22 | return bless { 23 | filename => $filename, 24 | groupname => 'IsPathGroup', 25 | }, $class; 26 | } 27 | 28 | sub create_group { 29 | return App::Ack::Filter::IsPathGroup->new(); 30 | } 31 | 32 | sub filter { 33 | my ( $self, $file ) = @_; 34 | 35 | return $file->name eq $self->{filename}; 36 | } 37 | 38 | sub inspect { 39 | my ( $self ) = @_; 40 | 41 | return ref($self) . ' - ' . $self->{filename}; 42 | } 43 | 44 | sub to_string { 45 | my ( $self ) = @_; 46 | 47 | return $self->{filename}; 48 | } 49 | 50 | 1; 51 | -------------------------------------------------------------------------------- /lib/App/Ack/Filter/IsPathGroup.pm: -------------------------------------------------------------------------------- 1 | package App::Ack::Filter::IsPathGroup; 2 | 3 | =head1 NAME 4 | 5 | App::Ack::Filter::IsPathGroup 6 | 7 | =head1 DESCRIPTION 8 | 9 | The App::Ack::Filter::IsPathGroup class optimizes multiple ::IsPath 10 | calls into one container. See App::Ack::Filter::IsGroup for details. 11 | 12 | =cut 13 | 14 | use strict; 15 | use warnings; 16 | use parent 'App::Ack::Filter'; 17 | 18 | sub new { 19 | my ( $class ) = @_; 20 | 21 | return bless { 22 | data => {}, 23 | }, $class; 24 | } 25 | 26 | sub add { 27 | my ( $self, $filter ) = @_; 28 | 29 | $self->{data}->{ $filter->{filename} } = 1; 30 | 31 | return; 32 | } 33 | 34 | sub filter { 35 | my ( $self, $file ) = @_; 36 | 37 | return exists $self->{data}->{$file->name}; 38 | } 39 | 40 | sub inspect { 41 | my ( $self ) = @_; 42 | 43 | return ref($self) . " - $self"; 44 | } 45 | 46 | sub to_string { 47 | my ( $self ) = @_; 48 | 49 | return join(' ', keys %{$self->{data}}); 50 | } 51 | 52 | 1; 53 | -------------------------------------------------------------------------------- /lib/App/Ack/Filter/Match.pm: -------------------------------------------------------------------------------- 1 | package App::Ack::Filter::Match; 2 | 3 | use strict; 4 | use warnings; 5 | use parent 'App::Ack::Filter'; 6 | 7 | use App::Ack::Filter::MatchGroup (); 8 | 9 | =head1 NAME 10 | 11 | App::Ack::Filter::Match 12 | 13 | =head1 DESCRIPTION 14 | 15 | Implements filtering files by their filename (regular expression). 16 | 17 | =cut 18 | 19 | sub new { 20 | my ( $class, $re ) = @_; 21 | 22 | $re =~ s{^/|/$}{}g; # XXX validate? 23 | $re = qr/$re/i; 24 | 25 | return bless { 26 | regex => $re, 27 | groupname => 'MatchGroup', 28 | }, $class; 29 | } 30 | 31 | sub create_group { 32 | return App::Ack::Filter::MatchGroup->new; 33 | } 34 | 35 | sub filter { 36 | my ( $self, $file ) = @_; 37 | 38 | return $file->basename =~ /$self->{regex}/; 39 | } 40 | 41 | sub inspect { 42 | my ( $self ) = @_; 43 | 44 | return ref($self) . ' - ' . $self->{regex}; 45 | } 46 | 47 | sub to_string { 48 | my ( $self ) = @_; 49 | 50 | return "Filename matches $self->{regex}"; 51 | } 52 | 53 | BEGIN { 54 | App::Ack::Filter->register_filter(match => __PACKAGE__); 55 | } 56 | 57 | 1; 58 | -------------------------------------------------------------------------------- /lib/App/Ack/Filter/MatchGroup.pm: -------------------------------------------------------------------------------- 1 | package App::Ack::Filter::MatchGroup; 2 | 3 | =head1 NAME 4 | 5 | App::Ack::Filter::MatchGroup 6 | 7 | =head1 DESCRIPTION 8 | 9 | The App::Ack::Filter::MatchGroup class optimizes multiple ::Match calls 10 | into one container. See App::Ack::Filter::IsGroup for details. 11 | 12 | =cut 13 | 14 | use strict; 15 | use warnings; 16 | use parent 'App::Ack::Filter'; 17 | 18 | sub new { 19 | my ( $class ) = @_; 20 | 21 | return bless { 22 | matches => [], 23 | big_re => undef, 24 | }, $class; 25 | } 26 | 27 | sub add { 28 | my ( $self, $filter ) = @_; 29 | 30 | push @{ $self->{matches} }, $filter->{regex}; 31 | 32 | my $re = join('|', map { "(?:$_)" } @{ $self->{matches} }); 33 | $self->{big_re} = qr/$re/; 34 | 35 | return; 36 | } 37 | 38 | sub filter { 39 | my ( $self, $file ) = @_; 40 | 41 | return $file->basename =~ /$self->{big_re}/; 42 | } 43 | 44 | # This class has no inspect() or to_string() method. 45 | # It will just use the default one unless someone writes something useful. 46 | 47 | 1; 48 | -------------------------------------------------------------------------------- /perltidyrc: -------------------------------------------------------------------------------- 1 | --maximum-line-length=120 2 | --indent-columns=4 3 | --preserve-line-endings 4 | --no-tabs 5 | 6 | --continuation-indentation=4 7 | 8 | --paren-tightness=1 9 | --brace-tightness=1 10 | --block-brace-tightness=0 11 | --square-bracket-tightness=1 12 | --space-keyword-paren 13 | --nocuddled-else 14 | --opening-token-right 15 | --delete-old-newlines 16 | --comma-arrow-breakpoints=0 17 | --nobreak-at-old-ternary-breakpoints 18 | 19 | --noblanks-before-comments 20 | --blanks-before-subs 21 | --blanks-before-blocks 22 | 23 | -asc # allows perltidy to add a ';' when missing (default) 24 | -dsm # allows perltidy to delete an unnecessary ';' (default) 25 | 26 | -------------------------------------------------------------------------------- /squash: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | =pod 4 | 5 | Squashes together the parts of ack into the single ack-standalone 6 | file. 7 | 8 | The arguments to squash are either filenames from the ack distro 9 | like Ack.pm, or module names like File::Next. For modules, squash 10 | loads them and then pushes them into ack-standalone. 11 | 12 | POD gets stripped from all modules. 13 | 14 | =cut 15 | 16 | use warnings; 17 | use strict; 18 | 19 | use File::Next; 20 | 21 | # Make clear that ack is not supposed to be edited. 22 | my $NO_EDIT_COMMENT = <<'EOCOMMENT'; 23 | # 24 | # This file, ack, is generated code. 25 | # Please DO NOT EDIT or send patches for it. 26 | # 27 | # Please take a look at the source from 28 | # https://github.com/beyondgrep/ack3 29 | # and submit patches against the individual files 30 | # that build ack. 31 | # 32 | EOCOMMENT 33 | 34 | my @parts; 35 | push @parts, <<"PERL"; 36 | #!/usr/bin/env perl 37 | $NO_EDIT_COMMENT 38 | \$App::Ack::STANDALONE = 1; 39 | PERL 40 | 41 | for my $arg ( @ARGV ) { 42 | my $filename = $arg; 43 | if ( $arg =~ /::/ ) { 44 | my $key = "$arg.pm"; 45 | $key =~ s{::}{/}g; 46 | $filename = $INC{$key} or die "Can't find the file for $arg"; 47 | } 48 | my $is_ack = $filename eq 'ack'; 49 | 50 | warn "Reading $filename\n"; 51 | push @parts, 52 | "{\n", 53 | _get_code_from_file( $filename ), 54 | "}\n", 55 | ; 56 | } 57 | 58 | my $code = join( '', @parts ); 59 | 60 | # We only use two of the four constructors in File::Next, so delete the other two. 61 | for my $unused_func ( qw( dirs everything ) ) { 62 | $code =~ s/^sub $unused_func\b.*?^}//sm or die qq{Unable to find the sub "$unused_func" to remove from ack-standalone}; 63 | } 64 | print $code; 65 | 66 | exit 0; 67 | 68 | 69 | sub _get_code_from_file { 70 | my $filename = shift; 71 | 72 | my @lines; 73 | open( my $fh, '<', $filename ) or die "Can't open $filename: $!"; 74 | my $was_in_pod = 0; 75 | my $in_pod = 0; 76 | 77 | while ( <$fh> ) { 78 | next if /^use (?:File::Next|App::Ack)/; 79 | 80 | s/^use parent (.+)/BEGIN {\n our \@ISA = $1\n}/; 81 | 82 | if ( $was_in_pod && !$in_pod ) { 83 | $was_in_pod = 0; 84 | } 85 | 86 | # See if we're in module POD blocks. 87 | if ( $filename ne 'ack' ) { 88 | $was_in_pod = $in_pod; 89 | 90 | if ( /^=(\w+)/ ) { 91 | $in_pod = ($1 ne 'cut'); 92 | next; 93 | } 94 | elsif ( $in_pod ) { 95 | next; 96 | } 97 | } 98 | 99 | # Replace the shebang line and append 'no edit' comment 100 | if ( s{^#!.+}{package main;} ) { 101 | # Skip the shebang line and replace it with package statement. 102 | } 103 | 104 | # Remove Perl::Critic comments. 105 | # I'd like to remove all comments, but this is a start 106 | s{\s*##.+critic.*}{}; 107 | push @lines, $_; 108 | } 109 | 110 | close $fh; 111 | 112 | return join( '', @lines ); 113 | } 114 | -------------------------------------------------------------------------------- /stack: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | make ack-standalone 3 | ./ack-standalone "$@" 4 | -------------------------------------------------------------------------------- /t/00-load.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use 5.010; 4 | use warnings; 5 | use strict; 6 | 7 | use Test::More tests => 20; 8 | 9 | # Load all these modules to get their versions. 10 | use App::Ack; 11 | use File::Next; 12 | use File::Spec; 13 | use Getopt::Long; 14 | use Pod::Perldoc; 15 | use Pod::Text; 16 | use Pod::Usage; 17 | use Term::ANSIColor; 18 | use Test::Harness; 19 | 20 | my @modules = qw( 21 | File::Next 22 | File::Spec 23 | Getopt::Long 24 | Pod::Perldoc 25 | Pod::Text 26 | Pod::Usage 27 | Term::ANSIColor 28 | Test::Harness 29 | Test::More 30 | ); 31 | 32 | pass( 'All external modules loaded' ); 33 | 34 | diag sprintf( 'Testing ack %s under Perl v%vd, %s', $App::Ack::VERSION, $^V, $^X ); 35 | for my $module ( @modules ) { 36 | no strict 'refs'; 37 | my $ver = ${$module . '::VERSION'}; 38 | diag( "Using $module $ver" ); 39 | } 40 | diag( 'PATH=' . ($ENV{PATH} // '') ); 41 | 42 | # Find all the .pm files in blib/ and make sure they can be Cd. 43 | my $iter = File::Next::files( { file_filter => sub { /\.pm$/ } }, 'blib' ); 44 | while ( my $file = $iter->() ) { 45 | $file =~ s/\.pm$// or die "There should be a .pm at the end of $file but there isn't"; 46 | my @dirs = File::Spec->splitdir( $file ); 47 | 48 | # Break apart the path, throw away blib/lib/, and reconstitute the module name. 49 | my $junk = shift @dirs; 50 | die unless $junk eq 'blib'; 51 | 52 | $junk = shift @dirs; 53 | die unless $junk eq 'lib'; 54 | 55 | my $module = join( '::', @dirs ); 56 | $module =~ /^([a-z::]+)$/i or die "Invalid module name $module"; 57 | $module = $1; # Untainted 58 | my $rc = eval "use $module; 1;"; 59 | ok( $rc, "use $module" ); 60 | } 61 | 62 | done_testing(); 63 | -------------------------------------------------------------------------------- /t/FilterTest.pm: -------------------------------------------------------------------------------- 1 | package FilterTest; 2 | 3 | use strict; 4 | use warnings; 5 | use parent 'Exporter'; 6 | 7 | use App::Ack::File; 8 | use File::Next; 9 | use Util; 10 | use Test::More; 11 | 12 | our @EXPORT = qw(filter_test); 13 | 14 | sub swamp_files { 15 | my @swamp_files; 16 | 17 | my $files = File::Next::files( 't/swamp' ); 18 | while ( my $file = $files->() ) { 19 | push( @swamp_files, $file ); 20 | } 21 | 22 | return @swamp_files; 23 | } 24 | 25 | sub filter_test { 26 | local $Test::Builder::Level = $Test::Builder::Level + 1; 27 | 28 | my $filter_args = shift; 29 | my $expected_matches = shift; 30 | my $msg = shift or die 'Must pass a message to filter_test()'; 31 | 32 | return subtest "filter_test($msg)" => sub { 33 | my $filter = eval { 34 | App::Ack::Filter->create_filter(@{$filter_args}); 35 | }; 36 | 37 | ok($filter) or diag($@); 38 | 39 | my @matches = map { 40 | $_->name 41 | } grep { 42 | $filter->filter($_) 43 | } map { 44 | App::Ack::File->new($_) 45 | } swamp_files(); 46 | 47 | sets_match(\@matches, $expected_matches, $msg); 48 | }; 49 | } 50 | 51 | 52 | 1; 53 | -------------------------------------------------------------------------------- /t/ack-1.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: All the Montresors 3 | ack3-only: true 4 | args: Montresor -h t/text 5 | stdout: | 6 | the catacombs of the Montresors. 7 | "The Montresors," I replied, "were a great and numerous family." 8 | "For the love of God, Montresor!" 9 | 10 | --- 11 | name: Stop at one Montresor 12 | ack3-only: true 13 | args: Montresor -1 -h t/text 14 | stdout: | 15 | the catacombs of the Montresors. 16 | 17 | --- 18 | name: With dash v 19 | ack3-only: true 20 | args: c -1 -h -v t/text/raven.txt 21 | stdout: ' Only this and nothing more."' 22 | 23 | --- 24 | name: Dash f 25 | ack3-only: true 26 | args: -1 -f --sort-files t/swamp 27 | stdout: 28 | t/swamp/0 29 | 30 | --- 31 | name: Dash g 32 | ack3-only: true 33 | args: -1 -g fresh t/ 34 | stdout: 35 | t/swamp/fresh.css 36 | 37 | --- 38 | name: Dash l 39 | ack3-only: true 40 | args: -1 -l --sort-files the t/text 41 | stdout: | 42 | t/text/amontillado.txt 43 | -------------------------------------------------------------------------------- /t/ack-Q.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More; 7 | 8 | plan tests => 2; 9 | 10 | use lib 't'; 11 | use Util; 12 | 13 | prep_environment(); 14 | 15 | my $ACK = $ENV{ACK_TEST_STANDALONE} ? 'ack-standalone' : 'ack'; 16 | 17 | subtest 'Square brackets' => sub { 18 | plan tests => 4; 19 | 20 | my @args = qw( [ack] t/swamp ); 21 | my @results = run_ack( @args ); 22 | cmp_ok( scalar @results, '>', 100, 'Without quoting, the [ack] returns many matches' ); 23 | 24 | my $target = reslash( 't/swamp/Makefile' ); 25 | my @expected = line_split( <<"HERE" ); 26 | $target:15:# EXE_FILES => [q[ack]] 27 | $target:17:# NAME => q[ack] 28 | HERE 29 | for my $arg ( qw( -Q --literal ) ) { 30 | ack_lists_match( [ @args, $arg ], \@expected, "$arg should make svn+ssh finable" ); 31 | } 32 | }; 33 | 34 | 35 | subtest 'Patterns that would be invalid if not -Q' => sub { 36 | plan tests => 21; 37 | 38 | my %problems = ( 39 | '*' => 'Quantifier follows nothing in regex', 40 | '[' => 'Unmatched [ in regex', 41 | '(' => 'Unmatched ( in regex', 42 | ); 43 | 44 | while ( my ($problem, $explanation) = each %problems ) { 45 | my ($output,$stderr) = run_ack_with_stderr( $problem ); 46 | 47 | is_empty_array( $output, 'No output' ); 48 | is( $stderr->[0], "$ACK: Invalid regex '$problem'", 'Line 1 OK' ); 49 | is( $stderr->[1], "Regex: $problem", 'Line 2 OK' ); 50 | like( $stderr->[2], qr/\Q^---HERE $explanation/, 'Does the explanation match?' ); 51 | is( scalar @{$stderr}, 3, 'Only 3 lines' ); 52 | } 53 | 54 | for my $problem ( keys %problems ) { 55 | my @results = run_ack( '-Q', $problem ); 56 | cmp_ok( scalar @results, '>', 100, 'When we quote, all is happy and we get lots of results' ); 57 | } 58 | }; 59 | 60 | 61 | exit 0; 62 | -------------------------------------------------------------------------------- /t/ack-Q.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # The unquoted "+" in "svn+ssh" will make the match fail. 3 | name: Plus sign without -Q 4 | args: svn+ssh t/swamp 5 | exitcode: 1 6 | stdout: 7 | 8 | --- 9 | name: Plus sign with -Q 10 | args: svn+ssh t/swamp -Q 11 | ordered: true 12 | stdout: | 13 | t/swamp/Rakefile:44: baseurl = "svn+ssh://#{ENV['USER']}@rubyforge.org/var/svn/#{PKG_NAME}" 14 | t/swamp/Rakefile:50: baseurl = "svn+ssh://#{ENV['USER']}@rubyforge.org/var/svn/#{PKG_NAME}" 15 | -------------------------------------------------------------------------------- /t/ack-c.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: -c finds things 3 | args: God -c t/text 4 | stdout: | 5 | t/text/amontillado.txt:2 6 | t/text/bill-of-rights.txt:0 7 | t/text/constitution.txt:0 8 | t/text/gettysburg.txt:1 9 | t/text/movies.txt:0 10 | t/text/number.txt:0 11 | t/text/numbered-text.txt:0 12 | t/text/ozymandias.txt:0 13 | t/text/raven.txt:2 14 | 15 | --- 16 | name: -c finds nothing 17 | args: NOTTHERE -c t/text 18 | exitcode: 1 19 | stdout: | 20 | t/text/amontillado.txt:0 21 | t/text/bill-of-rights.txt:0 22 | t/text/constitution.txt:0 23 | t/text/gettysburg.txt:0 24 | t/text/movies.txt:0 25 | t/text/number.txt:0 26 | t/text/numbered-text.txt:0 27 | t/text/ozymandias.txt:0 28 | t/text/raven.txt:0 29 | 30 | --- 31 | name: -c and --no-filename 32 | args: God -c --no-filename t/text 33 | stdout: | 34 | 5 35 | 36 | --- 37 | name: -c and -v 38 | args: the -i -w -v -c --sort-files t/text 39 | ordered: true 40 | stdout: | 41 | t/text/amontillado.txt:206 42 | t/text/bill-of-rights.txt:45 43 | t/text/constitution.txt:259 44 | t/text/gettysburg.txt:15 45 | t/text/movies.txt:25 46 | t/text/number.txt:1 47 | t/text/numbered-text.txt:20 48 | t/text/ozymandias.txt:9 49 | t/text/raven.txt:77 50 | 51 | --- 52 | name: -c and -l 53 | args: congress -i -l -c --sort-files t/text 54 | ordered: true 55 | stdout: | 56 | t/text/bill-of-rights.txt:1 57 | t/text/constitution.txt:29 58 | 59 | --- 60 | name: -c and -l find nothing 61 | args: NOTTHERE -i -l -c --sort-files t/text 62 | ordered: true 63 | exitcode: 1 64 | stdout: 65 | 66 | --- 67 | name: -c and -h 68 | args: Montresor -c -h t/text 69 | stdout: | 70 | 3 71 | 72 | --- 73 | name: Normal count 74 | args: Montresor -c -h t/text/amontillado.txt 75 | stdout: | 76 | 3 77 | 78 | --- 79 | name: Count with --not 80 | args: Montresor -c -h --not God t/text/amontillado.txt 81 | stdout: | 82 | 2 83 | -------------------------------------------------------------------------------- /t/ack-column.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: With --columns 3 | args: nevermore --column -w -i --with-filename --noenv t/text/raven.txt 4 | ordered: true 5 | stdout: | 6 | t/text/raven.txt:55:23: Quoth the Raven, "Nevermore." 7 | t/text/raven.txt:62:24: With such name as "Nevermore." 8 | t/text/raven.txt:69:26: Then the bird said, "Nevermore." 9 | t/text/raven.txt:76:18: Of 'Never -- nevermore.' 10 | t/text/raven.txt:83:24: Meant in croaking "Nevermore." 11 | t/text/raven.txt:90:26: She shall press, ah, nevermore! 12 | t/text/raven.txt:97:23: Quoth the Raven, "Nevermore." 13 | t/text/raven.txt:104:23: Quoth the Raven, "Nevermore." 14 | t/text/raven.txt:111:23: Quoth the Raven, "Nevermore." 15 | t/text/raven.txt:118:23: Quoth the Raven, "Nevermore." 16 | t/text/raven.txt:125:22: Shall be lifted--nevermore! 17 | 18 | --- 19 | name: With --column and --not 20 | args: nevermore --not Quoth -w -i --with-filename --noenv --column t/text/raven.txt 21 | ordered: true 22 | stdout: | 23 | t/text/raven.txt:62:24: With such name as "Nevermore." 24 | t/text/raven.txt:69:26: Then the bird said, "Nevermore." 25 | t/text/raven.txt:76:18: Of 'Never -- nevermore.' 26 | t/text/raven.txt:83:24: Meant in croaking "Nevermore." 27 | t/text/raven.txt:90:26: She shall press, ah, nevermore! 28 | t/text/raven.txt:125:22: Shall be lifted--nevermore! 29 | 30 | --- 31 | name: Without columns 32 | args: nevermore -w -i --with-filename --noenv --no-column t/text/raven.txt 33 | ordered: true 34 | stdout: | 35 | t/text/raven.txt:55: Quoth the Raven, "Nevermore." 36 | t/text/raven.txt:62: With such name as "Nevermore." 37 | t/text/raven.txt:69: Then the bird said, "Nevermore." 38 | t/text/raven.txt:76: Of 'Never -- nevermore.' 39 | t/text/raven.txt:83: Meant in croaking "Nevermore." 40 | t/text/raven.txt:90: She shall press, ah, nevermore! 41 | t/text/raven.txt:97: Quoth the Raven, "Nevermore." 42 | t/text/raven.txt:104: Quoth the Raven, "Nevermore." 43 | t/text/raven.txt:111: Quoth the Raven, "Nevermore." 44 | t/text/raven.txt:118: Quoth the Raven, "Nevermore." 45 | t/text/raven.txt:125: Shall be lifted--nevermore! 46 | -------------------------------------------------------------------------------- /t/ack-create-ackrc.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use lib 't'; 7 | 8 | use Test::More tests => 5; 9 | use Util; 10 | use App::Ack (); 11 | use App::Ack::ConfigDefault (); 12 | 13 | prep_environment(); 14 | 15 | my @commented = App::Ack::ConfigDefault::options(); 16 | my @uncommented = App::Ack::ConfigDefault::options_clean(); 17 | 18 | cmp_ok( scalar @commented, '>', scalar @uncommented, 'There are fewer lines in the uncommented options.' ); 19 | 20 | my @output = run_ack( 'ack', '--create-ackrc' ); 21 | 22 | ok(scalar(grep { $_ eq '--ignore-ack-defaults' } @output), '--ignore-ack-defaults should be present in output'); 23 | @output = grep { $_ ne '--ignore-ack-defaults' } @output; 24 | 25 | lists_match(\@output, \@commented, 'lines in output should match the default options'); 26 | 27 | my @versions = grep { /\Qack version $App::Ack::VERSION/ } @commented; 28 | is( scalar @versions, 1, 'Got exactly one version line' ); 29 | -------------------------------------------------------------------------------- /t/ack-dump.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use lib 't'; 7 | use Util; 8 | 9 | use Test::More tests => 5; 10 | 11 | use App::Ack::ConfigDefault; 12 | 13 | prep_environment(); 14 | 15 | DUMP: { 16 | my @expected = App::Ack::ConfigDefault::options_clean(); 17 | 18 | my @args = qw( --dump ); 19 | my @results = run_ack( @args ); 20 | 21 | is( $results[0], 'Defaults', 'header should be Defaults' ); 22 | splice @results, 0, 2; # remove header (2 lines) 23 | 24 | s/^\s*// for @results; 25 | 26 | sets_match( \@results, \@expected, __FILE__ ); 27 | 28 | my @perl = grep { /\bperl\b/ } @results; 29 | is( scalar @perl, 2, 'Two specs for Perl' ); 30 | 31 | my @ignore_dir = grep { /ignore-dir/ } @results; 32 | is( scalar @ignore_dir, 28, 'Twenty-eight specs for ignoring directories' ); 33 | } 34 | 35 | exit 0; 36 | -------------------------------------------------------------------------------- /t/ack-f.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Default directory exclusions 3 | args: -f t/swamp 4 | stdout: | 5 | t/swamp/0 6 | t/swamp/c-header.h 7 | t/swamp/c-source.c 8 | t/swamp/constitution-100k.pl 9 | t/swamp/crystallography-weenies.f 10 | t/swamp/example.R 11 | t/swamp/file.bar 12 | t/swamp/file.foo 13 | t/swamp/foo_test.py 14 | t/swamp/fresh.css 15 | t/swamp/groceries/another_subdir/fruit 16 | t/swamp/groceries/another_subdir/junk 17 | t/swamp/groceries/another_subdir/meat 18 | t/swamp/groceries/dir.d/fruit 19 | t/swamp/groceries/dir.d/junk 20 | t/swamp/groceries/dir.d/meat 21 | t/swamp/groceries/fruit 22 | t/swamp/groceries/junk 23 | t/swamp/groceries/meat 24 | t/swamp/groceries/subdir/fruit 25 | t/swamp/groceries/subdir/junk 26 | t/swamp/groceries/subdir/meat 27 | t/swamp/html.htm 28 | t/swamp/html.html 29 | t/swamp/incomplete-last-line.txt 30 | t/swamp/javascript.js 31 | t/swamp/lua-shebang-test 32 | t/swamp/Makefile 33 | t/swamp/Makefile.PL 34 | t/swamp/MasterPage.master 35 | t/swamp/notaMakefile 36 | t/swamp/notaRakefile 37 | t/swamp/notes.md 38 | t/swamp/options-crlf.pl 39 | t/swamp/options.pl 40 | t/swamp/perl-test.t 41 | t/swamp/perl-without-extension 42 | t/swamp/perl.cgi 43 | t/swamp/perl.handler.pod 44 | t/swamp/perl.pl 45 | t/swamp/perl.pm 46 | t/swamp/perl.pod 47 | t/swamp/pipe-stress-freaks.F 48 | t/swamp/Rakefile 49 | t/swamp/Sample.ascx 50 | t/swamp/Sample.asmx 51 | t/swamp/sample.asp 52 | t/swamp/sample.aspx 53 | t/swamp/sample.rake 54 | t/swamp/service.svc 55 | t/swamp/stuff.cmake 56 | t/swamp/test_foo.py 57 | t/swamp/test.py 58 | t/swamp/CMakeLists.txt 59 | t/swamp/swamp/ignoreme.txt 60 | t/swamp/not-an-#emacs-workfile# 61 | 62 | --- 63 | name: Combined filters 64 | args: -f t/swamp -t perl -t rake 65 | stdout: | 66 | t/swamp/0 67 | t/swamp/constitution-100k.pl 68 | t/swamp/perl.pm 69 | t/swamp/Rakefile 70 | t/swamp/options-crlf.pl 71 | t/swamp/options.pl 72 | t/swamp/perl-without-extension 73 | t/swamp/perl.cgi 74 | t/swamp/Makefile.PL 75 | t/swamp/perl-test.t 76 | t/swamp/perl.handler.pod 77 | t/swamp/perl.pl 78 | t/swamp/perl.pod 79 | 80 | --- 81 | name: Exit code with no matches 82 | args: "-f t/swamp --type-add=baz:ext:baz -t baz" 83 | exitcode: 1 84 | stdout: 85 | -------------------------------------------------------------------------------- /t/ack-files-from.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | use lib 't'; 6 | 7 | use Test::More tests => 6; 8 | 9 | use Util; 10 | 11 | prep_environment(); 12 | 13 | 14 | my @textfiles = qw( 15 | t/text/amontillado.txt 16 | t/text/bill-of-rights.txt 17 | t/text/constitution.txt 18 | t/text/gettysburg.txt 19 | t/text/ozymandias.txt 20 | t/text/raven.txt 21 | ); 22 | 23 | 24 | subtest 'Basic reading from files, no switches' => sub { 25 | plan tests => 1; 26 | 27 | my $target_file = reslash( 't/swamp/options.pl' ); 28 | my @expected = line_split( <<"HERE" ); 29 | $target_file:2:use strict; 30 | HERE 31 | 32 | my $tempfile = create_tempfile( qw( t/swamp/options.pl t/swamp/pipe-stress-freaks.F ) ); 33 | 34 | ack_lists_match( [ '--files-from=' . $tempfile->filename, 'strict' ], \@expected, 'Looking for strict in multiple files' ); 35 | 36 | unlink $tempfile->filename; 37 | }; 38 | 39 | 40 | subtest 'Non-existent file specified' => sub { 41 | plan tests => 3; 42 | 43 | my @args = qw( strict ); 44 | my ( $stdout, $stderr ) = run_ack_with_stderr( '--files-from=non-existent-file', @args); 45 | 46 | is_empty_array( $stdout, 'No STDOUT for non-existent file' ); 47 | is( scalar @{$stderr}, 1, 'One line of STDERR for non-existent file' ); 48 | like( $stderr->[0], qr/Unable to open non-existent-file:/, 49 | 'Correct warning message for non-existent file' ); 50 | }; 51 | 52 | 53 | subtest 'Source file exists, but non-existent files mentioned in the file' => sub { 54 | plan tests => 4; 55 | 56 | my $tempfile = create_tempfile( qw( t/swamp/options.pl file-that-isnt-there ) ); 57 | my ( $stdout, $stderr ) = run_ack_with_stderr( '--files-from=' . $tempfile->filename, 'CASE'); 58 | 59 | is( scalar @{$stdout}, 1, 'One hit found' ); 60 | like( $stdout->[0], qr/THIS IS ALL IN UPPER CASE/, 'Find the one line in the file' ); 61 | 62 | is( scalar @{$stderr}, 1, 'One line of STDERR for non-existent file' ); 63 | like( $stderr->[0], qr/file-that-isnt-there: No such file/, 'Correct warning message for non-existent file' ); 64 | }; 65 | 66 | 67 | subtest '-l and --files-from' => sub { 68 | plan tests => 1; 69 | 70 | my $tempfile = create_tempfile( @textfiles ); 71 | 72 | my @expected = qw( 73 | t/text/amontillado.txt 74 | t/text/gettysburg.txt 75 | t/text/raven.txt 76 | ); 77 | 78 | ack_sets_match( [ '--files-from=' . $tempfile->filename, 'God', '-l' ], \@expected, 'Looking for God files' ); 79 | }; 80 | 81 | 82 | subtest '-L and --files-from' => sub { 83 | plan tests => 1; 84 | 85 | my $tempfile = create_tempfile( @textfiles ); 86 | 87 | my @expected = qw( 88 | t/text/bill-of-rights.txt 89 | t/text/constitution.txt 90 | t/text/ozymandias.txt 91 | ); 92 | 93 | ack_sets_match( [ '--files-from=' . $tempfile->filename, 'God', '-L' ], \@expected, 'Looking for absence of God' ); 94 | }; 95 | 96 | 97 | subtest '-c and --files-from' => sub { 98 | plan tests => 1; 99 | 100 | my $tempfile = create_tempfile( @textfiles ); 101 | 102 | my @expected = qw( 103 | t/text/amontillado.txt:2 104 | t/text/bill-of-rights.txt:0 105 | t/text/constitution.txt:0 106 | t/text/gettysburg.txt:1 107 | t/text/ozymandias.txt:0 108 | t/text/raven.txt:2 109 | ); 110 | 111 | ack_sets_match( [ '--files-from=' . $tempfile->filename, 'God', '-c' ], \@expected, 'Looking for God files' ); 112 | }; 113 | 114 | 115 | done_testing(); 116 | 117 | exit 0; 118 | -------------------------------------------------------------------------------- /t/ack-g.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use Test::More tests => 2; 7 | 8 | use lib 't'; 9 | use Util; 10 | 11 | prep_environment(); 12 | 13 | subtest 'test -g with --color' => sub { 14 | plan tests => 2; 15 | 16 | my $file_regex = 'text'; 17 | my $expected_original = <<'HERE'; 18 | t/(text)/amontillado.txt 19 | t/(text)/bill-of-rights.txt 20 | t/(text)/constitution.txt 21 | t/(text)/gettysburg.txt 22 | t/(text)/movies.txt 23 | t/(text)/number.txt 24 | t/(text)/numbered-(text).txt 25 | t/(text)/ozymandias.txt 26 | t/(text)/raven.txt 27 | HERE 28 | 29 | $expected_original = windows_slashify( $expected_original ) if is_windows; 30 | 31 | my @expected = colorize( $expected_original ); 32 | 33 | my @args = ( '--sort-files', '-g', $file_regex ); 34 | 35 | my @results = run_ack(@args, '--color', 't/text'); 36 | 37 | is_deeply( \@results, \@expected, 'Colorizing -g output with --color should work'); 38 | }; 39 | 40 | 41 | subtest q{test -g without --color; make sure colors don't show} => sub { 42 | if ( !has_io_pty() ) { 43 | plan skip_all => 'IO::Pty is required for this test'; 44 | return; 45 | } 46 | 47 | plan tests => 1; 48 | 49 | my $file_regex = 'text'; 50 | my $expected = <<'HERE'; 51 | t/text/amontillado.txt 52 | t/text/bill-of-rights.txt 53 | t/text/constitution.txt 54 | t/text/gettysburg.txt 55 | t/text/movies.txt 56 | t/text/number.txt 57 | t/text/numbered-text.txt 58 | t/text/ozymandias.txt 59 | t/text/raven.txt 60 | HERE 61 | 62 | my @args = ( '--sort-files', '-g', $file_regex, 't/text' ); 63 | 64 | my $results = run_ack_interactive(@args); 65 | 66 | is( $results, $expected, 'Colorizing -g output without --color should have no color' ); 67 | }; 68 | 69 | done_testing(); 70 | 71 | exit 0; 72 | -------------------------------------------------------------------------------- /t/ack-h.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: No switches, one file 3 | args: strict t/swamp/options.pl 4 | stdout: | 5 | use strict; 6 | 7 | --- 8 | name: No switches, multiple files 9 | args: strict t/swamp/options.pl t/swamp/pipe-stress-freaks.F 10 | stdout: | 11 | t/swamp/options.pl:2:use strict; 12 | 13 | --- 14 | name: With switches, one file 15 | args: 16 | - strict t/swamp/options.pl -H 17 | - strict t/swamp/options.pl --with-filename 18 | stdout: | 19 | t/swamp/options.pl:2:use strict; 20 | 21 | --- 22 | name: With switches, multiple files 23 | args: 24 | - strict t/swamp/options.pl t/swamp/crystallography-weenies.f -h 25 | - strict t/swamp/options.pl t/swamp/crystallography-weenies.f --no-filename 26 | stdout: | 27 | use strict; 28 | -------------------------------------------------------------------------------- /t/ack-help-types.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use List::Util qw(sum); 7 | use Test::More; 8 | 9 | use lib 't'; 10 | use Util; 11 | 12 | prep_environment(); 13 | 14 | my @types = ( 15 | perl => [qw{.pl .pod .pl .t}], 16 | python => [qw{.py}], 17 | ruby => [qw{.rb Rakefile}], 18 | ); 19 | 20 | plan tests => 11; 21 | 22 | my @output = run_ack( '--help-types' ); 23 | 24 | while ( my ($type,$checks) = splice( @types, 0, 2 ) ) { 25 | my ( $matching_line ) = grep { /^ $type / } @output; 26 | 27 | ok( $matching_line, "A match should be found for $type in the output for --help-types" ); 28 | 29 | foreach my $check (@{$checks}) { 30 | like( $matching_line, qr/\Q$check\E/, "Line for --$type in output for --help-types contains $check" ); 31 | } 32 | } 33 | 34 | done_testing(); 35 | 36 | exit 0; 37 | -------------------------------------------------------------------------------- /t/ack-help.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More; 7 | use lib 't'; 8 | use Util; 9 | 10 | { 11 | my $help_options; 12 | 13 | sub _populate_help_options { 14 | my ( $output, undef ) = run_ack_with_stderr( '--help' ); 15 | 16 | $help_options = []; 17 | 18 | foreach my $line (@{$output}) { 19 | if ( $line =~ /^\s+-/ ) { 20 | while ( $line =~ /(-[^\s=,]+)/g ) { 21 | my $option = $1; 22 | chop $option if $option =~ /\[$/; 23 | 24 | if ( $option =~ s/^--\[no\]/--/ ) { 25 | my $negated_option = $option; 26 | substr $negated_option, 2, 0, 'no'; 27 | push @{$help_options}, $negated_option; 28 | } 29 | 30 | push @{$help_options}, $option; 31 | } 32 | } 33 | } 34 | 35 | return; 36 | } 37 | 38 | sub get_help_options { 39 | _populate_help_options() unless $help_options; 40 | 41 | return @{ $help_options }; 42 | } 43 | } 44 | 45 | sub option_in_usage { 46 | my ( $expected_option ) = @_; 47 | 48 | my @help_options = get_help_options(); 49 | my $found; 50 | 51 | foreach my $option ( @help_options ) { 52 | if ( $option eq $expected_option ) { 53 | $found = 1; 54 | last; 55 | } 56 | } 57 | 58 | ok( $found, "Option '$expected_option' found in --help output" ); 59 | 60 | return; 61 | } 62 | 63 | my @options = get_expected_options(); 64 | 65 | plan tests => scalar(@options); 66 | 67 | prep_environment(); 68 | 69 | foreach my $option ( @options ) { 70 | option_in_usage( $option ); 71 | } 72 | -------------------------------------------------------------------------------- /t/ack-i.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Straight -i 3 | args: 4 | - APPLE t/swamp/groceries/fruit t/swamp/groceries/junk t/swamp/groceries/meat -i 5 | - APPLE t/swamp/groceries/fruit t/swamp/groceries/junk t/swamp/groceries/meat --ignore-case 6 | ordered: true 7 | stdout: | 8 | t/swamp/groceries/fruit:1:apple 9 | t/swamp/groceries/junk:1:apple fritters 10 | 11 | --- 12 | name: No -i or -I 13 | args: 14 | - foo 15 | - foo -I 16 | - foo --no-smart-case 17 | ordered: true 18 | stdin: | 19 | Football 20 | foo bar 21 | foOtball 22 | bluhblah 23 | football 24 | stdout: | 25 | foo bar 26 | football 27 | 28 | --- 29 | name: Normal -i 30 | args: 31 | - foo -i 32 | - foo --ignore-case 33 | - Foo -i 34 | - Foo --ignore-case 35 | ordered: true 36 | stdin: | 37 | Football 38 | foo bar 39 | pickles 40 | foOtball 41 | bluhblah 42 | football 43 | stdout: | 44 | Football 45 | foo bar 46 | foOtball 47 | football 48 | 49 | --- 50 | name: Smartcase invoked 51 | args: 52 | - foo --smart-case 53 | - foo -S 54 | ordered: true 55 | stdin: | 56 | football 57 | foo bar 58 | Football 59 | foOtball 60 | stdout: | 61 | football 62 | foo bar 63 | Football 64 | foOtball 65 | 66 | --- 67 | name: Smartcase invoked because of the case of the search string 68 | args: 69 | - Foo --smart-case 70 | - Foo -S 71 | ordered: true 72 | stdin: | 73 | football 74 | foo bar 75 | Football 76 | foOtball 77 | stdout: | 78 | Football 79 | 80 | --- 81 | name: -i overrides --smart-case 82 | args: 83 | - Foo --smart-case -i 84 | - foo --smart-case -i 85 | - Foo -S -i 86 | - foo -S -i 87 | ordered: true 88 | stdin: | 89 | football 90 | foo bar 91 | Football 92 | foOtball 93 | stdout: | 94 | football 95 | foo bar 96 | Football 97 | foOtball 98 | 99 | --- 100 | name: -I overrides -i 101 | args: 102 | - Foo -i -I 103 | - Foo --ignore-case -I 104 | ordered: true 105 | stdin: | 106 | Football 107 | football 108 | foo bar 109 | foOtball 110 | stdout: | 111 | Football 112 | 113 | --- 114 | name: -I overrides --smart-case 115 | args: 116 | - Foo --smart-case -I 117 | - Foo -S -I 118 | ordered: true 119 | stdin: | 120 | Football 121 | football 122 | foo bar 123 | foOtball 124 | stdout: | 125 | Football 126 | -------------------------------------------------------------------------------- /t/ack-ignore-file.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More tests => 2; 7 | 8 | use lib 't'; 9 | use Util; 10 | 11 | prep_environment(); 12 | 13 | my @all = qw( 14 | t/swamp/groceries/another_subdir/fruit 15 | t/swamp/groceries/another_subdir/junk 16 | t/swamp/groceries/another_subdir/meat 17 | t/swamp/groceries/dir.d/fruit 18 | t/swamp/groceries/dir.d/junk 19 | t/swamp/groceries/dir.d/meat 20 | t/swamp/groceries/fruit 21 | t/swamp/groceries/junk 22 | t/swamp/groceries/meat 23 | t/swamp/groceries/subdir/fruit 24 | t/swamp/groceries/subdir/junk 25 | t/swamp/groceries/subdir/meat 26 | ); 27 | my @meat = grep { /meat/ } @all; 28 | my @junk = grep { /junk/ } @all; 29 | 30 | 31 | subtest 'is:xxx matching' => sub { 32 | plan tests => 5; 33 | 34 | ack_sets_match( 35 | [qw( -f t/swamp/groceries )], 36 | [ @all ], 37 | 'Unfiltered' 38 | ); 39 | 40 | ack_sets_match( 41 | [qw( -f t/swamp/groceries --ignore-file=is:fruit )], 42 | [ @meat, @junk ], 43 | 'Ignoring fruit with is' 44 | ); 45 | 46 | ack_sets_match( 47 | [qw( -f t/swamp/groceries --ignore-file=is:bongo )], 48 | [ @all ], 49 | 'Ignoring with is that does not match' 50 | ); 51 | 52 | ack_sets_match( 53 | [qw( -f t/swamp/groceries --ignore-file=is:subdir )], 54 | [ @all ], 55 | '--ignore-file only operatoes on filenames, not dirnames' 56 | ); 57 | ack_sets_match( 58 | [qw( -f t/swamp/groceries --ignore-file=is:fruit --ignore-file=is:junk )], 59 | [ @meat ], 60 | 'Multiple is arguments' 61 | ); 62 | }; 63 | 64 | 65 | subtest 'Invalid invocation' => sub { 66 | plan tests => 8; 67 | 68 | my @bad_args = ( 69 | '--ignore-file=foo', 70 | '--ignore-file=foo:bar', 71 | ); 72 | 73 | for my $bad_arg ( @bad_args ) { 74 | my ( $man_output, $man_stderr ) = run_ack_with_stderr( $bad_arg ); 75 | 76 | is_empty_array( $man_output, "No output for $bad_arg" ); 77 | is( scalar @{$man_stderr}, 2, "Exactly two errors for $bad_arg" ); 78 | like( $man_stderr->[0], qr/ack(?:-standalone)?: Unknown filter type 'foo'. Type must be one of: ext, firstlinematch, is, match./ ); 79 | like( $man_stderr->[1], qr/ack(?:-standalone)?: Invalid option on command line/ ); 80 | } 81 | }; 82 | 83 | 84 | done_testing(); 85 | exit 0; 86 | -------------------------------------------------------------------------------- /t/ack-k.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: No restrictions on type 3 | args: -f --show-types t/etc 4 | stdout: | 5 | t/etc/buttonhook.xml.xxx => xml 6 | t/etc/shebang.empty.xxx => 7 | t/etc/shebang.foobar.xxx => 8 | t/etc/shebang.php.xxx => php 9 | t/etc/shebang.pl.xxx => perl 10 | t/etc/shebang.py.xxx => python 11 | t/etc/shebang.rb.xxx => ruby 12 | t/etc/shebang.sh.xxx => shell 13 | 14 | --- 15 | name: Only known types 16 | args: -f -k --show-types t/etc 17 | stdout: | 18 | t/etc/buttonhook.xml.xxx => xml 19 | t/etc/shebang.php.xxx => php 20 | t/etc/shebang.pl.xxx => perl 21 | t/etc/shebang.py.xxx => python 22 | t/etc/shebang.rb.xxx => ruby 23 | t/etc/shebang.sh.xxx => shell 24 | 25 | --- 26 | name: -k and -f 27 | args: 28 | - t/swamp -f -k 29 | - t/swamp -f --known-types 30 | stdout: | 31 | t/swamp/0 32 | t/swamp/constitution-100k.pl 33 | t/swamp/Rakefile 34 | t/swamp/options-crlf.pl 35 | t/swamp/options.pl 36 | t/swamp/javascript.js 37 | t/swamp/html.html 38 | t/swamp/perl-without-extension 39 | t/swamp/sample.rake 40 | t/swamp/perl.cgi 41 | t/swamp/Makefile 42 | t/swamp/pipe-stress-freaks.F 43 | t/swamp/perl.pod 44 | t/swamp/html.htm 45 | t/swamp/perl-test.t 46 | t/swamp/perl.handler.pod 47 | t/swamp/perl.pl 48 | t/swamp/Makefile.PL 49 | t/swamp/MasterPage.master 50 | t/swamp/c-source.c 51 | t/swamp/perl.pm 52 | t/swamp/c-header.h 53 | t/swamp/crystallography-weenies.f 54 | t/swamp/CMakeLists.txt 55 | t/swamp/Sample.ascx 56 | t/swamp/Sample.asmx 57 | t/swamp/sample.asp 58 | t/swamp/sample.aspx 59 | t/swamp/service.svc 60 | t/swamp/stuff.cmake 61 | t/swamp/example.R 62 | t/swamp/fresh.css 63 | t/swamp/lua-shebang-test 64 | t/swamp/notes.md 65 | t/swamp/test.py 66 | t/swamp/test_foo.py 67 | t/swamp/foo_test.py 68 | 69 | --- 70 | name: -k and negated --type specifiers 71 | args: 72 | - -f -T perl t/swamp -k 73 | - -f -T perl t/swamp --known-types 74 | - -f --noperl t/swamp -k 75 | - -f --noperl t/swamp --known-types 76 | stdout: | 77 | t/swamp/Rakefile 78 | t/swamp/javascript.js 79 | t/swamp/html.html 80 | t/swamp/sample.rake 81 | t/swamp/Makefile 82 | t/swamp/MasterPage.master 83 | t/swamp/pipe-stress-freaks.F 84 | t/swamp/html.htm 85 | t/swamp/c-source.c 86 | t/swamp/c-header.h 87 | t/swamp/crystallography-weenies.f 88 | t/swamp/CMakeLists.txt 89 | t/swamp/Sample.ascx 90 | t/swamp/Sample.asmx 91 | t/swamp/sample.asp 92 | t/swamp/sample.aspx 93 | t/swamp/service.svc 94 | t/swamp/stuff.cmake 95 | t/swamp/example.R 96 | t/swamp/fresh.css 97 | t/swamp/lua-shebang-test 98 | t/swamp/notes.md 99 | t/swamp/test.py 100 | t/swamp/test_foo.py 101 | t/swamp/foo_test.py 102 | -------------------------------------------------------------------------------- /t/ack-l.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Simple -l 3 | args: 4 | - strict t/text -l 5 | - strict t/text --files-with-matches 6 | stdout: | 7 | t/text/bill-of-rights.txt 8 | t/text/constitution.txt 9 | 10 | --- 11 | name: Simple -L 12 | args: 13 | - strict t/text -L 14 | - strict t/text --files-without-matches 15 | stdout: | 16 | t/text/amontillado.txt 17 | t/text/gettysburg.txt 18 | t/text/movies.txt 19 | t/text/number.txt 20 | t/text/numbered-text.txt 21 | t/text/ozymandias.txt 22 | t/text/raven.txt 23 | 24 | --- 25 | name: -l with inverted match 26 | args: 27 | - religion t/text -l -v 28 | - religion t/text -l --invert-match 29 | - religion t/text --files-with-matches -v 30 | - religion t/text --files-with-matches --invert-match 31 | stdout: | 32 | t/text/amontillado.txt 33 | t/text/bill-of-rights.txt 34 | t/text/constitution.txt 35 | t/text/gettysburg.txt 36 | t/text/movies.txt 37 | t/text/number.txt 38 | t/text/numbered-text.txt 39 | t/text/ozymandias.txt 40 | t/text/raven.txt 41 | -------------------------------------------------------------------------------- /t/ack-m.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: -m 3 | args: -m 3 -w the t/text/bill-of-rights.txt t/text/constitution.txt 4 | stdout: | 5 | t/text/bill-of-rights.txt:4:or prohibiting the free exercise thereof; or abridging the freedom of 6 | t/text/bill-of-rights.txt:5:speech, or of the press; or the right of the people peaceably to assemble, 7 | t/text/bill-of-rights.txt:6:and to petition the Government for a redress of grievances. 8 | t/text/constitution.txt:3:We the People of the United States, in Order to form a more perfect 9 | t/text/constitution.txt:4:Union, establish Justice, insure domestic Tranquility, provide for the 10 | t/text/constitution.txt:5:common defense, promote the general Welfare, and secure the Blessings 11 | 12 | --- 13 | name: -1 14 | args: -1 -w the t/text/bill-of-rights.txt t/text/constitution.txt 15 | stdout: | 16 | t/text/bill-of-rights.txt:4:or prohibiting the free exercise thereof; or abridging the freedom of 17 | 18 | --- 19 | name: -m with -l 20 | args: -m 3 -l -w --sort the t/text/ 21 | stdout: | 22 | t/text/amontillado.txt 23 | t/text/bill-of-rights.txt 24 | t/text/constitution.txt 25 | -------------------------------------------------------------------------------- /t/ack-man.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | use 5.010001; 6 | 7 | use lib 't'; 8 | use Util; 9 | 10 | use Test::More; 11 | 12 | plan tests => 2; 13 | 14 | prep_environment(); 15 | 16 | my ($stdout, $stderr) = run_ack_with_stderr( '--man' ); 17 | 18 | my @lines = @{$stdout}; 19 | my $nlines = scalar @lines; 20 | my $ok = ($nlines > 900) && ($nlines < 2000); 21 | 22 | diag $nlines; 23 | if ( !ok( $ok, "Manual should be between 900-2000 lines long but is actually $nlines long" ) ) { 24 | my @first = grep { defined } @lines[0..19]; 25 | diag( '--man start=', explain( \@first ) ); 26 | 27 | 28 | my @last = grep { defined } @lines[-20..-1]; 29 | diag( '--man end=', explain( \@last ) ); 30 | } 31 | 32 | my $filtered_stderr = filter_out_perldoc_noise( $stderr ); 33 | is_empty_array( $filtered_stderr, 'Nothing in STDERR' ); 34 | 35 | done_testing(); 36 | 37 | exit 0; 38 | -------------------------------------------------------------------------------- /t/ack-match.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More; 7 | use lib 't'; 8 | use Util; 9 | 10 | prep_environment(); 11 | 12 | my @tests = ( 13 | [ qw/Sue/ ], 14 | [ qw/boy -i/ ], # case-insensitive is handled correctly with --match 15 | [ qw/ll+ -Q/ ], # quotemeta is handled correctly with --match 16 | [ qw/gon -w/ ], # words is handled correctly with --match 17 | ); 18 | 19 | plan tests => @tests + 2; 20 | 21 | test_match( @{$_} ) for @tests; 22 | 23 | # Giving only the --match argument (and no other args) should not result in an error. 24 | run_ack( '--match', 'Sue' ); 25 | 26 | subtest 'Not giving a regex when piping into ack should result in an error' => sub { 27 | plan tests => 4; 28 | 29 | # Not giving a regex when piping into ack should result in an error. 30 | my ($stdout, $stderr) = pipe_into_ack_with_stderr( 't/text/amontillado.txt', '-t', 'perl' ); 31 | isnt( get_rc(), 0, 'ack should return an error when piped into without a regex' ); 32 | is_empty_array( $stdout, 'ack should return no STDOUT when piped into without a regex' ); 33 | cmp_ok( scalar @{$stderr}, '>', 0, 'Has to have at least one line of error message, but could have more under Appveyor' ); 34 | 35 | my $name = $ENV{ACK_TEST_STANDALONE} ? 'ack-standalone' : 'ack'; 36 | is( $stderr->[0], "$name: No regular expression found.", 'Error message matches' ); 37 | }; 38 | 39 | done_testing(); 40 | 41 | exit 0; 42 | 43 | # Call ack normally and compare output to calling with --match regex. 44 | # 45 | # Due to 2 calls to run_ack, this sub runs altogether 3 tests. 46 | sub test_match { 47 | local $Test::Builder::Level = $Test::Builder::Level + 1; 48 | 49 | my $regex = shift; 50 | my @args = @_; 51 | push @args, '--sort-files'; 52 | 53 | return subtest subtest_name( @args ) => sub { 54 | my @files = ( 't/text' ); 55 | my @results_normal = run_ack( @args, $regex, @files ); 56 | my @results_match = run_ack( @args, @files, '--match', $regex ); 57 | 58 | return sets_match( \@results_normal, \@results_match, "Same output for regex '$regex'." ); 59 | }; 60 | } 61 | -------------------------------------------------------------------------------- /t/ack-n.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More tests => 10; 7 | use lib 't'; 8 | use Util; 9 | 10 | my @expected_norecurse = line_split( <<'END' ); 11 | t/swamp/groceries/fruit:1:apple 12 | t/swamp/groceries/junk:1:apple fritters 13 | END 14 | 15 | my @expected_recurse = line_split( <<'END' ); 16 | t/swamp/groceries/another_subdir/fruit:1:apple 17 | t/swamp/groceries/another_subdir/junk:1:apple fritters 18 | t/swamp/groceries/dir.d/fruit:1:apple 19 | t/swamp/groceries/dir.d/junk:1:apple fritters 20 | t/swamp/groceries/fruit:1:apple 21 | t/swamp/groceries/junk:1:apple fritters 22 | t/swamp/groceries/subdir/fruit:1:apple 23 | t/swamp/groceries/subdir/junk:1:apple fritters 24 | END 25 | 26 | if ( is_windows() ) { 27 | s{/}{\\}g for ( @expected_norecurse, @expected_recurse ); 28 | } 29 | 30 | my @args; 31 | my @lines; 32 | 33 | prep_environment(); 34 | 35 | # We sort to ensure deterministic results. 36 | @args = qw( -n --sort-files apple t/swamp/groceries ); 37 | @lines = run_ack(@args); 38 | lists_match( \@lines, \@expected_norecurse, '-n should disable recursion' ); 39 | 40 | @args = qw( --no-recurse --sort-files apple t/swamp/groceries ); 41 | @lines = run_ack(@args); 42 | lists_match( \@lines, \@expected_norecurse, '--no-recurse should disable recursion' ); 43 | 44 | # Make sure that re-enabling recursion works. 45 | @args = qw( -n -r --sort-files apple t/swamp/groceries ); 46 | @lines = run_ack(@args); 47 | lists_match( \@lines, \@expected_recurse, '-r after -n should re-enable recursion' ); 48 | 49 | @args = qw( --no-recurse -R --sort-files apple t/swamp/groceries ); 50 | @lines = run_ack(@args); 51 | lists_match( \@lines, \@expected_recurse, '-R after --no-recurse should re-enable recursion' ); 52 | 53 | @args = qw( --no-recurse --recurse --sort-files apple t/swamp/groceries ); 54 | @lines = run_ack(@args); 55 | lists_match( \@lines, \@expected_recurse, '--recurse after --no-recurse should re-enable recursion' ); 56 | 57 | done_testing(); 58 | 59 | exit 0; 60 | -------------------------------------------------------------------------------- /t/ack-o.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: No -o 3 | args: 'the\s+\S+ t/text/gettysburg.txt' 4 | ordered: true 5 | stdout: | 6 | but it can never forget what they did here. It is for us the living, 7 | rather, to be dedicated here to the unfinished work which they who 8 | here dedicated to the great task remaining before us -- that from these 9 | the last full measure of devotion -- that we here highly resolve that 10 | shall have a new birth of freedom -- and that government of the people, 11 | by the people, for the people, shall not perish from the earth. 12 | 13 | --- 14 | name: With -o 15 | args: 'the\s+\S+ t/text/gettysburg.txt -o' 16 | ordered: true 17 | stdout: | 18 | the living, 19 | the unfinished 20 | the great 21 | the last 22 | the people, 23 | the people, 24 | the people, 25 | the earth. 26 | -------------------------------------------------------------------------------- /t/ack-print0.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use Test::More tests => 15; 7 | 8 | use lib 't'; 9 | use Util; 10 | 11 | prep_environment(); 12 | 13 | G_NO_PRINT0: { 14 | my @expected = qw( 15 | t/text/amontillado.txt 16 | t/text/bill-of-rights.txt 17 | t/text/constitution.txt 18 | t/text/movies.txt 19 | t/text/ozymandias.txt 20 | ); 21 | 22 | my $filename_regex = 'i'; 23 | my @files = qw( t/text/ ); 24 | my @args = ( '-g', $filename_regex ); 25 | my @results = run_ack( @args, @files ); 26 | 27 | sets_match( \@results, \@expected, 'Files found with -g and without --print0' ); 28 | is_empty_array( [ grep { /\0/ } @results ], ' ... no null byte in output' ); 29 | } 30 | 31 | G_PRINT0: { 32 | my $expected = join( "\0", map { reslash($_) } qw( 33 | t/text/amontillado.txt 34 | t/text/bill-of-rights.txt 35 | t/text/constitution.txt 36 | t/text/movies.txt 37 | t/text/ozymandias.txt 38 | ) ) . "\0"; # string of filenames separated and concluded with null byte 39 | 40 | my $filename_regex = 'i'; 41 | my @files = qw( t/text ); 42 | my @args = ( '-g', $filename_regex, '--sort-files', '--print0' ); 43 | my @results = run_ack( @args, @files ); 44 | 45 | is_deeply( \@results, [$expected], 'Files found with -g and with --print0' ); 46 | } 47 | 48 | F_PRINT0: { 49 | my @files = qw( t/text/ ); 50 | my @args = qw( -f --print0 ); 51 | my @results = run_ack( @args, @files ); 52 | 53 | # Checking for exact files is fragile, so just see whether we have \0 in output. 54 | is( scalar @results, 1, 'Only one line of output with -f and --print0' ); 55 | is_nonempty_array( [ grep { /\0/ } @results ], ' ... and null bytes in output' ); 56 | } 57 | 58 | L_PRINT0: { 59 | my $regex = 'of'; 60 | my @files = qw( t/text/ ); 61 | my @args = ( '-l', '--print0', $regex ); 62 | my @results = run_ack( @args, @files ); 63 | 64 | # Checking for exact files is fragile, so just see whether we have \0 in output. 65 | is( scalar @results, 1, 'Only one line of output with -l and --print0' ); 66 | is_nonempty_array( [ grep { /\0/ } @results ], ' ... and null bytes in output' ); 67 | } 68 | 69 | COUNT_PRINT0: { 70 | my $regex = 'of'; 71 | my @files = qw( t/text/ ); 72 | my @args = ( '--count', '--print0', $regex ); 73 | my @results = run_ack( @args, @files ); 74 | 75 | # Checking for exact files is fragile, so just see whether we have \0 in output. 76 | is( scalar @results, 1, 'Only one line of output with --count and --print0' ); 77 | is_nonempty_array( [ grep { /\0/ } @results ], ' ... and null bytes in output' ); 78 | is_nonempty_array( [ grep { /:\d+/ } @results ], ' ... and ":\d+" in output, so the counting also works' ); 79 | } 80 | -------------------------------------------------------------------------------- /t/ack-s.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More tests => 1; 7 | use lib 't'; 8 | use Util; 9 | use File::Temp; 10 | 11 | prep_environment(); 12 | 13 | RESTRICTED_DIRECTORIES: { 14 | my @args = qw( hello -s ); 15 | 16 | my $dir = File::Temp->newdir; 17 | my $wd = getcwd_clean(); 18 | 19 | safe_chdir( $dir->dirname ); 20 | 21 | safe_mkdir( 'foo' ); 22 | write_file( 'foo/bar' => "hello\n" ); 23 | write_file( 'baz' => "hello\n" ); 24 | 25 | chmod 0000, 'foo'; 26 | chmod 0000, 'baz'; 27 | 28 | my (undef, $stderr) = run_ack_with_stderr( @args ); 29 | 30 | is_empty_array( $stderr, 'Nothing in stderr' ); 31 | 32 | safe_chdir( $wd ); 33 | } 34 | 35 | done_testing(); 36 | exit 0; 37 | -------------------------------------------------------------------------------- /t/ack-s.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Errors without -s 3 | args: search-term non-existent-file.txt 4 | exitcode: 1 5 | stdout: 6 | stderr: | 7 | ack: non-existent-file.txt: No such file or directory 8 | 9 | --- 10 | name: Errors with -s 11 | args: -s search-term non-existent-file.txt 12 | exitcode: 1 13 | stdout: 14 | stderr: 15 | -------------------------------------------------------------------------------- /t/ack-show-types.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use Test::More tests => 10; 7 | 8 | use lib 't'; 9 | use Util; 10 | 11 | prep_environment(); 12 | 13 | RUBY_AND_RAKE: { 14 | do_ruby_test( qw( -f --show-types t/swamp/Rakefile ) ); 15 | do_ruby_test( qw( -g \bRakef --show-types t/swamp ) ); 16 | } 17 | 18 | 19 | REQUIRE_F_OR_G: { 20 | my ( $stdout, $stderr ) = run_ack_with_stderr('--show-types'); 21 | is_empty_array($stdout, 'No output'); 22 | is(scalar(@{$stderr}), 1, 'A single line should be present on standard error'); 23 | like($stderr->[0], qr/--show-types can only be used with -f or -g./, 'Right error message' ); 24 | is(get_rc(), 255, 'The ack command should not fail'); 25 | } 26 | 27 | 28 | exit 0; 29 | 30 | 31 | sub do_ruby_test { 32 | local $Test::Builder::Level = $Test::Builder::Level + 1; 33 | 34 | my @args = @_; 35 | 36 | my @results = run_ack( @args ); 37 | 38 | is( scalar @results, 1, "Only one file should be returned from 'ack @args'" ); 39 | sets_match( get_types( $results[0] ), [qw( ruby rake )], "'ack @args' must return all the expected types" ); 40 | 41 | return; 42 | } 43 | 44 | 45 | sub get_types { 46 | my $line = shift; 47 | $line =~ s/.* => //; 48 | 49 | my @types = split( /,/, $line ); 50 | 51 | return \@types; 52 | } 53 | -------------------------------------------------------------------------------- /t/ack-type-del.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More tests => 12; 7 | use lib 't'; 8 | use Util; 9 | 10 | prep_environment(); 11 | 12 | my ( $stdout, $stderr ); 13 | my $help_types_output; 14 | 15 | # sanity check 16 | ( $stdout, $stderr ) = run_ack_with_stderr('-t', 'perl', '-f', 't/swamp'); 17 | is( scalar(@{$stdout}), 12, 'Found initial 11 files' ); 18 | is_empty_array( $stderr, 'Nothing in stderr' ); 19 | 20 | ( $stdout, $stderr ) = run_ack_with_stderr('--type-del=perl', '--type-del=perltest', '-t', 'perl', '-f', 't/swamp'); 21 | is_empty_array( $stdout, 'Nothing in stdout' ); 22 | first_line_like( $stderr, qr/Unknown type 'perl'/ ); 23 | 24 | ( $stdout, $stderr ) = run_ack_with_stderr('--type-del=perl', '--type-del=perltest', '--type-add=perl:ext:pm', '-t', 'perl', '-f', 't/swamp'); 25 | is( scalar(@{$stdout}), 1, 'Got one output line' ); 26 | is_empty_array( $stderr, 'Nothing in stderr' ); 27 | 28 | # more sanity checking 29 | $help_types_output = run_ack( '--help-types' ); 30 | like( $help_types_output, qr/\Qperl/ ); 31 | 32 | $help_types_output = run_ack( '--type-del=perl', '--type-del=perltest', '--help-types' ); 33 | unlike( $help_types_output, qr/\Qperl/ ); 34 | 35 | DUMP: { 36 | my @dump_output = run_ack( '--type-del=perl', '--type-del=perltest', '--dump' ); 37 | # discard everything up to the ARGV section 38 | while ( @dump_output && $dump_output[0] ne 'ARGV' ) { 39 | shift @dump_output; 40 | } 41 | shift @dump_output; # discard ARGV 42 | shift @dump_output; # discard header 43 | foreach my $line (@dump_output) { 44 | $line =~ s/^\s+|\s+$//g; 45 | } 46 | lists_match( \@dump_output, ['--type-del=perl', '--type-del=perltest'], '--type-del should show up in --dump output' ); 47 | } 48 | -------------------------------------------------------------------------------- /t/ack-v.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Normal case 3 | args: -v w t/text/ozymandias.txt 4 | ordered: true 5 | stdout: | 6 | I met a traveller from an antique land 7 | Stand in the desert... Near them, on the sand, 8 | Which yet survive, stamped on these lifeless things, 9 | The hand that mocked them, and the heart that fed: 10 | 'My name is Ozymandias, king of kings: 11 | Nothing beside remains. Round the decay 12 | 13 | --- 14 | name: Ignore case 15 | args: -i -v w t/text/ozymandias.txt 16 | ordered: true 17 | stdout: | 18 | I met a traveller from an antique land 19 | Stand in the desert... Near them, on the sand, 20 | The hand that mocked them, and the heart that fed: 21 | 'My name is Ozymandias, king of kings: 22 | Nothing beside remains. Round the decay 23 | 24 | --- 25 | name: With not 26 | args: -i -v the --not them t/text/ozymandias.txt 27 | ordered: true 28 | stdout: | 29 | I met a traveller from an antique land 30 | Who said: Two vast and trunkless legs of stone 31 | Stand in the desert... Near them, on the sand, 32 | Half sunk, a shattered visage lies, whose frown, 33 | And wrinkled lip, and sneer of cold command, 34 | Tell that its sculptor well those passions read 35 | The hand that mocked them, and the heart that fed: 36 | 'My name is Ozymandias, king of kings: 37 | Look on my works, ye Mighty, and despair!' 38 | Of that colossal wreck, boundless and bare 39 | -------------------------------------------------------------------------------- /t/ack-version.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More tests => 2; 7 | use lib 't'; 8 | use Util; 9 | 10 | use App::Ack; 11 | 12 | prep_environment(); 13 | 14 | my ( $stdout, $stderr ) = run_ack_with_stderr( '--version' ); 15 | 16 | is_empty_array( $stderr, 'Nothing in stderr' ); 17 | my @lines = @{$stdout}; 18 | like( $lines[0], qr/\Q$App::Ack::VERSION/, 'Found the version in the first line' ); 19 | 20 | done_testing(); 21 | 22 | exit 0; 23 | -------------------------------------------------------------------------------- /t/ack-w.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Straight -w 3 | args: -w foo 4 | ordered: true 5 | stdin: | 6 | foo 7 | foo bar 8 | foobar 9 | foot 10 | underfoot 11 | football 12 | foo-foo football 13 | End of the line foo 14 | I pity da foo'. 15 | stdout: | 16 | foo 17 | foo bar 18 | foo-foo football 19 | End of the line foo 20 | I pity da foo'. 21 | 22 | --- 23 | name: -w and optional character 24 | args: 25 | - -w foot? 26 | - -w (foot?) 27 | - "-w (?:foot?)" 28 | ordered: true 29 | stdin: | 30 | foo 31 | foot 32 | Trampled underfoot 33 | foobarf 34 | foo-bar 35 | foo-bart 36 | Our ten-foot foo-bird is foobar. 37 | football 38 | stdout: | 39 | foo 40 | foot 41 | foo-bar 42 | foo-bart 43 | Our ten-foot foo-bird is foobar. 44 | 45 | --- 46 | name: -w and optional character, highlighting 47 | args: -w foot? --underline 48 | ordered: true 49 | stdin: | 50 | Trampled underfoot 51 | foobarf 52 | Our ten-foot foo-bird is foobar. 53 | football 54 | stdout: | 55 | Our ten-foot foo-bird is foobar. 56 | ^^^^ ^^^ 57 | 58 | --- 59 | name: -w and optional group 60 | args: 61 | - -w foo(bar)? 62 | ordered: true 63 | stdin: | 64 | foo 65 | foobar 66 | foo-bar 67 | foo-bart 68 | Trampled underfoot 69 | foobarf 70 | stdout: | 71 | foo 72 | foobar 73 | foo-bar 74 | foo-bart 75 | 76 | --- 77 | name: -w and alternation 78 | args: 79 | - -w foo|bar 80 | - -w (foo|bar) 81 | ordered: true 82 | stdin: | 83 | bar 84 | barometric 85 | foo 86 | fubar 87 | schmfoo 88 | schmofool 89 | subarometric 90 | stdout: | 91 | bar 92 | foo 93 | 94 | --- 95 | name: -w and function definition 96 | args: 97 | - -w (set|get)_user_(name|perm) 98 | - -w ((set|get)_user_(name|perm)) 99 | - "-w (?:(?:set|get)_user_(?:name|perm))" 100 | - "-w (?:(set|get)_user_(name|perm))" 101 | - "-w ((?:set|get)_user_(?:name|perm))" 102 | ordered: true 103 | stdin: | 104 | get_user_perm 105 | get_user_permission 106 | reset_user_name 107 | set_user_name 108 | stdout: | 109 | get_user_perm 110 | set_user_name 111 | 112 | --- 113 | name: single-letter words 114 | args: 115 | - -w \w 116 | ordered: true 117 | stdin: | 118 | a 119 | b 120 | c 121 | !e! 122 | no 123 | dd 124 | xxx 125 | stdout: | 126 | a 127 | b 128 | c 129 | !e! 130 | 131 | --- 132 | name: -w with trailing metacharacters 133 | args: 134 | - Milit\w\w -w -h --sort-files t/text/bill-of-rights.txt 135 | - Milit.. -w -h --sort-files t/text/bill-of-rights.txt 136 | ordered: true 137 | stdout: | 138 | A well regulated Militia, being necessary to the security of a free State, 139 | cases arising in the land or naval forces, or in the Militia, when in 140 | 141 | --- 142 | name: Begins and ends with word char 143 | args: Militia -w t/text/bill-of-rights.txt 144 | ordered: true 145 | stdout: | 146 | A well regulated Militia, being necessary to the security of a free State, 147 | cases arising in the land or naval forces, or in the Militia, when in 148 | 149 | --- 150 | name: Begins/ends with grouping parens. First/last char of regex is not a word, disabling the word boundary check at the end of the match. 151 | args: 152 | - Militia() -w t/text/bill-of-rights.txt 153 | - ()Militia -w t/text/bill-of-rights.txt 154 | - (Militia) -w t/text/bill-of-rights.txt 155 | ordered: true 156 | stdout: | 157 | A well regulated Militia, being necessary to the security of a free State, 158 | cases arising in the land or naval forces, or in the Militia, when in 159 | 160 | --- 161 | name: Alternating numbers 162 | args: 650|660|670|680 -w t/text/number.txt 163 | exitcode: 1 164 | stdout: "" 165 | -------------------------------------------------------------------------------- /t/anchored.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Front anchored 3 | args: -h -i ^congress t/text/constitution.txt 4 | stdout: | 5 | Congress prior to the Year one thousand eight hundred and eight, but 6 | 7 | --- 8 | name: Back anchored 9 | args: -h -i congress$ t/text/constitution.txt 10 | ordered: true 11 | stdout: | 12 | All legislative Powers herein granted shall be vested in a Congress 13 | Fact, with such Exceptions, and under such Regulations as the Congress 14 | Records, and judicial Proceedings of every other State. And the Congress 15 | 16 | --- 17 | name: Unanchored 18 | args: -h -i congress t/text/constitution.txt 19 | ordered: true 20 | stdout: | 21 | All legislative Powers herein granted shall be vested in a Congress 22 | the first Meeting of the Congress of the United States, and within 23 | thereof; but the Congress may at any time by Law make or alter such 24 | The Congress shall assemble at least once in every Year, and such 25 | Neither House, during the Session of Congress, shall, without the Consent 26 | a Law, in like Manner as if he had signed it, unless the Congress by 27 | The Congress shall have Power To lay and collect Taxes, Duties, Imposts 28 | the discipline prescribed by Congress; 29 | particular States, and the Acceptance of Congress, become the Seat of 30 | Congress prior to the Year one thousand eight hundred and eight, but 31 | the Consent of the Congress, accept of any present, Emolument, Office, 32 | No State shall, without the Consent of the Congress, lay any Imposts or 33 | subject to the Revision and Control of the Congress. 34 | No State shall, without the Consent of Congress, lay any Duty of Tonnage, 35 | and Representatives to which the State may be entitled in the Congress: 36 | The Congress may determine the Time of chusing the Electors, and the 37 | Office, the Same shall devolve on the Vice President, and the Congress may 38 | be established by Law: but the Congress may by Law vest the Appointment 39 | He shall from time to time give to the Congress Information on the State 40 | Court, and in such inferior Courts as the Congress may from time to 41 | Fact, with such Exceptions, and under such Regulations as the Congress 42 | shall be at such Place or Places as the Congress may by Law have directed. 43 | The Congress shall have Power to declare the Punishment of Treason, but 44 | Records, and judicial Proceedings of every other State. And the Congress 45 | New States may be admitted by the Congress into this Union; but no new 46 | concerned as well as of the Congress. 47 | The Congress shall have Power to dispose of and make all needful Rules and 48 | The Congress, whenever two thirds of both Houses shall deem it necessary, 49 | Ratification may be proposed by the Congress; Provided that no Amendment 50 | -------------------------------------------------------------------------------- /t/bad-ackrc-opt.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | use lib 't'; 6 | 7 | use Util; 8 | use Test::More tests => 4; 9 | 10 | prep_environment(); 11 | 12 | my ( $stdout, $stderr ) = run_ack_with_stderr( '--noenv', '--ackrc=./bad-ackrc', 'the', 't/text' ); 13 | 14 | is_empty_array( $stdout, 'Nothing to stdout' ); 15 | is( @{$stderr}, 1, 'only one line to stderr' ); 16 | like( $stderr->[0], qr/Unable to load ackrc/, 'Got the right message' ); 17 | isnt( get_rc(), 0, 'Non-zero return code' ); 18 | 19 | done_testing(); 20 | 21 | exit 0; 22 | -------------------------------------------------------------------------------- /t/basic.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: No switches, one directory 3 | args: consecrated t/text 4 | stdout: | 5 | t/text/gettysburg.txt:14:struggled here, have consecrated it, far above our poor power to add or 6 | 7 | --- 8 | name: No switches, one file 9 | args: strict t/swamp/options.pl 10 | stdout: | 11 | use strict; 12 | 13 | --- 14 | name: No switches, multiple files 15 | args: strict t/text/constitution.txt t/swamp/pipe-stress-freaks.F t/swamp/options.pl 16 | stdout: | 17 | t/text/constitution.txt:225:such District (not exceeding ten Miles square) as may, by Cession of 18 | t/swamp/options.pl:2:use strict; 19 | 20 | --- 21 | name: With inclusion switch 22 | args: 23 | - strict -H t/swamp/options.pl 24 | - strict --with-filename t/swamp/options.pl 25 | stdout: | 26 | t/swamp/options.pl:2:use strict; 27 | 28 | --- 29 | name: With exclusion switch, multiple files 30 | args: 31 | - strict -h t/swamp/options.pl t/swamp/crystallography-weenies.f 32 | - strict --no-filename t/swamp/options.pl t/swamp/crystallography-weenies.f 33 | stdout: | 34 | use strict; 35 | -------------------------------------------------------------------------------- /t/boolean.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: No booleans 3 | args: -i nevermore t/text/raven.txt 4 | ordered: true 5 | indent-stdout: 4 6 | stdout: | 7 | Quoth the Raven, "Nevermore." 8 | With such name as "Nevermore." 9 | Then the bird said, "Nevermore." 10 | Of 'Never -- nevermore.' 11 | Meant in croaking "Nevermore." 12 | She shall press, ah, nevermore! 13 | Quoth the Raven, "Nevermore." 14 | Quoth the Raven, "Nevermore." 15 | Quoth the Raven, "Nevermore." 16 | Quoth the Raven, "Nevermore." 17 | Shall be lifted--nevermore! 18 | 19 | --- 20 | name: Nevermore not quoth 21 | args: -i nevermore --not quoth t/text/raven.txt 22 | ordered: true 23 | indent-stdout: 4 24 | stdout: | 25 | With such name as "Nevermore." 26 | Then the bird said, "Nevermore." 27 | Of 'Never -- nevermore.' 28 | Meant in croaking "Nevermore." 29 | She shall press, ah, nevermore! 30 | Shall be lifted--nevermore! 31 | 32 | --- 33 | name: Nevermore not quoth not the 34 | args: -i nevermore --not quoth --not the t/text/raven.txt 35 | ordered: true 36 | indent-stdout: 4 37 | stdout: | 38 | With such name as "Nevermore." 39 | Of 'Never -- nevermore.' 40 | Meant in croaking "Nevermore." 41 | She shall press, ah, nevermore! 42 | Shall be lifted--nevermore! 43 | 44 | --- 45 | name: Quoth not nevermore 46 | args: quoth --not nevermore -i t/text/raven.txt 47 | exitcode: 1 48 | stdout: "" 49 | -------------------------------------------------------------------------------- /t/build_regex.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use Test::More tests => 12; 7 | 8 | use App::Ack; 9 | 10 | use lib 't'; 11 | use Util; 12 | 13 | 14 | # Note: All of the expected strings have to be in the format before Perl 5.14. 15 | # The _check() function does the transformation. 16 | # Old style: (?m-xis:blah.*) 17 | # New style: (?^m:blah.*) 18 | PLAIN: { 19 | _check( 20 | 'foo', 21 | {}, 22 | '(?-xism:foo)', 23 | '(?m-xis:foo)', 24 | 'Nuthin fancy' 25 | ); 26 | _check( 27 | 'foo-bar', 28 | {}, 29 | '(?-xism:foo-bar)', 30 | '(?m-xis:foo-bar)', 31 | 'Not just a plain word' 32 | ); 33 | } 34 | 35 | 36 | SMARTCASE: { 37 | _check( 38 | 'foo', 39 | { S => 1 }, 40 | '(?-xism:(?i)foo)', 41 | '(?m-xis:(?i)foo)', 42 | 'Smartcase on a lowercase word' 43 | ); 44 | _check( 45 | 'Foo', 46 | { S => 1 }, 47 | '(?-xism:Foo)', 48 | '(?m-xis:Foo)', 49 | 'Smartcase on a mixed-case word' 50 | ); 51 | } 52 | 53 | 54 | DASH_Q: { 55 | _check( 56 | 'foo', 57 | { Q => 1 }, 58 | '(?-xism:foo)', 59 | '(?m-xis:foo)', 60 | 'Nothing for -Q to do' 61 | ); 62 | _check( 63 | 'thing^ and ($foo)', 64 | { Q => 1 }, 65 | '(?-xism:thing\^\ and\ \(\$foo\))', 66 | undef, # No scan regex when there are anchors 67 | '-Q has things to escape' 68 | ); 69 | } 70 | 71 | 72 | DASH_i: { 73 | _check( 74 | 'NeXT', 75 | { i => 1 }, 76 | '(?-xism:(?i)NeXT)', 77 | '(?m-xis:(?i)NeXT)', 78 | 'Simple -i' 79 | ); 80 | } 81 | 82 | 83 | DASH_w: { 84 | _check( 85 | 'wookie', 86 | { w => 1 }, 87 | '(?-xism:\b(?:wookie)\b)', 88 | '(?m-xis:wookie)', 89 | 'Simple -w' 90 | ); 91 | _check( 92 | 'wookie-boogie', 93 | { w => 1 }, 94 | '(?-xism:(?:^|\b|\s)\K(?:wookie-boogie)(?=\s|\b|$))', 95 | '(?m-xis:wookie-boogie)', 96 | 'Not just a single word' 97 | ); 98 | _check( 99 | 'blah.*', 100 | { w => 1 }, 101 | '(?-xism:(?:^|\b|\s)\K(?:blah.*)(?=\s|\b|$))', 102 | '(?m-xis:blah.*)', 103 | '-w on something ending with metacharacters' 104 | ); 105 | _check( 106 | '[abc]thing', 107 | { w => 1 }, 108 | '(?-xism:(?:^|\b|\s)\K(?:[abc]thing)(?=\s|\b|$))', 109 | '(?m-xis:[abc]thing)', 110 | '-w on something beginning with a range' 111 | ); 112 | _check( 113 | '[abc]thing.+?', 114 | { w => 1 }, 115 | '(?-xism:(?:^|\b|\s)\K(?:[abc]thing.+?)(?=\s|\b|$))', 116 | '(?m-xis:[abc]thing.+?)', 117 | '-w on something beginning with a range and ending with metacharacters' 118 | ); 119 | } 120 | 121 | 122 | exit 0; 123 | 124 | 125 | sub _check { 126 | local $Test::Builder::Level = $Test::Builder::Level + 1; 127 | 128 | my $str = shift; 129 | my $opt = shift; 130 | my $exp_match = shift; 131 | my $exp_scan = shift; 132 | my $msg = shift or die 'Must provide a message'; 133 | 134 | return subtest $msg => sub () { 135 | $opt = { %{$opt} }; 136 | 137 | $opt->{$_} //= [] for qw( and or not ); 138 | 139 | my ($match, $scan) = App::Ack::build_regex( $str, $opt ); 140 | 141 | regex_eq( $match, $exp_match, 'match matches' ); 142 | regex_eq( $scan, $exp_scan, 'scan matches' ); 143 | }; 144 | } 145 | -------------------------------------------------------------------------------- /t/command-line-files.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | # This file validates behaviors of specifying files on the command line. 4 | 5 | use warnings; 6 | use strict; 7 | 8 | use Test::More tests => 4; 9 | 10 | use lib 't'; 11 | use Util; 12 | 13 | prep_environment(); 14 | 15 | my @source_files = map { reslash($_) } qw( 16 | t/swamp/options-crlf.pl 17 | t/swamp/options.pl 18 | t/swamp/options.pl.bak 19 | ); 20 | 21 | JUST_THE_DIR: { 22 | my @expected = line_split( <<"HERE" ); 23 | $source_files[0]:19:notawordhere 24 | $source_files[1]:19:notawordhere 25 | HERE 26 | 27 | my @files = qw( t/swamp ); 28 | my @args = qw( notaword ); 29 | 30 | ack_sets_match( [ @args, @files ], \@expected, q{One hit for specifying a dir} ); 31 | } 32 | 33 | # Even a .bak file gets searched if you specify it on the command line. 34 | SPECIFYING_A_BAK_FILE: { 35 | my @expected = line_split( <<"HERE" ); 36 | $source_files[1]:19:notawordhere 37 | $source_files[2]:19:notawordhere 38 | HERE 39 | 40 | my @files = qw( 41 | t/swamp/options.pl 42 | t/swamp/options.pl.bak 43 | ); 44 | my @args = qw( notaword ); 45 | 46 | ack_sets_match( [ @args, @files ], \@expected, q{Two hits for specifying the file} ); 47 | } 48 | 49 | FILE_NOT_THERE: { 50 | my $file = reslash( 't/swamp/perl.pod' ); 51 | 52 | my $filename = $ENV{ACK_TEST_STANDALONE} ? 'ack-standalone' : 'ack'; 53 | my @expected_stderr = ( "$filename: non-existent-file.txt: No such file or directory" ); 54 | 55 | my @expected_stdout = line_split( <<"HERE" ); 56 | ${file}:3:=head2 There's important stuff in here! 57 | HERE 58 | 59 | my @files = ( 'non-existent-file.txt', $file ); 60 | my @args = qw( head2 ); 61 | my ($stdout, $stderr) = run_ack_with_stderr( @args, @files ); 62 | 63 | lists_match( $stderr, \@expected_stderr, q{Error if there's no file} ); 64 | lists_match( $stdout, \@expected_stdout, 'Find the one file that has a hit' ); 65 | } 66 | 67 | exit 0; 68 | -------------------------------------------------------------------------------- /t/config-backwards-compat.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use lib 't'; 7 | use Util; 8 | use Test::More tests => 3; 9 | 10 | prep_environment(); 11 | 12 | my $temp_config = create_tempfile( <<'HERE' ); 13 | # Always sort 14 | --sort-files 15 | 16 | # I'm tired of grouping 17 | --noheading 18 | --break 19 | 20 | # Handle .hwd files 21 | --type-set=hwd=.hwd 22 | 23 | # Handle .md files 24 | --type-set=md=.mkd 25 | --type-add=md=.md 26 | 27 | # Handle .textile files 28 | --type-set=textile=.textile 29 | 30 | # Hooray for smart-case! 31 | --smart-case 32 | 33 | --ignore-dir=nytprof 34 | HERE 35 | 36 | my @args = ( '--ackrc=' . $temp_config->filename, '-t', 'md', 'One', 't/swamp/' ); 37 | 38 | my $file = reslash('t/swamp/notes.md'); 39 | my $line = 3; 40 | 41 | my ( $stdout, $stderr ) = run_ack_with_stderr( @args ); 42 | is( scalar(@{$stdout}), 1, 'Got back exactly one line' ); 43 | like $stdout->[0], qr/\Q$file:$line\E.*[*] One/; 44 | is_empty_array( $stderr, 'No output to stderr' ); 45 | -------------------------------------------------------------------------------- /t/context-with-newlines.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | # https://github.com/beyondgrep/ack2/issues/522 4 | 5 | use strict; 6 | use warnings; 7 | use Test::More tests => 4; 8 | 9 | use lib 't'; 10 | use Util; 11 | 12 | prep_environment(); 13 | 14 | my @stdout; 15 | 16 | @stdout = run_ack("use strict;\nuse warnings", 't/swamp'); 17 | is_empty_array( \@stdout, 'An embedded newline in the search regex should never match anything' ); 18 | 19 | @stdout = run_ack('-A', '1', "use strict;\nuse warnings", 't/swamp'); 20 | is_empty_array( \@stdout, 'An embedded newline in the search regex should never match anything, even with context' ); 21 | -------------------------------------------------------------------------------- /t/context.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Checks also beginning of file. 3 | args: a -w -B1 t/text/ozymandias.txt 4 | ordered: true 5 | stdout: | 6 | I met a traveller from an antique land 7 | -- 8 | Stand in the desert... Near them, on the sand, 9 | Half sunk, a shattered visage lies, whose frown, 10 | 11 | --- 12 | name: Before with line number 13 | args: sand --sort-files -B2 t/text/ozymandias.txt t/text/bill-of-rights.txt 14 | ordered: true 15 | stdout: | 16 | t/text/ozymandias.txt-1-I met a traveller from an antique land 17 | t/text/ozymandias.txt-2-Who said: Two vast and trunkless legs of stone 18 | t/text/ozymandias.txt:3:Stand in the desert... Near them, on the sand, 19 | -- 20 | t/text/ozymandias.txt-12-Nothing beside remains. Round the decay 21 | t/text/ozymandias.txt-13-Of that colossal wreck, boundless and bare 22 | t/text/ozymandias.txt:14:The lone and level sands stretch far away. 23 | 24 | --- 25 | name: Checks also end of file. 26 | args: sands -A2 t/text/ozymandias.txt 27 | ordered: true 28 | stdout: | 29 | The lone and level sands stretch far away. 30 | 31 | --- 32 | name: Context defaults to 2. 33 | args: Montresor -w -C t/text/amontillado.txt 34 | stdout: | 35 | "Yes,"I said, "let us be gone." 36 | 37 | "For the love of God, Montresor!" 38 | 39 | "Yes," I said, "for the love of God!" 40 | 41 | --- 42 | name: Context 1 43 | args: foot -w -C 1 t/text/amontillado.txt 44 | stdout: | 45 | requesting him to be cautious as he followed. We came at length to 46 | the foot of the descent, and stood together on the damp ground of 47 | the catacombs of the Montresors. 48 | -- 49 | 50 | "A huge human foot d'or, in a field azure; the foot crushes a serpent 51 | rampant whose fangs are imbedded in the heel." 52 | 53 | --- 54 | name: Context 0 means no context 55 | args: Montresor -w -C 0 t/text/amontillado.txt 56 | stdout: | 57 | "For the love of God, Montresor!" 58 | 59 | --- 60 | name: -1 must not stop the ending context from displaying. 61 | args: right -1 -C1 t/text/bill-of-rights.txt 62 | stdout: | 63 | or prohibiting the free exercise thereof; or abridging the freedom of 64 | speech, or of the press; or the right of the people peaceably to assemble, 65 | and to petition the Government for a redress of grievances. 66 | 67 | --- 68 | name: -C with overlapping contexts (adjacent lines) 69 | args: -C 05|06 t/text/numbered-text.txt 70 | stdout: | 71 | This is line 03 72 | This is line 04 73 | This is line 05 74 | This is line 06 75 | This is line 07 76 | This is line 08 77 | 78 | --- 79 | name: -C with contexts that touch. 80 | args: -C 03|08 t/text/numbered-text.txt 81 | stdout: | 82 | This is line 01 83 | This is line 02 84 | This is line 03 85 | This is line 04 86 | This is line 05 87 | This is line 06 88 | This is line 07 89 | This is line 08 90 | This is line 09 91 | This is line 10 92 | 93 | --- 94 | name: -C with contexts that just don't touch. 95 | args: -C 03|09 t/text/numbered-text.txt 96 | stdout: | 97 | This is line 01 98 | This is line 02 99 | This is line 03 100 | This is line 04 101 | This is line 05 102 | -- 103 | This is line 07 104 | This is line 08 105 | This is line 09 106 | This is line 10 107 | This is line 11 108 | -------------------------------------------------------------------------------- /t/double-hyphen.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Double-hyphen allows hyphens after 3 | args: -i --sort -- -E t/text 4 | # The -i must be in force for the /-E/ to find "-e" 5 | ordered: true 6 | stdout: | 7 | t/text/amontillado.txt:284:to the yells of him who clamored. I re-echoed--I aided--I surpassed 8 | t/text/amontillado.txt:327:I re-erected the old rampart of bones. For the half of a century 9 | t/text/bill-of-rights.txt:53:fact tried by a jury, shall be otherwise re-examined in any Court of 10 | -------------------------------------------------------------------------------- /t/empty-lines.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | # https://github.com/beyondgrep/ack2/issues/562 4 | 5 | use strict; 6 | use warnings; 7 | use lib 't'; 8 | 9 | use Test::More tests => 2; 10 | use Util; 11 | 12 | prep_environment(); 13 | 14 | 15 | my $tempfile = create_tempfile( ('') x 3 ); 16 | 17 | my @results = run_ack('^\s\s+$', $tempfile->filename); 18 | 19 | is_empty_array( \@results, '^\s\s+$ should never match a sequence of empty lines' ); 20 | 21 | done_testing(); 22 | 23 | exit 0; 24 | -------------------------------------------------------------------------------- /t/etc/buttonhook.xml.xxx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /t/etc/shebang.empty.xxx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beyondgrep/ack3/b304dbee1cb4d125c6d44f191be8cd758c88acc3/t/etc/shebang.empty.xxx -------------------------------------------------------------------------------- /t/etc/shebang.foobar.xxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/foobar 2 | -------------------------------------------------------------------------------- /t/etc/shebang.php.xxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | -------------------------------------------------------------------------------- /t/etc/shebang.pl.xxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # NOTE: This must stay a very short file so that C<-B $open_fh> will 3 | # think it's binary. 4 | -------------------------------------------------------------------------------- /t/etc/shebang.py.xxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | -------------------------------------------------------------------------------- /t/etc/shebang.rb.xxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | -------------------------------------------------------------------------------- /t/etc/shebang.sh.xxx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sh 2 | -------------------------------------------------------------------------------- /t/exit-code.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | use lib 't'; 6 | 7 | use Test::More tests => 4; 8 | use Util; 9 | 10 | prep_environment(); 11 | 12 | run_ack( 'legislative', 't/text/constitution.txt' ); 13 | is( get_rc(), 0, 'Exit code with matches should be 0' ); 14 | 15 | run_ack( 'foo', 't/text/constitution.txt' ); 16 | is( get_rc(), 1, 'Exit code with no matches should be 1' ); 17 | -------------------------------------------------------------------------------- /t/ext-filter.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | use lib 't'; 6 | 7 | use FilterTest; 8 | use Test::More tests => 1; 9 | 10 | use App::Ack::Filter::Extension; 11 | 12 | filter_test( 13 | [ ext => qw/pl pod pm t/ ], [ 14 | 't/swamp/Makefile.PL', 15 | 't/swamp/blib/ignore.pm', 16 | 't/swamp/blib/ignore.pod', 17 | 't/swamp/constitution-100k.pl', 18 | 't/swamp/options-crlf.pl', 19 | 't/swamp/options.pl', 20 | 't/swamp/perl-test.t', 21 | 't/swamp/perl.handler.pod', 22 | 't/swamp/perl.pl', 23 | 't/swamp/perl.pm', 24 | 't/swamp/perl.pod', 25 | ], 'only the given extensions should be matched' 26 | ); 27 | -------------------------------------------------------------------------------- /t/file-filter-inverted.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Exclusions of filetype 3 | args: 4 | - t/swamp -f -T perl 5 | - t/swamp -f -t noperl 6 | - t/swamp -f --noperl 7 | stdout: | 8 | t/swamp/c-header.h 9 | t/swamp/c-source.c 10 | t/swamp/crystallography-weenies.f 11 | t/swamp/example.R 12 | t/swamp/file.bar 13 | t/swamp/file.foo 14 | t/swamp/foo_test.py 15 | t/swamp/fresh.css 16 | t/swamp/groceries/another_subdir/fruit 17 | t/swamp/groceries/another_subdir/junk 18 | t/swamp/groceries/another_subdir/meat 19 | t/swamp/groceries/dir.d/fruit 20 | t/swamp/groceries/dir.d/junk 21 | t/swamp/groceries/dir.d/meat 22 | t/swamp/groceries/fruit 23 | t/swamp/groceries/junk 24 | t/swamp/groceries/meat 25 | t/swamp/groceries/subdir/fruit 26 | t/swamp/groceries/subdir/junk 27 | t/swamp/groceries/subdir/meat 28 | t/swamp/html.htm 29 | t/swamp/html.html 30 | t/swamp/incomplete-last-line.txt 31 | t/swamp/javascript.js 32 | t/swamp/lua-shebang-test 33 | t/swamp/Makefile 34 | t/swamp/MasterPage.master 35 | t/swamp/notaMakefile 36 | t/swamp/notaRakefile 37 | t/swamp/notes.md 38 | t/swamp/pipe-stress-freaks.F 39 | t/swamp/Rakefile 40 | t/swamp/Sample.ascx 41 | t/swamp/Sample.asmx 42 | t/swamp/sample.asp 43 | t/swamp/sample.aspx 44 | t/swamp/sample.rake 45 | t/swamp/service.svc 46 | t/swamp/stuff.cmake 47 | t/swamp/test.py 48 | t/swamp/test_foo.py 49 | t/swamp/CMakeLists.txt 50 | t/swamp/swamp/ignoreme.txt 51 | t/swamp/not-an-#emacs-workfile# 52 | 53 | --- 54 | name: Exclusions with added extensions 55 | args: 56 | - "t/swamp -f --type-add=pod:ext:pod -t perl -T pod" 57 | stdout: | 58 | t/swamp/0 59 | t/swamp/constitution-100k.pl 60 | t/swamp/perl.pm 61 | t/swamp/options-crlf.pl 62 | t/swamp/options.pl 63 | t/swamp/perl-without-extension 64 | t/swamp/perl.cgi 65 | t/swamp/Makefile.PL 66 | t/swamp/perl-test.t 67 | t/swamp/perl.pl 68 | -------------------------------------------------------------------------------- /t/file-permission.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | # Make sure ack can handle files it can't read. 4 | 5 | use warnings; 6 | use strict; 7 | 8 | use Test::More; 9 | 10 | use lib 't'; 11 | use Util; 12 | 13 | use File::Spec; 14 | use File::Copy; 15 | use File::Temp; 16 | 17 | use constant NTESTS => 5; 18 | 19 | plan skip_all => q{Can't be checked under Win32} if is_windows(); 20 | plan skip_all => q{Can't be run as root} if $> == 0; 21 | 22 | plan tests => NTESTS; 23 | 24 | prep_environment(); 25 | 26 | my $temp_dir = File::Temp::newdir('temp.XXXX', CLEANUP => 1, EXLOCK => 0, TMPDIR => 1); 27 | my $target = File::Spec->catfile( $temp_dir, 'foo' ); 28 | 29 | copy( $0, $target ) or die "Can't copy $0 to $target"; 30 | 31 | my ($old_mode,$error) = make_unreadable( $target ); 32 | 33 | sub o { return sprintf '%o', shift } 34 | 35 | SKIP: { 36 | skip $error if $error; 37 | 38 | # Execute a search on this file. 39 | check_with( 'regex 1', $target ); 40 | 41 | # --count takes a different execution path 42 | check_with( 'regex 2', '--count', $target, { 43 | expected_stdout => 1, 44 | } ); 45 | 46 | my($volume,$path) = File::Spec->splitpath($target); 47 | 48 | # Run another test on the directory containing the read only file. 49 | check_with( 'notinthere', $volume . $path ); 50 | 51 | # Change permissions back. 52 | my $rc = chmod $old_mode, $target; 53 | ok( $rc, "Succeeded chmodding $target to " . o($old_mode) ); 54 | my (undef, undef, $back_mode) = stat($target); 55 | is( o($back_mode), o($old_mode), "${target}'s are back to what we expect" ); 56 | } 57 | 58 | done_testing(); 59 | 60 | sub check_with { 61 | local $Test::Builder::Level = $Test::Builder::Level + 1; 62 | 63 | my @args = @_; 64 | 65 | return subtest subtest_name( @args ) => sub { 66 | plan tests => 4; 67 | 68 | my $opts = {}; 69 | foreach my $arg ( @args ) { 70 | if ( ref($arg) eq 'HASH' ) { 71 | $opts = $arg; 72 | } 73 | } 74 | @args = grep { ref ne 'HASH' } @args; 75 | 76 | my $expected_stdout = $opts->{expected_stdout} || 0; 77 | 78 | my ($stdout, $stderr) = run_ack_with_stderr( @args ); 79 | is( get_rc(), 1, 'Exit code 1 for no output for grep compatibility' ); 80 | # Should be 2 for best grep compatibility since there was an error but we agreed that wasn't required. 81 | 82 | is( scalar @{$stdout}, $expected_stdout, 'No normal output' ) or diag(explain($stdout)); 83 | is( scalar @{$stderr}, 1, 'One line of stderr output' ) or diag(explain($stderr)); 84 | # Don't check for exact text of warning, the message text depends on LC_MESSAGES 85 | like( $stderr->[0], qr/\Q$target\E:/, 'Warning message OK' ); 86 | }; 87 | } 88 | -------------------------------------------------------------------------------- /t/filetype-detection.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Lua shebang 3 | args: 4 | - t/swamp -f -t lua 5 | args-ack3: 6 | - t/swamp -f --lua 7 | stdout: | 8 | t/swamp/lua-shebang-test 9 | 10 | --- 11 | name: R extensions 12 | args: 13 | - t/swamp -f -t rr 14 | args-ack3: 15 | - t/swamp -f --rr 16 | stdout: | 17 | t/swamp/example.R 18 | 19 | --- 20 | name: ASP.NET 21 | args: 22 | - t/swamp -f -t aspx 23 | args-ack3: 24 | - t/swamp -f --aspx 25 | stdout: | 26 | t/swamp/MasterPage.master 27 | t/swamp/Sample.ascx 28 | t/swamp/Sample.asmx 29 | t/swamp/sample.aspx 30 | t/swamp/service.svc 31 | 32 | --- 33 | name: Python 34 | args: 35 | - t/swamp -f -t python 36 | args-ack3: 37 | - t/swamp -f --python 38 | stdout: | 39 | t/swamp/test.py 40 | t/swamp/foo_test.py 41 | t/swamp/test_foo.py 42 | 43 | --- 44 | name: Pytest 45 | args: 46 | - t/swamp -f -t pytest 47 | args-ack3: 48 | - t/swamp -f --pytest 49 | stdout: | 50 | t/swamp/foo_test.py 51 | t/swamp/test_foo.py 52 | -------------------------------------------------------------------------------- /t/filetypes.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use Test::More tests => 19; 7 | 8 | use lib 't'; 9 | use Util; 10 | 11 | 12 | prep_environment(); 13 | 14 | my %types_for_file; 15 | populate_filetypes(); 16 | 17 | 18 | sets_match( [filetypes( 't/swamp/perl.pod' )], [qw( perl pod )], 'foo.pod can be multiple things' ); 19 | sets_match( [filetypes( 't/swamp/perl.pm' )], [qw( perl )], 't/swamp/perl.pm' ); 20 | sets_match( [filetypes( 't/swamp/Makefile.PL' )], [qw( perl )], 't/swamp/Makefile.PL' ); 21 | sets_match( [filetypes( 'Unknown.wango' )], [], 'Unknown' ); 22 | 23 | ok( is_filetype( 't/swamp/perl.pod', 'perl' ), 'foo.pod can be perl' ); 24 | ok( is_filetype( 't/swamp/perl.pod', 'pod' ), 'foo.pod can be pod' ); 25 | ok( !is_filetype( 't/swamp/perl.pod', 'ruby' ), 'foo.pod cannot be ruby' ); 26 | ok( is_filetype( 't/swamp/perl.handler.pod', 'perl' ), 'perl.handler.pod can be perl' ); 27 | ok( is_filetype( 't/swamp/Makefile', 'make' ), 'Makefile is a makefile' ); 28 | ok( is_filetype( 't/swamp/Rakefile', 'rake' ), 'Rakefile is a rakefile' ); 29 | 30 | is_empty_array( [filetypes('t/swamp/#emacs-workfile.pl#')], 'correctly skip files starting and ending with hash mark' ); 31 | 32 | MATCH_VIA_CONTENT: { 33 | my %lookups = ( 34 | 't/swamp/Makefile' => 'make', 35 | 't/swamp/Makefile.PL' => 'perl', 36 | 't/etc/shebang.php.xxx' => 'php', 37 | 't/etc/shebang.pl.xxx' => 'perl', 38 | 't/etc/shebang.py.xxx' => 'python', 39 | 't/etc/shebang.rb.xxx' => 'ruby', 40 | 't/etc/shebang.sh.xxx' => 'shell', 41 | 't/etc/buttonhook.xml.xxx' => 'xml', 42 | ); 43 | for my $filename ( sort keys %lookups ) { 44 | my $type = $lookups{$filename}; 45 | sets_match( [filetypes( $filename )], [ $type ], "Checking $filename" ); 46 | } 47 | } 48 | 49 | done_testing; 50 | 51 | exit 0; 52 | 53 | 54 | sub populate_filetypes { 55 | my ( $type_lines, undef ) = run_ack_with_stderr('--help-types'); 56 | 57 | my @types_to_try; 58 | 59 | foreach my $line ( @{$type_lines} ) { 60 | if ( $line =~ /^ (\w+) / ) { 61 | push @types_to_try, $1; 62 | } 63 | } 64 | 65 | foreach my $type (@types_to_try) { 66 | my ( $filenames, undef ) = run_ack_with_stderr('-f', '-t', $type, 't/swamp', 't/etc'); 67 | 68 | foreach my $filename ( @{$filenames} ) { 69 | push @{ $types_for_file{$filename} }, $type; 70 | } 71 | } 72 | 73 | return; 74 | } 75 | 76 | 77 | sub filetypes { 78 | my $filename = reslash(shift); 79 | 80 | return @{ $types_for_file{$filename} || [] }; 81 | } 82 | 83 | 84 | sub is_filetype { 85 | my ( $filename, $wanted_type ) = @_; 86 | 87 | for my $maybe_type ( filetypes( $filename ) ) { 88 | return 1 if $maybe_type eq $wanted_type; 89 | } 90 | 91 | return 0; 92 | } 93 | -------------------------------------------------------------------------------- /t/filter.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More tests => 5; 7 | 8 | use App::Ack::Filter; 9 | 10 | my $filter; 11 | 12 | $filter = eval { 13 | App::Ack::Filter->create_filter('test'); 14 | }; 15 | 16 | ok( !$filter, 'Creating an unknown filter should fail' ); 17 | like( $@, qr/unknown filter/i, 'Got the expected error' ); 18 | 19 | App::Ack::Filter->register_filter(test => 'TestFilter'); 20 | 21 | $filter = eval { 22 | App::Ack::Filter->create_filter('test', qw/foo bar/); 23 | }; 24 | 25 | ok( $filter, 'Creating a registered filter should succeed' ) or diag($@); 26 | isa_ok( $filter, 'TestFilter', 'Creating a test filter should be a TestFilter' ); 27 | is_deeply( $filter, [qw/foo bar/], 'Extra arguments should get passed through to constructor' ); 28 | 29 | 30 | package TestFilter; 31 | 32 | use strict; 33 | use warnings; 34 | 35 | sub new { 36 | my ( $class, @args ) = @_; 37 | 38 | return bless \@args, $class; 39 | } 40 | 41 | 1; 42 | -------------------------------------------------------------------------------- /t/firstlinematch-filter.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | use lib 't'; 6 | 7 | use FilterTest; 8 | use Test::More tests => 1; 9 | 10 | use App::Ack::Filter::FirstLineMatch; 11 | 12 | filter_test( 13 | [ firstlinematch => '/^#!.*perl/' ], [ 14 | 't/swamp/#emacs-workfile.pl#', 15 | 't/swamp/0', 16 | 't/swamp/Makefile.PL', 17 | 't/swamp/options-crlf.pl', 18 | 't/swamp/options.pl', 19 | 't/swamp/options.pl.bak', 20 | 't/swamp/perl-test.t', 21 | 't/swamp/perl-without-extension', 22 | 't/swamp/perl.cgi', 23 | 't/swamp/perl.pl', 24 | 't/swamp/perl.pm', 25 | 't/swamp/blib/ignore.pm', 26 | 't/swamp/blib/ignore.pod', 27 | ], 'only files with "perl" in their first line should be matched' 28 | ); 29 | -------------------------------------------------------------------------------- /t/from-stdin.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use Test::More tests => 3; 7 | 8 | use lib 't'; 9 | use Util; 10 | 11 | prep_environment(); 12 | 13 | 14 | PIPE_INTO_ACK: { 15 | my @expected = line_split( <<'END' ); 16 | Of 'Never -- nevermore.' 17 | She shall press, ah, nevermore! 18 | Shall be lifted--nevermore! 19 | END 20 | 21 | my $file = 't/text/raven.txt'; 22 | my @args = qw( nevermore ); 23 | my @results = pipe_into_ack( $file, @args ); 24 | 25 | is_deeply( \@results, \@expected, 'Piping a file' ); 26 | } 27 | 28 | 29 | PIPE_INTO_DASH_I: { 30 | my @expected = line_split( <<'END' ); 31 | Quoth the Raven, "Nevermore." 32 | With such name as "Nevermore." 33 | Then the bird said, "Nevermore." 34 | Of 'Never -- nevermore.' 35 | Meant in croaking "Nevermore." 36 | She shall press, ah, nevermore! 37 | Quoth the Raven, "Nevermore." 38 | Quoth the Raven, "Nevermore." 39 | Quoth the Raven, "Nevermore." 40 | Quoth the Raven, "Nevermore." 41 | Shall be lifted--nevermore! 42 | END 43 | 44 | my $file = 't/text/raven.txt'; 45 | my @args = qw( nevermore -i ); 46 | my @results = pipe_into_ack( $file, @args ); 47 | 48 | is_deeply( \@results, \@expected, 'Piping a file with -i' ); 49 | } 50 | 51 | 52 | PIPE_INTO_DASH_C: { 53 | my $file = 't/text/raven.txt'; 54 | my @args = qw( nevermore -i -c ); 55 | my @results = pipe_into_ack( $file, @args ); 56 | 57 | is_deeply( \@results, [ 11 ], 'Piping into ack --count should return one line of results' ); 58 | } 59 | 60 | 61 | exit 0; 62 | -------------------------------------------------------------------------------- /t/home/.ackrc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beyondgrep/ack3/b304dbee1cb4d125c6d44f191be8cd758c88acc3/t/home/.ackrc -------------------------------------------------------------------------------- /t/illegal-regex.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More; 7 | use lib 't'; 8 | use Util; 9 | 10 | plan tests => 2; 11 | 12 | prep_environment(); 13 | 14 | my $ACK = $ENV{ACK_TEST_STANDALONE} ? 'ack-standalone' : 'ack'; 15 | 16 | # This unmatched paren is fatal. 17 | subtest 'Check fatal' => sub { 18 | plan tests => 2; 19 | 20 | my ($output,$stderr) = run_ack_with_stderr( '(set|get)_user_(id|(username)' ); 21 | 22 | my @expected = line_split( <<"HERE" ); 23 | $ACK: Invalid regex '(set|get)_user_(id|(username)' 24 | Regex: (set|get)_user_(id|(username) 25 | ^---HERE Unmatched ( in regex 26 | HERE 27 | is_empty_array( $output, 'No output' ); 28 | lists_match( $stderr, \@expected, 'Error body' ); 29 | }; 30 | 31 | # In Perl 5.20 and below, opening brace at the end doesn't get a warning. 32 | # In Perl 5.22 and above, we get a warning but the text changes. 33 | # This opening brace at the end is just a warning, but we still catch it in Perl > 5.20. 34 | subtest 'Check warning' => sub { 35 | if ( $^V < 5.022 ) { 36 | return pass( "Perl $^V does not throw a warning on the closing brace" ); 37 | } 38 | 39 | plan tests => 5; 40 | 41 | my ($output,$stderr) = run_ack_with_stderr( 'foo{' ); 42 | 43 | is_empty_array( $output, 'No output' ); 44 | is( $stderr->[0], "$ACK: Invalid regex 'foo{'", 'Line 1 OK' ); 45 | is( $stderr->[1], 'Regex: foo{', 'Line 2 OK' ); 46 | like( $stderr->[2], qr/\Q^---HERE Unescaped left brace/, 'The message changes between Perl versions' ); 47 | is( scalar @{$stderr}, 3, 'Only 3 lines' ); 48 | }; 49 | 50 | 51 | exit 0; 52 | -------------------------------------------------------------------------------- /t/incomplete-last-line.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use Test::More tests => 4; 7 | 8 | use lib 't'; 9 | use Util; 10 | 11 | prep_environment(); 12 | 13 | # Check that a match on the last line of a file without a proper 14 | # ending newline gets this newline appended by ack. 15 | 16 | 17 | VERIFY_LAST_LINE_IS_MISSING_NEWLINE: { 18 | # Verify that our test data file is set up the way we expect and that it hasn't had a newline 19 | # added to the end of the file by mistake. 20 | open( my $fh, '<', 't/swamp/incomplete-last-line.txt' ) or die $!; 21 | my @lines = <$fh>; 22 | close $fh; 23 | is( substr( $lines[0], -1, 1 ), "\n", 'First line ends with a newline' ); 24 | is( substr( $lines[1], -1, 1 ), "\n", 'Second line ends with a newline' ); 25 | is( substr( $lines[2], -1, 1 ), '!', 'Third line ends with a bang, not a newline' ); 26 | } 27 | 28 | 29 | INCOMPLETE_LAST_LINE: { 30 | my @expected = line_split( <<'HERE' ); 31 | but no new line on the last line! 32 | the last full measure of devotion -- that we here highly resolve that 33 | HERE 34 | 35 | my @args = qw( -h --nogroup last ); 36 | my @files = qw( t/swamp/incomplete-last-line.txt t/text/gettysburg.txt ); 37 | 38 | ack_lists_match( [ @args, @files ], \@expected, 'Incomplete line gets a newline appended.' ); 39 | } 40 | 41 | done_testing(); 42 | 43 | exit 0; 44 | -------------------------------------------------------------------------------- /t/invalid-ackrc.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use File::Temp; 7 | use List::Util qw( sum ); 8 | use Test::More; 9 | use lib 't'; 10 | use Util; 11 | 12 | my %types = ( 13 | perl => [qw{.pl .pod .pl .t}], 14 | python => [qw{.py}], 15 | ruby => [qw{.rb Rakefile}], 16 | ); 17 | 18 | plan tests => 3; 19 | 20 | prep_environment(); 21 | 22 | my $wd = getcwd_clean(); 23 | 24 | my $tempdir = File::Temp->newdir; 25 | 26 | safe_chdir( $tempdir->dirname ); 27 | write_file( '.ackrc', "--frobnicate\n" ); 28 | 29 | subtest 'Check --env and weird options' => sub { 30 | plan tests => 10; 31 | 32 | my $output = run_ack( '--env', '--help' ); 33 | like( $output, qr/Usage: ack/ ); 34 | 35 | $output = run_ack( '--env', '--thpppt' ); 36 | like( $output, qr/ack --thpppt/ ); 37 | 38 | $output = run_ack( '--env', '--bar' ); 39 | like( $output, qr/It's a grep/ ); 40 | 41 | $output = run_ack( '--env', '--cathy' ); 42 | like( $output, qr/CHOCOLATE/ ); 43 | 44 | $output = run_ack( '--env', '--version' ); 45 | like( $output, qr/ack v3\.\d+\.\d+/ ); 46 | }; 47 | 48 | subtest 'Check for all the types' => sub { 49 | plan tests => 50 | 2 51 | + (scalar keys %types) 52 | + (sum map { scalar @{$_} } values %types); 53 | 54 | ( my $output, my $stderr ) = run_ack_with_stderr( '--env', '--help-types' ); 55 | 56 | # We use "scalar grep" here instead of List::Util::any because any isn't in every version of List::Util. 57 | ok( (scalar grep { /Usage: ack/ } @{$output}), 'Found at least one usage line' ); 58 | ok( (scalar grep { /Unknown option: frobnicate/ } @{$stderr}), 'Found the illegal option in the ackrc' ); 59 | 60 | while ( my ($type,$checks) = each %types ) { 61 | my ( $matching_line ) = grep { /^\s+$type/ } @{$output}; 62 | 63 | ok( $matching_line, "Got at least one for --$type" ); 64 | foreach my $check (@{$checks}) { 65 | like( $matching_line, qr/\Q$check\E/ ); 66 | } 67 | } 68 | }; 69 | 70 | 71 | subtest 'Check --env --man' => sub { 72 | plan tests => 2; 73 | 74 | my ($output, $stderr) = run_ack_with_stderr( '--env', '--man' ); 75 | my $filtered_stderr = filter_out_perldoc_noise( $stderr ); 76 | 77 | is( scalar @{$filtered_stderr}, 0, 'Should have no output to stderr: ack --env --man' ) 78 | or diag( join( "\n", 'STDERR:', @{$filtered_stderr} ) ); 79 | 80 | my $first_two_lines = join( "\n", @{$output}[0,1] ); 81 | $first_two_lines =~ s/(.)\cH\1/$1/g; # Eliminate the overstrike used by Pod::Perldoc::ToTextOverstrike 82 | like( $first_two_lines, qr/NAME.+ack(?:-standalone)?\s/sm ); 83 | }; 84 | 85 | safe_chdir( $wd ); 86 | 87 | exit 0; 88 | -------------------------------------------------------------------------------- /t/invalid-options.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | # https://github.com/beyondgrep/ack3/issues/192 4 | 5 | use strict; 6 | use warnings; 7 | 8 | use Test::More tests => 5; 9 | use lib 't'; 10 | use Util; 11 | 12 | prep_environment(); 13 | 14 | 15 | my ( $stdout, $stderr ) = run_ack_with_stderr( '--bloofga' ); 16 | is_empty_array( $stdout, 'No output because of our bad option' ); 17 | 18 | is( $stderr->[0], 'Unknown option: bloofga', 'First line is the error, and should only appear once' ); 19 | like( $stderr->[1], qr/ack(?:-standalone)?: Invalid option on command line/, 'Second line is the general error' ); 20 | is( scalar @{$stderr}, 2, 'There are no more lines' ); 21 | 22 | is( get_rc(), 255, 'Should fail' ); 23 | 24 | exit 0; 25 | -------------------------------------------------------------------------------- /t/is-filter.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | use lib 't'; 6 | 7 | use FilterTest; 8 | use Test::More tests => 1; 9 | 10 | use App::Ack::Filter::Is; 11 | 12 | filter_test( 13 | [ is => 'Makefile' ], 14 | [ 't/swamp/Makefile' ], 15 | 'Only Makefile should be matched' 16 | ); 17 | -------------------------------------------------------------------------------- /t/line-endings.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | =pod 4 | 5 | This test checks for problems where line endings aren't handled correctly 6 | by our pre-scanning of the file. 7 | 8 | # https://github.com/beyondgrep/ack2/issues/491 9 | v2.14 with "-l" not emitting all matching files. 10 | 11 | > echo ' ' >space-newline.txt 12 | > echo $' \n' >space-newline-newline.txt 13 | > ack ' $' space-newline*.txt 14 | space-newline-newline.txt 15 | 1: 16 | 17 | space-newline.txt 18 | 1: 19 | > ack -l ' $' space-newline*.txt 20 | space-newline.txt 21 | 22 | =cut 23 | 24 | use strict; 25 | use warnings; 26 | use lib 't'; 27 | 28 | use Test::More tests => 4; 29 | use File::Temp; 30 | use Util; 31 | 32 | prep_environment(); 33 | 34 | my $dir = File::Temp->newdir; 35 | my $wd = getcwd_clean(); 36 | 37 | safe_chdir( $dir->dirname ); 38 | 39 | my %matching_files = ( 40 | 'space-newline.txt' => " \n", 41 | 'space-newline-space.txt' => " \n\t", 42 | 'space-newline-newline.txt' => " \n\n", 43 | 'space-newline-and-more.txt' => "this\n \nthat\n", 44 | 'words-and-spaces.txt' => "this \nand that\n", 45 | ); 46 | my %nonmatching_files = ( 47 | 'tabby.txt' => "\t\n", 48 | 'also-tabby.txt' => " \t\n", 49 | ); 50 | 51 | my %all_files = ( %matching_files, %nonmatching_files ); 52 | while ( my ($file,$content) = each %all_files ) { 53 | write_file( $file, $content ); 54 | } 55 | my @all_files = keys %all_files; 56 | my @matching_files = keys %matching_files; 57 | my @nonmatching_files = keys %nonmatching_files; 58 | 59 | subtest 'Match normally' => sub { 60 | plan tests => 2; 61 | 62 | my @results = run_ack( ' $', @all_files ); 63 | my @files_matched = @results; 64 | s/:.*// for @files_matched; 65 | sets_match( \@files_matched, [ @matching_files ], 'Found all the matching files in results' ); 66 | }; 67 | 68 | subtest 'Match with -l' => sub { 69 | plan tests => 2; 70 | 71 | my @results = run_ack( '-l', ' $', @all_files ); 72 | sets_match( \@results, [ @matching_files ], 'Matching files should be in -l output' ); 73 | }; 74 | 75 | subtest 'Non-match with -L' => sub { 76 | plan tests => 2; 77 | 78 | my @results = run_ack( '-L', ' $', @all_files ); 79 | sets_match( \@results, [ @nonmatching_files ], 'Nonmatching files should be in -L output' ); 80 | }; 81 | 82 | subtest 'Count with -c' => sub { 83 | plan tests => 2; 84 | 85 | my @results = run_ack( '-c', ' $', @all_files ); 86 | sets_match( \@results, [ 87 | map { "$_:" . ( $matching_files{$_} ? 1 : 0 ) } @all_files 88 | ], 'Matching files should be in -c output with correct counts' ); 89 | }; 90 | 91 | safe_chdir( $wd ); # Get out of temp directory so it can be cleaned up. 92 | 93 | done_testing(); 94 | exit 0; 95 | -------------------------------------------------------------------------------- /t/lowercase.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use warnings; 4 | use strict; 5 | use 5.010; 6 | 7 | use Test::More tests => 2; 8 | 9 | use App::Ack; 10 | 11 | my $lc_list = <<'END'; ## no critic ( CodeLayout::RequireASCII ) 12 | select . from table 13 | select \S+ from table 14 | select [^\s]+ from table 15 | 16 | # Character specifications 17 | find a tab -> \x09 18 | "foo" in hex -> \x66\x6f\x6f with lowercase digits 19 | "foo" in hex -> \x66\x6F\x6F with uppercase digits 20 | control sequences: ctrl-x=\cX 21 | # unicode sequences: \N{GRAVE ACCENT} 22 | unicode sequences: \N{U+263D} 23 | ladies and gentlemen, please welcome, ❤️! 24 | 25 | # Regex metacharacters 26 | keep stuff to the left -> \K 27 | non-word character -> \W 28 | non-space character -> \S 29 | non-digit character -> \D 30 | something something \X 31 | not vertical whitespace -> \V 32 | not horizontal whitespace -> \H 33 | linebreak -> \R 34 | 35 | # unicode regex metachars 36 | # https://www.regular-expressions.info/unicode.html 37 | named property -> \p{Word} same as \w 38 | not the named property -> \P{Word} same as \W 39 | single-character named property shorthand -> \pL is any letter 40 | negation of single-character named property shorthand -> \PL is not any letter 41 | 42 | # Not sure about \X 43 | big combination: \W\S\D\V\H\R but still lowercase 44 | 45 | # Zero-width assertions 46 | not a word boundary -> \B 47 | beginning of a string -> \A 48 | end of a string -> \Z 49 | end-of-match position of prior match -> \G 50 | 51 | # Captures 52 | named capture group and backref -> (?'NAME'pattern) \k'NAME' 53 | named capture group and backref -> (?pattern) \k 54 | # Weird combinations 55 | \A\B\S{14}\G\Dfoo\W*\S+\Z 56 | END 57 | 58 | 59 | my $uc_list = <<'END'; 60 | This is \\Here. 61 | foo[A-Z]+ 62 | [A-Z]*bar 63 | parens([A-Z]*) 64 | foo(?!lookahead)([A-Z]*) 65 | 66 | # Don't get confused by regex metacharacters. 67 | \WWhat now? 68 | dumb \DDonald 69 | lost in \SSpace 70 | lost in \\\\Diskland 71 | 72 | # Weird combinations 73 | \A\\\B\S{14}\G\\\D\\Dog\\\W*\\\\\S+\\\\\Z 74 | \\W//\\W//\\Larry//\\D//? 75 | END 76 | 77 | 78 | my @lc_list = _big_split($lc_list); 79 | if ( $] >= 5.012 ) { 80 | push( @lc_list, 'anything but \n -> \N' ); 81 | } 82 | 83 | my @uc_list = _big_split($uc_list); 84 | 85 | subtest 'Check lowercase' => sub { 86 | plan tests => scalar @lc_list; 87 | 88 | for my $lc ( @lc_list ) { 89 | my $re = qr/$lc/; 90 | ok( App::Ack::is_lowercase( $lc ), qq{"$lc" should be lowercase} ); 91 | } 92 | }; 93 | 94 | subtest 'Check uppercase' => sub { 95 | plan tests => scalar @uc_list; 96 | 97 | for my $uc ( @uc_list ) { 98 | my $re = qr/$uc/; 99 | ok( !App::Ack::is_lowercase( $uc ), qq{"$uc" should not be lowercase} ); 100 | } 101 | }; 102 | 103 | done_testing(); 104 | exit 0; 105 | 106 | 107 | sub _big_split { 108 | my $str = shift; 109 | 110 | return grep { /./ && !/^#/ } split( /\n/, $str ); 111 | } 112 | -------------------------------------------------------------------------------- /t/match-filter.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | use lib 't'; 6 | 7 | use FilterTest; 8 | use Test::More tests => 1; 9 | 10 | use App::Ack::Filter::Match; 11 | 12 | filter_test( 13 | [ match => '/^.akefile/' ], [ 14 | 't/swamp/Makefile', 15 | 't/swamp/Makefile.PL', 16 | 't/swamp/Rakefile', 17 | ], 'only files matching /^.akefile/ should be matched', 18 | ); 19 | -------------------------------------------------------------------------------- /t/multiple-captures.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | # Multiple capture groups used to confuse ack. 4 | # https://github.com/beyondgrep/ack2/issues/244 5 | 6 | use strict; 7 | use warnings; 8 | use Test::More; 9 | 10 | use lib 't'; 11 | use Util; 12 | 13 | plan tests => 2; 14 | 15 | prep_environment(); 16 | 17 | my ( $stdout, $stderr ) = run_ack_with_stderr('--color', '(foo)|(bar)', 't/swamp'); 18 | is_nonempty_array( $stdout ); 19 | is_empty_array( $stderr ); 20 | 21 | exit 0; 22 | -------------------------------------------------------------------------------- /t/mutex.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Order doesn't matter. They are reported in alphabetical order. 3 | name: -f and -g 4 | args: 5 | - -f -g WORD 6 | - -g WORD -f 7 | exitcode: 255 8 | stdout: 9 | stderr: | 10 | ack: Options '-f' and '-g' can't be used together. 11 | 12 | --- 13 | name: -f and -p 14 | args: 15 | - -f -p 16 | - -f -p 2 17 | exitcode: 255 18 | stdout: 19 | stderr: | 20 | ack: Options '-f' and '-p' can't be used together. 21 | 22 | --- 23 | name: -f and --proximate 24 | args: 25 | - -f --proximate 26 | - -f --proximate 2 27 | exitcode: 255 28 | stdout: 29 | stderr: | 30 | ack: Options '-f' and '--proximate' can't be used together. 31 | 32 | --- 33 | # Check for abbreviations. https://github.com/beyondgrep/ack3/issues/57 34 | name: -f and --proximate abbreviations 35 | args: 36 | - -f --pro 37 | - -f --prox 38 | - -f --proxi 39 | - -f --proxim 40 | - -f --proxima 41 | - -f --proximat 42 | - -f --proximate 43 | exitcode: 255 44 | stdout: 45 | stderr: | 46 | ack: Options '-f' and '--proximate' can't be used together. 47 | 48 | --- 49 | name: -f and --match 50 | args: 51 | - -f --match WORD 52 | exitcode: 255 53 | stdout: 54 | stderr: | 55 | ack: Options '-f' and '--match' can't be used together. 56 | 57 | --- 58 | name: -g and --match 59 | args: 60 | - -g WORD --match WORD 61 | exitcode: 255 62 | stdout: 63 | stderr: | 64 | ack: Options '-g' and '--match' can't be used together. 65 | 66 | --- 67 | name: --or and --and 68 | args: 69 | - this --or that --and other 70 | exitcode: 255 71 | stdout: 72 | stderr: | 73 | ack: Options '--and' and '--or' can't be used together. 74 | -------------------------------------------------------------------------------- /t/named-pipes.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | use lib 't'; 6 | 7 | use File::Temp; 8 | use Test::More; 9 | use Util; 10 | use POSIX (); 11 | 12 | MAIN: { 13 | local $SIG{'ALRM'} = sub { 14 | fail 'Timeout'; 15 | exit; 16 | }; 17 | 18 | prep_environment(); 19 | 20 | my $tempdir = File::Temp->newdir; 21 | safe_mkdir( "$tempdir/foo" ); 22 | my $rc = eval { POSIX::mkfifo( "$tempdir/foo/test.pipe", oct(660) ) }; 23 | if ( !$rc ) { 24 | dir_cleanup( $tempdir ); 25 | plan skip_all => $@ ? $@ : q{I can't run a mkfifo, so cannot run this test.}; 26 | } 27 | 28 | plan tests => 2; 29 | 30 | touch( "$tempdir/foo/bar.txt" ); 31 | 32 | alarm 5; # Should be plenty of time. 33 | 34 | my @results = run_ack( '-f', $tempdir ); 35 | 36 | is_deeply( \@results, [ 37 | "$tempdir/foo/bar.txt", 38 | ], 'Acking should not find the fifo' ); 39 | 40 | dir_cleanup( $tempdir ); 41 | 42 | done_testing(); 43 | } 44 | 45 | exit 0; 46 | 47 | sub dir_cleanup { 48 | my $tempdir = shift; 49 | 50 | unlink "$tempdir/foo/bar.txt"; 51 | rmdir "$tempdir/foo"; 52 | rmdir $tempdir; 53 | 54 | return; 55 | } 56 | -------------------------------------------------------------------------------- /t/needs-line-scan.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use Test::More tests => 1; 7 | 8 | use lib 't'; 9 | use Util; 10 | 11 | prep_environment(); 12 | 13 | # The "bongo" match is after the 100,000-byte cutoff. 14 | NEEDS_LINE_SCAN: { 15 | my @expected = line_split( <<'HERE' ); 16 | my $bongo = 'yada yada'; 17 | HERE 18 | 19 | my @files = qw( t/swamp ); 20 | my @args = qw( bongo -w -h ); 21 | 22 | ack_lists_match( [ @args, @files ], \@expected, 'Looking for Lenore!' ); 23 | } 24 | 25 | exit 0; 26 | -------------------------------------------------------------------------------- /t/noackrc.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | use lib 't'; 6 | 7 | use Test::More tests => 1; 8 | use Util; 9 | 10 | prep_environment(); 11 | 12 | my @expected = ( 13 | 't/swamp/Makefile.PL', 14 | 't/swamp/constitution-100k.pl', 15 | 't/swamp/options-crlf.pl', 16 | 't/swamp/options.pl', 17 | 't/swamp/perl.pl', 18 | ); 19 | 20 | my @args = ( '--ignore-ack-defaults', '--type-add=perl:ext:pl', '-t', 'perl', '-f' ); 21 | my @files = ( 't/swamp' ); 22 | 23 | ack_sets_match( [ @args, @files ], \@expected, __FILE__ ); 24 | 25 | done_testing(); 26 | -------------------------------------------------------------------------------- /t/noenv.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Test::More tests => 3; 7 | 8 | use lib 't'; 9 | use Util; 10 | 11 | use App::Ack::ConfigLoader; 12 | use Cwd qw( realpath ); 13 | use File::Spec (); 14 | use File::Temp (); 15 | 16 | sub is_global_file { 17 | my ( $filename ) = @_; 18 | 19 | return unless -f $filename; 20 | 21 | my ( undef, $dir ) = File::Spec->splitpath($filename); 22 | $dir = File::Spec->canonpath($dir); 23 | 24 | my (undef, $wd) = File::Spec->splitpath(getcwd_clean(), 1); 25 | $wd = File::Spec->canonpath($wd); 26 | 27 | return $wd !~ /^\Q$dir\E/; 28 | } 29 | 30 | sub remove_defaults_and_globals { 31 | my ( @sources ) = @_; 32 | 33 | return grep { 34 | $_->{name} ne 'Defaults' && !is_global_file($_->{name}) 35 | } @sources; 36 | } 37 | 38 | prep_environment(); 39 | 40 | my $wd = getcwd_clean() or die; 41 | 42 | my $tempdir = File::Temp->newdir; 43 | 44 | safe_chdir( $tempdir->dirname ); 45 | 46 | write_file( '.ackrc', <<'ACKRC' ); 47 | --type-add=perl:ext:pl,t,pm 48 | ACKRC 49 | 50 | subtest 'without --noenv' => sub { 51 | plan tests => 1; 52 | 53 | local @ARGV = ('-f', 'lib/'); 54 | 55 | my @sources = App::Ack::ConfigLoader::retrieve_arg_sources(); 56 | @sources = remove_defaults_and_globals(@sources); 57 | 58 | is_deeply( \@sources, [ 59 | { 60 | name => File::Spec->canonpath(realpath(File::Spec->catfile($tempdir->dirname, '.ackrc'))), 61 | contents => [ '--type-add=perl:ext:pl,t,pm' ], 62 | project => 1, 63 | is_ackrc => 1, 64 | }, 65 | { 66 | name => 'ARGV', 67 | contents => ['-f', 'lib/'], 68 | }, 69 | ], 'Get back a long list of arguments' ); 70 | }; 71 | 72 | subtest 'with --noenv' => sub { 73 | plan tests => 1; 74 | 75 | local @ARGV = ('--noenv', '-f', 'lib/'); 76 | 77 | my @sources = App::Ack::ConfigLoader::retrieve_arg_sources(); 78 | @sources = remove_defaults_and_globals(@sources); 79 | 80 | is_deeply( \@sources, [ 81 | { 82 | name => 'ARGV', 83 | contents => ['-f', 'lib/'], 84 | }, 85 | ], 'Short list comes back because of --noenv' ); 86 | }; 87 | 88 | subtest '--noenv in config' => sub { 89 | plan tests => 3; 90 | 91 | append_file( '.ackrc', "--noenv\n" ); 92 | 93 | my ( $stdout, $stderr ) = run_ack_with_stderr('--env', 'perl'); 94 | is_empty_array( $stdout ); 95 | is( @{$stderr}, 1 ); 96 | like( $stderr->[0], qr/--noenv found in (?:.*)[.]ackrc/ ) or diag(explain($stderr)); 97 | }; 98 | 99 | safe_chdir( $wd ); # Go back to the original directory to avoid warnings 100 | -------------------------------------------------------------------------------- /t/prescan-line-boundaries.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | # https://github.com/beyondgrep/ack2/issues/571 4 | 5 | use strict; 6 | use warnings; 7 | use lib 't'; 8 | 9 | use Test::More tests => 2; 10 | use Util; 11 | 12 | prep_environment(); 13 | 14 | my $tempfile = create_tempfile( <<'HERE' ); 15 | fo 16 | 17 | oo 18 | HERE 19 | 20 | my @results = run_ack('-l', 'fo\s+oo', $tempfile->filename); 21 | is_empty_array( \@results, '\s+ should never match across line boundaries' ); 22 | -------------------------------------------------------------------------------- /t/process-substitution.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | use lib 't'; 6 | 7 | use Test::More; 8 | use Util; 9 | 10 | my @expected = ( 11 | 'THIS IS ALL IN UPPER CASE', 12 | 'this is a word here', 13 | ); 14 | 15 | prep_environment(); 16 | 17 | if ( is_windows() ) { 18 | plan skip_all => 'Test unreliable on Windows.'; 19 | } 20 | 21 | system 'bash', '-c', 'exit'; 22 | if ( $? ) { 23 | plan skip_all => 'You need bash to run this test'; 24 | exit; 25 | } 26 | 27 | plan tests => 1; 28 | 29 | my ( $read, $write ); 30 | 31 | pipe( $read, $write ); 32 | 33 | my $pid = fork(); 34 | 35 | my @output; 36 | 37 | if ( $pid ) { 38 | close $write; 39 | while ( <$read> ) { 40 | chomp; 41 | push @output, $_; 42 | } 43 | waitpid $pid, 0; 44 | } 45 | else { 46 | close $read; 47 | open STDOUT, '>&', $write or die "Can't open: $!"; 48 | open STDERR, '>&', $write or die "Can't open: $!"; 49 | 50 | my @args = adjust_executable( build_ack_invocation( qw( --noenv --nocolor --smart-case this ) ) ); 51 | my $args = join( ' ', @args ); 52 | exec 'bash', '-c', "$args <(cat t/swamp/options.pl)"; 53 | } 54 | 55 | lists_match( \@output, \@expected, __FILE__ ); 56 | 57 | exit 0; 58 | -------------------------------------------------------------------------------- /t/range/america-the-beautiful.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | America the Beautiful 5 | 6 | 7 |

America the Beautiful

8 | 9 |
10 | O beautiful for spacious skies, 11 | For amber waves of grain, 12 | For purple mountain majesties 13 | Above the fruited plain! 14 | America! America! 15 | God shed His grace on thee 16 | And crown thy good with brotherhood 17 | From sea to shining sea! 18 |
19 | 20 |
21 | O beautiful for pilgrim feet, 22 | Whose stern, impassioned stress 23 | A thoroughfare for freedom beat 24 | Across the wilderness! 25 | America! America! 26 | God mend thine every flaw, 27 | Confirm thy soul in self-control, 28 | Thy liberty in law! 29 |
30 | 31 |
32 | O beautiful for heroes proved 33 | In liberating strife, 34 | Who more than self their country loved 35 | And mercy more than life! 36 | America! America! 37 | May God thy gold refine, 38 | Till all success be nobleness, 39 | And every gain divine! 40 |
41 | 42 |
43 | O beautiful for patriot dream 44 | That sees beyond the years 45 | Thine alabaster cities gleam 46 | Undimmed by human tears! 47 | America! America! 48 | God shed His grace on thee 49 | And crown thy good with brotherhood 50 | From sea to shining sea! 51 |
52 | 53 | 54 | -------------------------------------------------------------------------------- /t/range/anchors-aweigh.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Anchors Aweigh> 5 | 6 | 7 |

Anchors Aweigh

8 | 9 |
10 | Stand Navy out to sea, fight our battle cry! 11 | We'll never change our course so vicious foes steer shy-y-y-y! 12 | Roll out the TNT, anchors aweigh! 13 | Sail on to victory, and sink their bones to Davy Jones, hooray! 14 |
15 | 16 |
17 | Anchors Aweigh, my boys, Anchors Aweigh! 18 | Farewell to foreign Shores, we sail at break of day-ay-ay-ay; 19 | Through our last night ashore, drink to the foam, 20 | Until we meet once more, here's wishing you a happy voyage home! 21 |
22 | 23 |
24 | Blue of the mighty deep, Gold of God's great sun; 25 | Let these our colors be, Till All of time be done-n-n-ne; 26 | On seven seas we learn, Navy's stern call: 27 | Faith, courage, service true, With honor over, honor over all. 28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /t/range/johnny-rebeck.txt: -------------------------------------------------------------------------------- 1 | VERSE 2 | There once was a little Dutchman 3 | His name was Johnny Rebeck 4 | He was a dealer in sauerkraut 5 | And sausages and speck 6 | He was the finest Dutchman 7 | The best you've ever seen 8 | 'til one day he invented 9 | A great big sausage machine 10 | Bang! 11 | 12 | CHORUS 13 | Oh, Mr. Johnny Rebeck what makes you be so mean? 14 | I told you you'd be sorry for inventing that machine 15 | Now all the neighbors' cats and dogs will nevermore be seen 16 | They'll all be ground to sausages in Johnny Rebeck's machine. 17 | Bang! 18 | 19 | VERSE 20 | One night the darn thing busted 21 | The darn thing wouldn't go 22 | So Johnny Rebeck crawled inside 23 | To see what made it so 24 | That night his wife had a nightmare 25 | And walking in her sleep 26 | She turned the crank 27 | And gave it a yank 28 | Pooooor Johnny Rebeck was meat 29 | Bang! 30 | -------------------------------------------------------------------------------- /t/range/rangefile.pm: -------------------------------------------------------------------------------- 1 | package RangeFile; 2 | 3 | # For testing the range function. 4 | 5 | use warnings; 6 | use strict; 7 | use 5.010; 8 | 9 | # This function calls print on "foo". 10 | sub foo { 11 | print 'foo'; 12 | return 1; 13 | } 14 | 15 | my $print = 1; 16 | my $update = 5; 17 | 18 | sub bar { 19 | print 'bar'; 20 | return 2; 21 | } 22 | my $task = 'print'; 23 | $update = 12; 24 | 25 | 1; 26 | -------------------------------------------------------------------------------- /t/range/stars-and-stripes-forever.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The Stars and Stripes Forever 5 | 6 | 7 |

The Stars and Stripes Forever

8 | 9 |

By John Philip Sousa

10 | 11 |
12 | Let martial note in triumph float 13 | And liberty extend its mighty hand 14 | A flag appears 'mid thunderous cheers, 15 | The banner of the Western land. 16 | The emblem of the brave and true 17 | Its folds protect no tyrant crew; 18 | The red and white and starry blue 19 | Is freedom's shield and hope. 20 | 21 | Let eagle shriek from lofty peak 22 | The never-ending watchword of our land; 23 | Let summer breeze waft through the trees 24 | The echo of the chorus grand. 25 | Sing out for liberty and light, 26 | Sing out for freedom and the right. 27 | Sing out for Union and its might, 28 | O patriotic sons. 29 |
30 | 31 |
32 | Other nations may deem their flags the best 33 | And cheer them with fervid elation 34 | But the flag of the North and South and West 35 | Is the flag of flags, the flag of Freedom's nation. 36 |
37 | 38 |
39 | Hurrah for the flag of the free! 40 | May it wave as our standard forever, 41 | The gem of the land and the sea, 42 | The banner of the right. 43 | Let despots remember the day 44 | When our fathers with mighty endeavor 45 | Proclaimed as they marched to the fray 46 | That by their might and by their right 47 | It waves forever. 48 |
49 | 50 |
51 | Hurrah for the flag of the free. 52 | May it wave as our standard forever 53 | The gem of the land and the sea, 54 | The banner of the right. 55 | Let despots remember the day 56 | When our fathers with mighty endeavor 57 | Proclaimed as they marched to the fray, 58 | That by their might and by their right 59 | It waves forever. 60 |
61 | 62 | 63 | -------------------------------------------------------------------------------- /t/run_yaml_test.py: -------------------------------------------------------------------------------- 1 | import difflib 2 | import glob 3 | import logging 4 | import os 5 | import subprocess 6 | import sys 7 | 8 | 9 | try: 10 | import yaml 11 | except ImportError: 12 | print('Unable to import yaml module') 13 | sys.exit(1) 14 | 15 | 16 | logger = logging.getLogger(__name__) 17 | 18 | 19 | def test_all_yaml_files(): 20 | """ 21 | Walk all the YAML files and run their tests. 22 | """ 23 | filenames = sorted(glob.glob('t/*.yaml')) 24 | for filename in filenames: 25 | skipped_ack3_only = 0 26 | logger.info(f'YAML: {filename}') 27 | with open(filename, 'r', encoding='UTF-8') as f: 28 | cases = yaml.load_all(f, yaml.FullLoader) 29 | for case in cases: 30 | case = massage_case(case) 31 | if case.get('ack3-only', False): 32 | skipped_ack3_only += 1 33 | else: 34 | run_case(case) 35 | if skipped_ack3_only: 36 | logger.info(f' Skipped {skipped_ack3_only} ack3-only case(s)') 37 | 38 | 39 | 40 | def massage_case(case: dict): 41 | """ 42 | Takes the raw case from the YAML and sets defaults. 43 | """ 44 | if 'exitcode' not in case: 45 | case['exitcode'] = 0 46 | 47 | # Make an array of args arrays out of it if it's not already. 48 | if not isinstance(case['args'], list): 49 | case['args'] = [case['args']] 50 | 51 | if case['stdout'] is None: 52 | case['stdout'] = '' 53 | 54 | return case 55 | 56 | 57 | def expected_lines(case): 58 | """ 59 | Gets the lines expected from the case, adjusting on the settings. 60 | """ 61 | lines = case['stdout'].splitlines() 62 | if indent := case.get('indent-stdout', 0): 63 | lines = [' ' * indent + x for x in lines] 64 | 65 | return lines 66 | 67 | 68 | def show_diff(exp, got): 69 | """ 70 | Shows the diffs between two sets of strings 71 | """ 72 | diff = '\n'.join( 73 | difflib.unified_diff( 74 | exp, got, fromfile='expected', tofile='got', lineterm='' 75 | ) 76 | ) 77 | print(diff) 78 | 79 | 80 | def run_case(case): 81 | """ 82 | Runs an individual case from the YAML. 83 | """ 84 | logger.info(' Case: %s' % case['name']) 85 | for args in case['args']: 86 | if os.getenv('DIRK'): 87 | command = ['./dirk', '--nocolor'] 88 | else: 89 | command = ['perl', '-Mblib', 'ack', '--noenv'] 90 | command += args.split() 91 | logger.info(' Command: %s' % ' '.join(command)) 92 | 93 | try: 94 | result = subprocess.run( 95 | command, 96 | input=case.get('stdin', None), 97 | capture_output=True, 98 | text=True, 99 | check=(not case['exitcode']), 100 | ) 101 | except subprocess.CalledProcessError as e: 102 | print('STDOUT from', command) 103 | print(repr(e.stdout)) 104 | print('STDERR from', command) 105 | print(repr(e.stderr)) 106 | raise 107 | 108 | if case['exitcode']: 109 | assert result.returncode == case['exitcode'] 110 | 111 | exp_lines = expected_lines(case) 112 | got_lines = result.stdout.splitlines() 113 | if not case.get('ordered', False): 114 | got_lines = sorted(got_lines) 115 | exp_lines = sorted(exp_lines) 116 | 117 | # show_diff(exp_lines, got_lines) 118 | assert got_lines == exp_lines 119 | 120 | 121 | test_all_yaml_files() 122 | -------------------------------------------------------------------------------- /t/runtests.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl 2 | #--------------------------------------------------------------------- 3 | # Run tests for ack 4 | # 5 | # Windows makes it hard to temporarily set environment variables, and 6 | # has horrible quoting rules, so what should be a one-liner gets its 7 | # own script. 8 | #--------------------------------------------------------------------- 9 | 10 | use ExtUtils::Command::MM; 11 | 12 | $ENV{PERL_DL_NONLAZY} = 1; 13 | $ENV{ACK_TEST_STANDALONE} = shift; 14 | 15 | defined($ENV{ACK_TEST_STANDALONE}) or die 'Must pass an argument to set ACK_TEST_STANDALONE'; 16 | 17 | # Make sure the tests' standard input is *never* a pipe (messes with ack's filter detection). 18 | open STDIN, '<', '/dev/null'; 19 | 20 | printf( 21 | "Running tests on %s, ACK_TEST_STANDALONE=%s\n", 22 | $ENV{ACK_TEST_STANDALONE} ? 'ack-standalone' : 'blib/script/ack', 23 | $ENV{ACK_TEST_STANDALONE} 24 | ); 25 | test_harness(shift, shift, shift); 26 | -------------------------------------------------------------------------------- /t/swamp/#emacs-workfile.pl#: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | ## no critic 3 | 4 | This is a scratch emacs workfile that has a shebang line but should not get read. 5 | -------------------------------------------------------------------------------- /t/swamp/0: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | 3 | print "Every Perl programmer knows that 0 evaluates to false, and that it is a recipe for DANGER!\n"; 4 | -------------------------------------------------------------------------------- /t/swamp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Not actually a cmake file! 2 | -------------------------------------------------------------------------------- /t/swamp/Makefile: -------------------------------------------------------------------------------- 1 | # This Makefile is for the ack extension to perl. 2 | # 3 | # It was generated automatically by MakeMaker version 4 | # 6.30 (Revision: Revision: 4535 ) from the contents of 5 | # Makefile.PL. Don't edit this file, edit Makefile.PL instead. 6 | # 7 | # ANY CHANGES MADE HERE WILL BE LOST! 8 | # 9 | # MakeMaker ARGV: () 10 | # 11 | # MakeMaker Parameters: 12 | 13 | # ABSTRACT => q[A grep-like program specifically for large source trees] 14 | # AUTHOR => q[Andy Lester ] 15 | # EXE_FILES => [q[ack]] 16 | # MAN3PODS => { } 17 | # NAME => q[ack] 18 | # PM => { Ack.pm=>q[$(INST_LIBDIR)/App/Ack.pm] } 19 | # PREREQ_PM => { Test::More=>q[0], Getopt::Long=>q[0], Term::ANSIColor=>q[0] } 20 | # VERSION_FROM => q[Ack.pm] 21 | # clean => { FILES=>q[ack-*] } 22 | # dist => { COMPRESS=>q[gzip -9f], SUFFIX=>q[gz] } 23 | 24 | There's not really anything here. It's just to have something that starts out like a makefile. 25 | -------------------------------------------------------------------------------- /t/swamp/Makefile.PL: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use warnings; 4 | use strict; 5 | use Test::More tests => 1; 6 | 7 | BEGIN { 8 | use_ok( 'App::Ack' ); 9 | } 10 | 11 | diag( "Testing App::Ack $App::Ack::VERSION, Perl $], $^X" ); 12 | -------------------------------------------------------------------------------- /t/swamp/MasterPage.master: -------------------------------------------------------------------------------- 1 | <%@ Master AutoEventWireup="true" CodeFile="MasterPage.master.cs" Language="C#" %> 2 | -------------------------------------------------------------------------------- /t/swamp/Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'hoe' 3 | 4 | def announce(msg='') 5 | STDERR.puts msg 6 | end 7 | 8 | PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : '' 9 | PKG_NAME = 'mechanize' 10 | PKG_VERSION = '0.6.4' + PKG_BUILD 11 | 12 | Hoe.new(PKG_NAME, PKG_VERSION) do |p| 13 | p.rubyforge_name = PKG_NAME 14 | p.author = 'Aaron Patterson' 15 | p.email = 'aaronp@rubyforge.org' 16 | p.summary = "Mechanize provides automated web-browsing" 17 | p.description = p.paragraphs_of('README.txt', 3).join("\n\n") 18 | p.url = p.paragraphs_of('README.txt', 1).first.strip 19 | p.changes = p.paragraphs_of('CHANGELOG.txt', 0..2).join("\n\n") 20 | files = 21 | (p.test_globs + ['test/**/tc_*.rb', 22 | "test/htdocs/**/*.{html,jpg}", 23 | 'test/data/server.*']).map { |x| 24 | Dir.glob(x) 25 | }.flatten + ['test/data/htpasswd'] 26 | p.extra_deps = ['hpricot'] 27 | p.spec_extras = { :test_files => files } 28 | end 29 | 30 | task :update_version do 31 | announce "Updating Mechanize Version to #{PKG_VERSION}" 32 | File.open("lib/mechanize/mech_version.rb", "w") do |f| 33 | f.puts "module WWW" 34 | f.puts " class Mechanize" 35 | f.puts " Version = '#{PKG_VERSION}'" 36 | f.puts " end" 37 | f.puts "end" 38 | end 39 | sh 'svn commit -m"updating version" lib/mechanize/mech_version.rb' 40 | end 41 | 42 | desc "Tag code" 43 | Rake::Task.define_task("tag") do |p| 44 | baseurl = "svn+ssh://#{ENV['USER']}@rubyforge.org/var/svn/#{PKG_NAME}" 45 | sh "svn cp -m 'tagged #{ PKG_VERSION }' . #{ baseurl }/tags/REL-#{ PKG_VERSION }" 46 | end 47 | 48 | desc "Branch code" 49 | Rake::Task.define_task("branch") do |p| 50 | baseurl = "svn+ssh://#{ENV['USER']}@rubyforge.org/var/svn/#{PKG_NAME}" 51 | sh "svn cp -m 'branched #{ PKG_VERSION }' #{baseurl}/trunk #{ baseurl }/branches/RB-#{ PKG_VERSION }" 52 | end 53 | 54 | desc "Update SSL Certificate" 55 | Rake::Task.define_task('ssl_cert') do |p| 56 | sh "openssl genrsa -des3 -out server.key 1024" 57 | sh "openssl req -new -key server.key -out server.csr" 58 | sh "cp server.key server.key.org" 59 | sh "openssl rsa -in server.key.org -out server.key" 60 | sh "openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt" 61 | sh "cp server.key server.pem" 62 | sh "mv server.key server.csr server.crt server.pem test/data/" 63 | sh "rm server.key.org" 64 | end 65 | -------------------------------------------------------------------------------- /t/swamp/Sample.ascx: -------------------------------------------------------------------------------- 1 | <%@ Control Language="C#" AutoEventWireup="true" CodeFile="Sample.ascx.cs" %> 2 | 3 |

Sample Control!

4 | -------------------------------------------------------------------------------- /t/swamp/Sample.asmx: -------------------------------------------------------------------------------- 1 | <%@ WebService Language="C#" Class="Sample" %> 2 | 3 | using System; 4 | using System.Web.Services; 5 | 6 | public class Sample : WebService { 7 | } 8 | -------------------------------------------------------------------------------- /t/swamp/blib/ignore.pm: -------------------------------------------------------------------------------- 1 | #!perl -T 2 | 3 | use warnings; 4 | use strict; 5 | use Test::More tests => 1; 6 | 7 | BEGIN { 8 | use_ok( 'App::Ack' ); 9 | } 10 | 11 | diag( "Testing App::Ack $App::Ack::VERSION, Perl $], $^X" ); 12 | -------------------------------------------------------------------------------- /t/swamp/blib/ignore.pod: -------------------------------------------------------------------------------- 1 | #!perl -T 2 | 3 | use warnings; 4 | use strict; 5 | use Test::More tests => 1; 6 | 7 | BEGIN { 8 | use_ok( 'App::Ack' ); 9 | } 10 | 11 | diag( "Testing App::Ack $App::Ack::VERSION, Perl $], $^X" ); 12 | -------------------------------------------------------------------------------- /t/swamp/c-header.h: -------------------------------------------------------------------------------- 1 | /* perl.h 2 | * 3 | * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 4 | * 2000, 2001, 2002, 2003, 2004, 2005, 2006, by Larry Wall and others 5 | * 6 | * You may distribute under the terms of either the GNU General Public 7 | * License or the Artistic License, as specified in the README file. 8 | * 9 | */ 10 | 11 | #ifndef H_PERL 12 | #define H_PERL 1 13 | -------------------------------------------------------------------------------- /t/swamp/c-source.c: -------------------------------------------------------------------------------- 1 | /* A Bison parser, made from plural.y 2 | by GNU Bison version 1.28 */ 3 | 4 | #define YYBISON 1 /* Identify Bison output. */ 5 | 6 | /* 7 | Contains the 8 | magic string --noenv 9 | which can be tricky to find. 10 | */ 11 | 12 | #define yyparse __gettextparse 13 | #define yylex __gettextlex 14 | #define yyerror __gettexterror 15 | #define yylval __gettextlval 16 | #define yychar __gettextchar 17 | #define yydebug __gettextdebug 18 | #define yynerrs __gettextnerrs 19 | #define EQUOP2 257 20 | #define CMPOP2 258 21 | #define ADDOP2 259 22 | #define MULOP2 260 23 | #define NUMBER 261 24 | 25 | #line 1 "plural.y" 26 | 27 | /* Expression parsing for plural form selection. 28 | Copyright (C) 2000, 2001 Free Software Foundation, Inc. 29 | Written by Ulrich Drepper , 2000. 30 | 31 | This program is free software; you can redistribute it and/or modify it 32 | under the terms of the GNU Library General Public License as published 33 | by the Free Software Foundation; either version 2, or (at your option) 34 | any later version. 35 | 36 | This program is distributed in the hope that it will be useful, 37 | but WITHOUT ANY WARRANTY; without even the implied warranty of 38 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 39 | Library General Public License for more details. 40 | 41 | You should have received a copy of the GNU Library General Public 42 | License along with this program; if not, write to the Free Software 43 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 44 | USA. */ 45 | 46 | /* The bison generated parser uses alloca. AIX 3 forces us to put this 47 | declaration at the beginning of the file. The declaration in bison's 48 | skeleton file comes too late. This must come before 49 | because may include arbitrary system headers. */ 50 | static void 51 | yyerror (str) 52 | const char *str; 53 | { 54 | /* Do nothing. We don't print error messages here. */ 55 | } 56 | -------------------------------------------------------------------------------- /t/swamp/crystallography-weenies.f: -------------------------------------------------------------------------------- 1 | PROGRAM FORMAT 2 | IMPLICIT NONE 3 | REAL :: X 4 | CHARACTER (LEN=11) :: FORM1 5 | CHARACTER (LEN=*), PARAMETER :: FORM2 = "( F12.3,A )" 6 | FORM1 = "( F12.3,A )" 7 | X = 12.0 8 | PRINT FORM1, X, ' HELLO ' 9 | WRITE (*, FORM2) 2*X, ' HI ' 10 | WRITE (*, "(F12.3,A )") 3*X, ' HI HI ' 11 | END 12 | -------------------------------------------------------------------------------- /t/swamp/example.R: -------------------------------------------------------------------------------- 1 | print('hi') 2 | -------------------------------------------------------------------------------- /t/swamp/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beyondgrep/ack3/b304dbee1cb4d125c6d44f191be8cd758c88acc3/t/swamp/favicon.ico -------------------------------------------------------------------------------- /t/swamp/file.bar: -------------------------------------------------------------------------------- 1 | This is a bar file. 2 | -------------------------------------------------------------------------------- /t/swamp/file.foo: -------------------------------------------------------------------------------- 1 | This is a foo file. 2 | -------------------------------------------------------------------------------- /t/swamp/foo_test.py: -------------------------------------------------------------------------------- 1 | # foo_test.py IS a pytest test, as well as Python. 2 | # This test doesn't test anything meaningful. It's just here for filetype tests. 3 | # But it should pass. 4 | 5 | def test_reality(): 6 | code = 1 7 | assert code == 1 8 | -------------------------------------------------------------------------------- /t/swamp/fresh.css: -------------------------------------------------------------------------------- 1 | html,.wp-dialog{background-color:#fff;}* html input,* html .widget{border-color:#dfdfdf;} 2 | -------------------------------------------------------------------------------- /t/swamp/fresh.css.min: -------------------------------------------------------------------------------- 1 | html,.wp-dialog{background-color:#fff;}* html input,* html .widget{border-color:#dfdfdf;} 2 | -------------------------------------------------------------------------------- /t/swamp/fresh.min.css: -------------------------------------------------------------------------------- 1 | html,.wp-dialog{background-color:#fff;}* html input,* html .widget{border-color:#dfdfdf;} 2 | -------------------------------------------------------------------------------- /t/swamp/groceries/CVS/fruit: -------------------------------------------------------------------------------- 1 | apple 2 | pear 3 | grapes 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/CVS/junk: -------------------------------------------------------------------------------- 1 | apple fritters 2 | grape jam 3 | fried pork rinds 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/CVS/meat: -------------------------------------------------------------------------------- 1 | pork 2 | beef 3 | chicken 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/RCS/fruit: -------------------------------------------------------------------------------- 1 | apple 2 | pear 3 | grapes 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/RCS/junk: -------------------------------------------------------------------------------- 1 | apple fritters 2 | grape jam 3 | fried pork rinds 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/RCS/meat: -------------------------------------------------------------------------------- 1 | pork 2 | beef 3 | chicken 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/another_subdir/CVS/fruit: -------------------------------------------------------------------------------- 1 | apple 2 | pear 3 | grapes 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/another_subdir/CVS/junk: -------------------------------------------------------------------------------- 1 | apple fritters 2 | grape jam 3 | fried pork rinds 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/another_subdir/CVS/meat: -------------------------------------------------------------------------------- 1 | pork 2 | beef 3 | chicken 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/another_subdir/RCS/fruit: -------------------------------------------------------------------------------- 1 | apple 2 | pear 3 | grapes 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/another_subdir/RCS/junk: -------------------------------------------------------------------------------- 1 | apple fritters 2 | grape jam 3 | fried pork rinds 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/another_subdir/RCS/meat: -------------------------------------------------------------------------------- 1 | pork 2 | beef 3 | chicken 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/another_subdir/fruit: -------------------------------------------------------------------------------- 1 | apple 2 | pear 3 | grapes 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/another_subdir/junk: -------------------------------------------------------------------------------- 1 | apple fritters 2 | grape jam 3 | fried pork rinds 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/another_subdir/meat: -------------------------------------------------------------------------------- 1 | pork 2 | beef 3 | chicken 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/dir.d/CVS/fruit: -------------------------------------------------------------------------------- 1 | apple 2 | pear 3 | grapes 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/dir.d/CVS/junk: -------------------------------------------------------------------------------- 1 | apple fritters 2 | grape jam 3 | fried pork rinds 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/dir.d/CVS/meat: -------------------------------------------------------------------------------- 1 | pork 2 | beef 3 | chicken 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/dir.d/RCS/fruit: -------------------------------------------------------------------------------- 1 | apple 2 | pear 3 | grapes 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/dir.d/RCS/junk: -------------------------------------------------------------------------------- 1 | apple fritters 2 | grape jam 3 | fried pork rinds 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/dir.d/RCS/meat: -------------------------------------------------------------------------------- 1 | pork 2 | beef 3 | chicken 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/dir.d/fruit: -------------------------------------------------------------------------------- 1 | apple 2 | pear 3 | grapes 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/dir.d/junk: -------------------------------------------------------------------------------- 1 | apple fritters 2 | grape jam 3 | fried pork rinds 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/dir.d/meat: -------------------------------------------------------------------------------- 1 | pork 2 | beef 3 | chicken 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/fruit: -------------------------------------------------------------------------------- 1 | apple 2 | pear 3 | grapes 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/junk: -------------------------------------------------------------------------------- 1 | apple fritters 2 | grape jam 3 | fried pork rinds 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/meat: -------------------------------------------------------------------------------- 1 | pork 2 | beef 3 | chicken 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/subdir/fruit: -------------------------------------------------------------------------------- 1 | apple 2 | pear 3 | grapes 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/subdir/junk: -------------------------------------------------------------------------------- 1 | apple fritters 2 | grape jam 3 | fried pork rinds 4 | -------------------------------------------------------------------------------- /t/swamp/groceries/subdir/meat: -------------------------------------------------------------------------------- 1 | pork 2 | beef 3 | chicken 4 | -------------------------------------------------------------------------------- /t/swamp/html.htm: -------------------------------------------------------------------------------- 1 | 2 | Boring test file 3 |

but trying to be conforming anyway

4 | 5 | 6 | -------------------------------------------------------------------------------- /t/swamp/html.html: -------------------------------------------------------------------------------- 1 | 2 | Boring test file 3 |

but trying to be conforming anyway

4 | 5 | 6 | -------------------------------------------------------------------------------- /t/swamp/incomplete-last-line.txt: -------------------------------------------------------------------------------- 1 | This is a file 2 | with three lines of text 3 | but no new line on the last line! -------------------------------------------------------------------------------- /t/swamp/javascript.js: -------------------------------------------------------------------------------- 1 | // JavaScript goodness 2 | 3 | // Files and directory structures 4 | var cssDir = "./Stylesheets/"; 5 | var NS4CSS = "wango.css"; 6 | 7 | -------------------------------------------------------------------------------- /t/swamp/lua-shebang-test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | print 'Hello, World!' 4 | -------------------------------------------------------------------------------- /t/swamp/minified.js.min: -------------------------------------------------------------------------------- 1 | var cssDir="./Stylesheets/";var NS4CSS="wango.css" 2 | -------------------------------------------------------------------------------- /t/swamp/minified.min.js: -------------------------------------------------------------------------------- 1 | var cssDir="./Stylesheets/";var NS4CSS="wango.css" 2 | -------------------------------------------------------------------------------- /t/swamp/moose-andy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beyondgrep/ack3/b304dbee1cb4d125c6d44f191be8cd758c88acc3/t/swamp/moose-andy.jpg -------------------------------------------------------------------------------- /t/swamp/not-an-#emacs-workfile#: -------------------------------------------------------------------------------- 1 | This is NOT a scratch emacs workfile. 2 | 3 | It sort of looks like one, but it's not. 4 | 5 | The filename kind of matches, but not really. 6 | -------------------------------------------------------------------------------- /t/swamp/notaMakefile: -------------------------------------------------------------------------------- 1 | This is just a plain old textfile that has a name that looks like 2 | "Makefile" but indeed is not. However, it should not match --make. 3 | -------------------------------------------------------------------------------- /t/swamp/notaRakefile: -------------------------------------------------------------------------------- 1 | This is just a plain old textfile that has a name that looks like 2 | "Rakefile" but indeed is not. However, it should not match --rake 3 | or --ruby. 4 | -------------------------------------------------------------------------------- /t/swamp/notes.md: -------------------------------------------------------------------------------- 1 | # Notes 2 | 3 | * One 4 | * Two 5 | * Three 6 | -------------------------------------------------------------------------------- /t/swamp/options-crlf.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use strict; 3 | use warnings; 4 | 5 | =head1 NAME 6 | 7 | options - Test file for ack command line options 8 | 9 | =cut 10 | 11 | [abc] 12 | 13 | @Q_fields = split(/\b(?:a|b|c)\b/) 14 | 15 | __DATA__ 16 | THIS IS ALL IN UPPER CASE 17 | 18 | this is a word here 19 | notawordhere 20 | -------------------------------------------------------------------------------- /t/swamp/options.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use strict; 3 | use warnings; 4 | 5 | =head1 NAME 6 | 7 | options - Test file for ack command line options 8 | 9 | =cut 10 | 11 | [abc] 12 | 13 | @Q_fields = split(/\b(?:a|b|c)\b/) 14 | 15 | __DATA__ 16 | THIS IS ALL IN UPPER CASE 17 | 18 | this is a word here 19 | notawordhere 20 | -------------------------------------------------------------------------------- /t/swamp/options.pl.bak: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use strict; 3 | use warnings; 4 | 5 | =head1 NAME 6 | 7 | Backup of options - Test file for ack command line options 8 | 9 | =cut 10 | 11 | [abc] 12 | 13 | @Q_fields = split(/\b(?:a|b|c)\b/) 14 | 15 | __DATA__ 16 | THIS IS ALL IN UPPER CASE 17 | 18 | this is a word here 19 | notawordhere 20 | -------------------------------------------------------------------------------- /t/swamp/perl-test.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use warnings; 4 | use strict; 5 | use Test::More tests => 1; 6 | 7 | BEGIN { 8 | use_ok( 'App::Ack' ); 9 | } 10 | 11 | diag( "Testing App::Ack $App::Ack::VERSION, Perl $], $^X" ); 12 | -------------------------------------------------------------------------------- /t/swamp/perl-without-extension: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | 3 | use strict; 4 | use warnings; 5 | print "I'm a Perl program without an extension\n"; 6 | -------------------------------------------------------------------------------- /t/swamp/perl.cgi: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use warnings; 4 | use strict; 5 | use Test::More tests => 1; 6 | 7 | BEGIN { 8 | use_ok( 'App::Ack' ); 9 | } 10 | 11 | diag( "Testing App::Ack $App::Ack::VERSION, Perl $], $^X" ); 12 | -------------------------------------------------------------------------------- /t/swamp/perl.handler.pod: -------------------------------------------------------------------------------- 1 | =head1 I'm Here! 2 | 3 | =cut 4 | -------------------------------------------------------------------------------- /t/swamp/perl.pl: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use warnings; 4 | use strict; 5 | use Test::More tests => 1; 6 | 7 | BEGIN { 8 | use_ok( 'App::Ack' ); 9 | } 10 | 11 | diag( "Testing App::Ack $App::Ack::VERSION, Perl $], $^X" ); 12 | -------------------------------------------------------------------------------- /t/swamp/perl.pm: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use warnings; 4 | use strict; 5 | use Test::More tests => 1; 6 | 7 | BEGIN { 8 | use_ok( 'App::Ack' ); 9 | } 10 | 11 | diag( "Testing App::Ack $App::Ack::VERSION, Perl $], $^X" ); 12 | -------------------------------------------------------------------------------- /t/swamp/perl.pod: -------------------------------------------------------------------------------- 1 | =head1 Dummy document 2 | 3 | =head2 There's important stuff in here! 4 | -------------------------------------------------------------------------------- /t/swamp/perl.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beyondgrep/ack3/b304dbee1cb4d125c6d44f191be8cd758c88acc3/t/swamp/perl.tar.gz -------------------------------------------------------------------------------- /t/swamp/perltoot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beyondgrep/ack3/b304dbee1cb4d125c6d44f191be8cd758c88acc3/t/swamp/perltoot.jpg -------------------------------------------------------------------------------- /t/swamp/pipe-stress-freaks.F: -------------------------------------------------------------------------------- 1 | PROGRAM FORMAT 2 | IMPLICIT NONE 3 | REAL :: X 4 | CHARACTER (LEN=11) :: FORM1 5 | CHARACTER (LEN=*), PARAMETER :: FORM2 = "( F12.3,A )" 6 | FORM1 = "( F12.3,A )" 7 | X = 12.0 8 | PRINT FORM1, X, ' HELLO ' 9 | WRITE (*, FORM2) 2*X, ' HI ' 10 | WRITE (*, "(F12.3,A )") 3*X, ' HI HI ' 11 | END 12 | -------------------------------------------------------------------------------- /t/swamp/sample.asp: -------------------------------------------------------------------------------- 1 | 2 | 3 | ASP Example 4 | 5 | 6 | <% Response.Write "Hello!" %> 7 | 8 | 9 | -------------------------------------------------------------------------------- /t/swamp/sample.aspx: -------------------------------------------------------------------------------- 1 | <% 2 | example.Text = "Example"; 3 | %> 4 | 5 | 6 | 7 | Sample ASP.Net Page 8 | 9 | 10 |
11 | 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /t/swamp/sample.rake: -------------------------------------------------------------------------------- 1 | task :ruby_env do 2 | RUBY_APP = if RUBY_PLATFORM =~ /java/ 3 | "jruby" 4 | else 5 | "ruby" 6 | end unless defined? RUBY_APP 7 | end 8 | -------------------------------------------------------------------------------- /t/swamp/service.svc: -------------------------------------------------------------------------------- 1 | <%@ ServiceHost Language="C#" Service="SampleService" %> 2 | -------------------------------------------------------------------------------- /t/swamp/solution8.tar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beyondgrep/ack3/b304dbee1cb4d125c6d44f191be8cd758c88acc3/t/swamp/solution8.tar -------------------------------------------------------------------------------- /t/swamp/stuff.cmake: -------------------------------------------------------------------------------- 1 | # Not actually a cmake file! 2 | -------------------------------------------------------------------------------- /t/swamp/swamp/ignoreme.txt: -------------------------------------------------------------------------------- 1 | quux 2 | -------------------------------------------------------------------------------- /t/swamp/test.py: -------------------------------------------------------------------------------- 1 | # test.py is NOT a pytest test, but is Python. 2 | 3 | code = 0 4 | 5 | # This should fail 6 | assert code == 1 7 | -------------------------------------------------------------------------------- /t/swamp/test_foo.py: -------------------------------------------------------------------------------- 1 | # test_foo.py IS a pytest test, as well as Python. 2 | # This test doesn't test anything meaningful. It's just here for filetype tests. 3 | # But it should pass. 4 | 5 | def test_reality(): 6 | code = 1 7 | assert code == 1 8 | -------------------------------------------------------------------------------- /t/test-pager: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | use Getopt::Long; 7 | 8 | my $skip; 9 | 10 | GetOptions( 11 | 'skip=i' => \$skip, 12 | ); 13 | 14 | while (<>) { 15 | if ( defined $skip && $. % $skip == 0 ) { 16 | next; 17 | } 18 | print; 19 | } 20 | -------------------------------------------------------------------------------- /t/text/bill-of-rights.txt: -------------------------------------------------------------------------------- 1 | # Amendment I 2 | 3 | Congress shall make no law respecting an establishment of religion, 4 | or prohibiting the free exercise thereof; or abridging the freedom of 5 | speech, or of the press; or the right of the people peaceably to assemble, 6 | and to petition the Government for a redress of grievances. 7 | 8 | # Amendment II 9 | 10 | A well regulated Militia, being necessary to the security of a free State, 11 | the right of the people to keep and bear Arms, shall not be infringed. 12 | 13 | # Amendment III 14 | 15 | No Soldier shall, in time of peace be quartered in any house, without 16 | the consent of the Owner, nor in time of war, but in a manner to be 17 | prescribed by law. 18 | 19 | # Amendment IV 20 | 21 | The right of the people to be secure in their persons, houses, papers, 22 | and effects, against unreasonable searches and seizures, shall not 23 | be violated, and no Warrants shall issue, but upon probable cause, 24 | supported by Oath or affirmation, and particularly describing the place 25 | to be searched, and the persons or things to be seized. 26 | 27 | # Amendment V 28 | 29 | No person shall be held to answer for a capital, or otherwise infamous 30 | crime, unless on a presentment or indictment of a Grand Jury, except in 31 | cases arising in the land or naval forces, or in the Militia, when in 32 | actual service in time of War or public danger; nor shall any person 33 | be subject for the same offence to be twice put in jeopardy of life 34 | or limb; nor shall be compelled in any criminal case to be a witness 35 | against himself, nor be deprived of life, liberty, or property, without 36 | due process of law; nor shall private property be taken for public use, 37 | without just compensation. 38 | 39 | # Amendment VI 40 | 41 | In all criminal prosecutions, the accused shall enjoy the right to a 42 | speedy and public trial, by an impartial jury of the State and district 43 | wherein the crime shall have been committed, which district shall have 44 | been previously ascertained by law, and to be informed of the nature and 45 | cause of the accusation; to be confronted with the witnesses against 46 | him; to have compulsory process for obtaining witnesses in his favor, 47 | and to have the Assistance of Counsel for his defence. 48 | 49 | # Amendment VII 50 | 51 | In Suits at common law, where the value in controversy shall exceed 52 | twenty dollars, the right of trial by jury shall be preserved, and no 53 | fact tried by a jury, shall be otherwise re-examined in any Court of 54 | the United States, than according to the rules of the common law. 55 | 56 | # Amendment VIII 57 | 58 | Excessive bail shall not be required, nor excessive fines imposed, 59 | nor cruel and unusual punishments inflicted. 60 | 61 | # Amendment IX 62 | 63 | The enumeration in the Constitution, of certain rights, shall not be 64 | construed to deny or disparage others retained by the people. 65 | 66 | # Amendment X 67 | 68 | The powers not delegated to the United States by the Constitution, nor 69 | prohibited by it to the States, are reserved to the States respectively, 70 | or to the people. 71 | -------------------------------------------------------------------------------- /t/text/gettysburg.txt: -------------------------------------------------------------------------------- 1 | Four score and seven years ago our fathers brought forth on this 2 | continent, a new nation, conceived in Liberty, and dedicated to the 3 | proposition that all men are created equal. 4 | 5 | Now we are engaged in a great civil war, testing whether that nation, 6 | or any nation so conceived and so dedicated, can long endure. We are met 7 | on a great battle-field of that war. We have come to dedicate a portion 8 | of that field, as a final resting place for those who here gave their 9 | lives that that nation might live. It is altogether fitting and proper 10 | that we should do this. 11 | 12 | But, in a larger sense, we can not dedicate -- we can not consecrate -- 13 | we can not hallow -- this ground. The brave men, living and dead, who 14 | struggled here, have consecrated it, far above our poor power to add or 15 | detract. The world will little note, nor long remember what we say here, 16 | but it can never forget what they did here. It is for us the living, 17 | rather, to be dedicated here to the unfinished work which they who 18 | fought here have thus far so nobly advanced. It is rather for us to be 19 | here dedicated to the great task remaining before us -- that from these 20 | honored dead we take increased devotion to that cause for which they gave 21 | the last full measure of devotion -- that we here highly resolve that 22 | these dead shall not have died in vain -- that this nation, under God, 23 | shall have a new birth of freedom -- and that government of the people, 24 | by the people, for the people, shall not perish from the earth. 25 | -------------------------------------------------------------------------------- /t/text/movies.txt: -------------------------------------------------------------------------------- 1 | 1941: Aykroyd, Belushi 2 | Animal House: Belushi, Matheson, Sutherland 3 | Blazing Saddles: Brooks, Kahn, Korman, Little, Pickens, Wilder 4 | The Blues Brothers: Aykroyd, Belushi, Candy, Fisher 5 | Caddyshack: Chase, Dangerfield, Murray 6 | Dragnet: Aykroyd, Hanks 7 | Elf: Asner, Caan, Deschanel, Ferrell, Newhart, Steenburgen 8 | Ghostbusters: Aykroyd, Murray, Moranis, Ramis 9 | Groundhog Day: Elliott, Murray 10 | Little Shop of Horrors: Guest, Martin, Moranis, Murray 11 | My Blue Heaven: Martin, Moranis 12 | Neighbors: Aykroyd, Belushi 13 | Parenthood: Martin, Steenburgen 14 | Planes, Trains and Automobiles: Candy, Martin, McKean 15 | The Princess Bride: Elwes, Falk, Guest, Patinkin, Shawn 16 | Roxanne: Martin, Willard 17 | Silver Streak: Pryor, Wilder, Willard 18 | Spaceballs: Candy, Moranis, Pullman, Rivers 19 | Stir Crazy: Pryor, Willard 20 | Stripes: Candy, Murray, Ramis 21 | This is Spinal Tap: Crystal, Guest, Hendra, Kirby, McKean, Reiner, Shearer, Willard 22 | Three Amigos: Chase, Martin, Short 23 | Trading Places: Akyroyd, Murphy 24 | Vacation: Candy, Chase, D'Angelo 25 | Waiting For Guffman: Guest, Levy, O'Hara, Willard 26 | When Harry Met Sally: Crystal, Fisher, Kirby, Ryan 27 | Young Frankenstein: Feldman, Garr, Kahn, Leachmann, Wilder 28 | -------------------------------------------------------------------------------- /t/text/number.txt: -------------------------------------------------------------------------------- 1 | 86700 2 | -------------------------------------------------------------------------------- /t/text/numbered-text.txt: -------------------------------------------------------------------------------- 1 | This is line 01 2 | This is line 02 3 | This is line 03 4 | This is line 04 5 | This is line 05 6 | This is line 06 7 | This is line 07 8 | This is line 08 9 | This is line 09 10 | This is line 10 11 | This is line 11 12 | This is line 12 13 | This is line 13 14 | This is line 14 15 | This is line 15 16 | This is line 16 17 | This is line 17 18 | This is line 18 19 | This is line 19 20 | This is line 20 21 | -------------------------------------------------------------------------------- /t/text/ozymandias.txt: -------------------------------------------------------------------------------- 1 | I met a traveller from an antique land 2 | Who said: Two vast and trunkless legs of stone 3 | Stand in the desert... Near them, on the sand, 4 | Half sunk, a shattered visage lies, whose frown, 5 | And wrinkled lip, and sneer of cold command, 6 | Tell that its sculptor well those passions read 7 | Which yet survive, stamped on these lifeless things, 8 | The hand that mocked them, and the heart that fed: 9 | And on the pedestal these words appear: 10 | 'My name is Ozymandias, king of kings: 11 | Look on my works, ye Mighty, and despair!' 12 | Nothing beside remains. Round the decay 13 | Of that colossal wreck, boundless and bare 14 | The lone and level sands stretch far away. 15 | -------------------------------------------------------------------------------- /t/trailing-whitespace.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use Test::More; 7 | 8 | use lib 't'; 9 | use Util; 10 | 11 | plan tests => 4; 12 | 13 | prep_environment(); 14 | 15 | subtest 'whitespace+dollar normal' => sub { 16 | plan tests => 4; 17 | 18 | my @expected = (); 19 | 20 | my @files = qw( t/text/ ); 21 | 22 | for my $context ( undef, '-A', '-B', '-C' ) { 23 | my @args = qw( \s$ ); 24 | 25 | push( @args, $context ) if $context; 26 | 27 | ack_sets_match( [ @args, @files ], \@expected, '\s$ should not match the newlines at the end of a line' ); 28 | } 29 | }; 30 | 31 | subtest 'whitespace+dollar with -l' => sub { 32 | plan tests => 1; 33 | 34 | my @expected = (); 35 | 36 | my @files = qw( t/text/ ); 37 | my @args = qw( \s$ -l ); 38 | 39 | ack_sets_match( [ @args, @files ], \@expected, '\s$ should not match the newlines at the end of a line' ); 40 | }; 41 | 42 | subtest 'whitespace+dollar with -L' => sub { 43 | plan tests => 1; 44 | 45 | my @expected = line_split( <<'HERE' ); 46 | t/text/amontillado.txt 47 | t/text/bill-of-rights.txt 48 | t/text/constitution.txt 49 | t/text/gettysburg.txt 50 | t/text/movies.txt 51 | t/text/number.txt 52 | t/text/numbered-text.txt 53 | t/text/ozymandias.txt 54 | t/text/raven.txt 55 | HERE 56 | 57 | my @files = qw( t/text/ ); 58 | my @args = qw( \s$ -L --sort ); 59 | 60 | ack_sets_match( [ @args, @files ], \@expected, '\s$ should not match the newlines at the end of a line' ); 61 | }; 62 | 63 | subtest 'whitespace+dollar with -v' => sub { 64 | plan tests => 4; 65 | 66 | my @expected = line_split( <<'HERE' ); 67 | I met a traveller from an antique land 68 | Who said: Two vast and trunkless legs of stone 69 | Stand in the desert... Near them, on the sand, 70 | Half sunk, a shattered visage lies, whose frown, 71 | And wrinkled lip, and sneer of cold command, 72 | Tell that its sculptor well those passions read 73 | Which yet survive, stamped on these lifeless things, 74 | The hand that mocked them, and the heart that fed: 75 | And on the pedestal these words appear: 76 | 'My name is Ozymandias, king of kings: 77 | Look on my works, ye Mighty, and despair!' 78 | Nothing beside remains. Round the decay 79 | Of that colossal wreck, boundless and bare 80 | The lone and level sands stretch far away. 81 | HERE 82 | 83 | my @files = qw( t/text/ozymandias.txt ); 84 | 85 | for my $context ( undef, '-A', '-B', '-C' ) { 86 | my @args = qw( \s$ -v ); 87 | 88 | push( @args, $context ) if $context; 89 | 90 | ack_sets_match( [ @args, @files ], \@expected, '\s$ should not match the newlines at the end of a line' ); 91 | } 92 | }; 93 | 94 | done_testing(); 95 | exit 0; 96 | -------------------------------------------------------------------------------- /t/yaml.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use Test::More tests => 26; 7 | 8 | use lib 't'; 9 | use Util; 10 | 11 | prep_environment(); 12 | 13 | MAIN: { 14 | my @yamlfiles = glob( 't/*.yaml' ); 15 | 16 | for my $file ( @yamlfiles ) { 17 | subtest $file => sub () { 18 | my @tests = read_tests( $file ); 19 | 20 | for my $test ( @tests ) { 21 | my $tempfilename; 22 | if ( my $stdin = $test->{stdin} ) { 23 | my $fh = File::Temp->new( UNLINK => 0 ); # We'll delete it ourselves. 24 | $tempfilename = $fh->filename; 25 | print {$fh} $stdin; 26 | close $fh; 27 | } 28 | 29 | my @args = ( 30 | @{$test->{args}}, 31 | @{$test->{'args-ack3'} // []}, 32 | ); 33 | for my $args ( @args ) { 34 | if ( $tempfilename ) { 35 | $args = [ @{$args}, $tempfilename ]; 36 | } 37 | subtest $test->{name} . ': ack ' . join( ' ', @{$args} ) => sub { 38 | if ( exists $test->{stderr} ) { 39 | ack_stderr_matches( $args, $test->{stderr}, $test->{name} ); 40 | is( get_rc(), $test->{exitcode}, 'Exit code matches' ); 41 | } 42 | else { 43 | if ( exists $test->{stdout} ) { 44 | my $stdout = $test->{stdout}; 45 | if ( $test->{ordered} ) { 46 | ack_lists_match( $args, $test->{stdout}, $test->{name} ); 47 | } 48 | else { 49 | ack_sets_match( $args, $test->{stdout}, $test->{name} ); 50 | } 51 | is( get_rc(), $test->{exitcode}, 'Exit code matches' ); 52 | } 53 | else { 54 | fail( "stdout must always be specified" ); 55 | } 56 | } 57 | } 58 | } 59 | if ( $tempfilename ) { 60 | unlink( $tempfilename ); 61 | } 62 | } 63 | }; 64 | } 65 | } 66 | 67 | 68 | exit 0; 69 | -------------------------------------------------------------------------------- /t/zero.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Handle a filename of a literal "0" 3 | args: -f --perl t/swamp 4 | stdout: | 5 | t/swamp/0 6 | t/swamp/constitution-100k.pl 7 | t/swamp/Makefile.PL 8 | t/swamp/options.pl 9 | t/swamp/options-crlf.pl 10 | t/swamp/perl.cgi 11 | t/swamp/perl.handler.pod 12 | t/swamp/perl.pl 13 | t/swamp/perl.pm 14 | t/swamp/perl.pod 15 | t/swamp/perl-test.t 16 | t/swamp/perl-without-extension 17 | -------------------------------------------------------------------------------- /tack: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | make 3 | perl -Mblib blib/script/ack "$@" 4 | -------------------------------------------------------------------------------- /xt/coding-standards.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use lib 't'; 7 | use File::Next; 8 | use Util; 9 | 10 | use Test::More; 11 | 12 | 13 | # Get all the ack component files. 14 | my @files = ( 'ack' ); 15 | my $libs = File::Next::files( { descend_filter => sub { !/\Q.git/ }, file_filter => sub { /\.pm$/ } }, 'lib' ); 16 | while ( my $file = $libs->() ) { 17 | push @files, $file; 18 | } 19 | @files == 20 or die 'I should have exactly 20 modules + ack'; 20 | 21 | # Get all the test files. 22 | for my $spec ( 't/*.t', 'xt/*.t' ) { 23 | my @these_files = glob( $spec ) or die "Couldn't find any $spec"; 24 | push( @files, @these_files ); 25 | } 26 | 27 | @files = grep { !/lowercase.t/ } @files; # lowercase.t has hi-bit and it's OK. 28 | 29 | plan tests => scalar @files; 30 | 31 | for my $file ( @files ) { 32 | subtest $file => sub { 33 | plan tests => 3; 34 | 35 | my @lines = read_file( $file ); 36 | my $text = join( '', @lines ); 37 | 38 | chomp @lines; 39 | my $ok = 1; 40 | my $lineno = 0; 41 | for my $line ( @lines ) { 42 | ++$lineno; 43 | if ( $line =~ /[^ -~]/ ) { 44 | my $col = $-[0] + 1; 45 | diag( "$file has hi-bit characters at $lineno:$col" ); 46 | $ok = 0; 47 | } 48 | if ( $line =~ /\s+$/ ) { 49 | diag( "$file has trailing whitespace on line $lineno" ); 50 | $ok = 0; 51 | } 52 | } 53 | ok( $ok, "$file: No hi-bit characters found and no trailing whitespace" ); 54 | ok( $lines[-1] ne '', "$file: Doesn't end with an empty line" ); 55 | 56 | is( index($text, "\t"), -1, "$file should have no embedded tabs" ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /xt/coresubs.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | # Checks that we are not using core C or C except in very specific places. 4 | # Same with C and C. 5 | # Ignore the App::Ack::Docs modules since they're nothing but text. 6 | 7 | ## no critic ( Bangs::ProhibitDebuggingModules ) 8 | 9 | use warnings; 10 | use strict; 11 | 12 | use Test::More tests => 4; 13 | 14 | use lib 't'; 15 | use Util; 16 | 17 | prep_environment(); 18 | 19 | my $ack_pm = quotemeta( reslash( 'blib/lib/App/Ack.pm' ) ); 20 | 21 | my @exclusions = qw( 22 | --ignore-dir=dev 23 | --ignore-file=is:ack-standalone 24 | ); 25 | 26 | for my $word ( qw( warn die ) ) { 27 | subtest "Finding $word" => sub { 28 | plan tests => 4; 29 | 30 | my @args = ( '(? or C. Use C and C. 40 | my $util_pm = quotemeta( reslash( 't/Util.pm' ) ); 41 | for my $word ( qw( chdir mkdir ) ) { 42 | subtest "Finding $word" => sub { 43 | plan tests => 3; 44 | 45 | my @args = ( '-w', '--ignore-file=is:coresubs.t', '--ignore-file=is:Dockerfile', '--ignore-dir=garage', @exclusions, $word ); 46 | my @results = run_ack( @args ); 47 | 48 | is( scalar @results, 1, 'Exactly one hit...' ) or do { require Data::Dumper; warn Data::Dumper::Dumper( \@results ) }; 49 | like( $results[0], qr/^$util_pm.+CORE::$word/, '... and it is in the function in Util.pm that wraps the core call' ); 50 | }; 51 | } 52 | 53 | exit 0; 54 | -------------------------------------------------------------------------------- /xt/pod.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use warnings; 4 | use strict; 5 | 6 | use Test::More; 7 | 8 | use Test::Pod 1.14; 9 | all_pod_files_ok(); 10 | --------------------------------------------------------------------------------