Differences between revisions 1 and 10 (spanning 9 versions)
Revision 1 as of 2008-06-13 07:34:33
Size: 4009
Comment:
Revision 10 as of 2011-05-03 21:59:12
Size: 7758
Editor: DaleWijnand
Comment: Removed reference to inexistent shell script
Deletions are marked like this. Additions are marked like this.
Line 5: Line 5:
<<TableOfContents>>
Line 6: Line 7:
== Debugging Shell Tests Scripts == == Debugging test scripts (>= 1.4) ==
Line 8: Line 9:
The following script, if placed in hg's `tests` folder, will run a single, simple shell test script much more quickly and in a predictable location, namely `tests/tmp`. It does not support test scripts involving servers, however. It assumes that it is run from within `tests`. Example: Starting with Mercurial 1.4, `run-tests.py` has a "debug" mode that disables the default "capture and diff" behavior and works with Python test scripts in addition to shell test scripts. In debug mode, `run-tests.py` simply echos its child's stdout (and stderr). Naturally, this makes it impossible for `run-tests.py` to tell if a test passed or failed: that's up to you to do by reading its output. As above, this is particularly useful for running `hg` under the Python debugger.
Line 10: Line 11:
Use the `--debug` option to activate debug mode:
Line 11: Line 13:
~/dev/hg/tests$ ./debug-test test-guessrenames
...
./run-tests.py --debug test-something
Line 14: Line 15:
(You can use debug mode on any number of test scripts, but in practice it's most useful on a single script.)
Line 15: Line 17:
And here is the `debug-test` script: Debug mode has no effect on the temporary directory used to run tests; it will remain `$TMPDIR/hgtests.XXXXXX`. If you want to preserve and inspect the test environment, use `--tmpdir` to specify a different temporary directory. Note that the meaning of `--tmpdir` changed in Mercurial 1.4: formerly, the test directory (`hgtests.XXXXXX`) was created inside the temp dir. Now, `--tmpdir` ''is'' the test directory. So if you are in the habit of running `./run-tests.py --tmpdir /mnt/ramdisk`, you'll have to break that habit. Instead, use `TMPDIR=/mnt/ramdisk ./run-tests.py`. `run-tests.py` will abort if the temp dir already exists to prevent accidents for people used to working this way.
Line 17: Line 19:
Putting it all together, then, the way I most commonly use debug mode is this:
Line 18: Line 21:
#!/bin/sh
TESTDIR=`pwd`
cd ..; HGDIR=`pwd`; cd tests # want an absolute path
HGRCPATH=$TESTDIR/tmp/hgrc
PYTHONPATH=$HGDIR:$PYTHONPATH
PATH=$HGDIR:$PATH
export HGRCPATH
export PYTHONPATH
export PATH
rm -rf tmp; mkdir tmp; cd tmp
echo "[ui]" >$HGRCPATH
echo "username = John Doe <john@doe.org>" >>$HGRCPATH
. ../$1
rm -rf tmp && ./run-tests.py --local --debug --tmpdir tmp test-something
Line 32: Line 23:
where:
  * the `rm -rf` is necessary because `run-tests.py` requires that the tmp dir not exist, and will not blow it away for you (to prevent accidents)
  * `--local` is optional, but speeds things up (saves the overhead of a throwaway build+install of Mercurial)
  * `--debug` enables debug mode
  * `--tmpdir` sets the test directory
Line 33: Line 29:
Hg provides a number of DebuggingFeatures you can use here. For example, you might want to change a particular call from `hg` to `hg --debugger` in your testscript. If you need to sift through the wreckage after one run, it's in `tmp/test-something`. Also, keep in mind that `run-tests.py` creates a test-specific `tmp/.hgrc`, so if you need to manually duplicate exactly what happened in the test script, you'll want to do something like this:
{{{
cd tmp
export HGRCPATH=$PWD/.hgrc
cd test-something
}}}
And of course, most test scripts create one or more test repositories under that, so another level of `cd` is generally required.
Line 35: Line 37:
You can also add `echo DEBUG; return` instructions anywhere in your test script to stop and inspect the state manually or to run a particular hg command under control of an external debugger (see below). The `echo DEBUG` part is just a reminder so you don't forget to remove the `return` again. The `hg --cwd` option is particularly handy for switching the debuggee to `tests/tmp/mytestrepo` or wherever. == Stepping into the python debugger ==
Line 37: Line 39:
Assuming a test is run as explained above, the python debugger can be activated by adding the [[DebuggingFeatures|--debugger]] option to {{{hg}}} in the test file.
For instance in {{{tests/test-basic.t}}}
{{{
  $ hg add a
}}}
could be changed for
{{{
  $ hg --debugger add a
}}}
and when the test is run it will break as follows:
{{{
loic@bet:~/software/mercurial/mercurial/tests$ rm -rf tmp && python run-tests.py --local --debug --tmpdir tmp test-basic.t
SALT1300291206.57 2 0
SALT1300291206.57 3 0
SALT1300291206.57 4 0
> /home/loic/software/mercurial/mercurial/mercurial/dispatch.py(601)_dispatch()
-> return runcommand(lui, repo, cmd, fullargs, ui, options, d,
(Pdb)
}}}
An alternative to using the --debugger option is to force a breakpoint. For instance if
{{{
        import pdb
        pdb.set_trace()
}}}
is added just before the {{{runcommand}}} call of {{{mercurial/mercurial/dispatch.py}}} it will break as follows, even if the test script is not modified.
{{{
> /home/loic/software/mercurial/mercurial/mercurial/dispatch.py(601)_dispatch()
-> return runcommand(lui, repo, cmd, fullargs, ui, options, d,
(Pdb)
}}}
You can also read more about the [[DebuggingFeatures|built in debugging]] features of hg.
Line 40: Line 73:
With the open-source [http://pydev.sourceforge.net/ Eclipse Pydev plugin], you can only launch `hg` directly from within [http://eclipse.org/ Eclipse]. So you need to stop the script before the command you want to debug (as described above) and setup a corresponding launch configuration in Eclipse. With the open-source [[http://pydev.sourceforge.net/|Eclipse Pydev plugin]], you can only launch `hg` directly from within [[http://eclipse.org/|Eclipse]]. So you need to stop the script before the command you want to debug (as described above) and setup a corresponding launch configuration in Eclipse.
Line 64: Line 97:
== Using Eclipse Pydev Extensions == == Using Eclipse Pydev extensions ==
Line 66: Line 99:
With the commercially licenced [http://www.fabioz.com/pydev/ Pydev Extensions], you can attach to a running instance of `hg`. This relies on a Pydev supplied module, whose path you need to set up. Amend the Python path in `debug-test` as follows: With the commercially licensed [[http://www.fabioz.com/pydev/|Pydev Extensions]], you can attach to a running instance of `hg`. This relies on a Pydev supplied module, whose path you need to set up. Amend the Python path in `debug-test` as follows:
Line 73: Line 106:
Then you can proceed as described in [http://www.fabioz.com/pydev/manual_adv_remote_debugger.html Pydev's remote debugging instructions]. Beware, though, that it did not work for me when placing `pydevd.settrace()` top-level in `dispatch.py`. It did work within actual commands in `commands.py` and also deeper within the code. Don't despair if Pydev tries to locate `demandimport.py` and cannot find it. Simply set a breakpoint at your desired location and continue the debugger. Then you can proceed as described in [[http://www.fabioz.com/pydev/manual_adv_remote_debugger.html|Pydev's remote debugging instructions]]. Beware, though, that it did not work for me when placing `pydevd.settrace()` top-level in `dispatch.py`. It did work within actual commands in `commands.py` and also deeper within the code. Don't despair if Pydev tries to locate `demandimport.py` and cannot find it. Simply set a breakpoint at your desired location and continue the debugger.
Line 107: Line 140:
== Debugging shell test scripts (pre-1.4) ==

The following script, if placed in hg's `tests` folder, will run a single, simple shell test script much more quickly and in a predictable location, namely `tests/tmp`. It does not support test scripts involving servers, however. It assumes that it is run from within `tests`. Example:

{{{
~/dev/hg/tests$ ./debug-test test-guessrenames
...
}}}

And here is the `debug-test` script:

{{{
#!/bin/sh
TESTDIR=`pwd`
cd ..; HGDIR=`pwd`; cd tests # want an absolute path
HGRCPATH=$TESTDIR/tmp/hgrc
PYTHONPATH=$HGDIR:$PYTHONPATH
PATH=$HGDIR:$PATH
export HGRCPATH
export PYTHONPATH
export PATH
rm -rf tmp; mkdir tmp; cd tmp
echo "[ui]" >$HGRCPATH
echo "username = John Doe <john@example.com>" >>$HGRCPATH
. ../$1
}}}

Hg provides a number of DebuggingFeatures you can use here. For example, you might want to change a particular call from `hg` to `hg --debugger` in your testscript.

You can also add `echo DEBUG; return` instructions anywhere in your test script to stop and inspect the state manually or to run a particular hg command under control of an external debugger (see below). The `echo DEBUG` part is just a reminder so you don't forget to remove the `return` again. The `hg --cwd` option is particularly handy for switching the debuggee to `tests/tmp/mytestrepo` or wherever.
Line 108: Line 172:
CategoryContributing CategoryTesting CategoryHowTo CategoryTesting CategoryDeveloper

Debugging Tests

It's quite handy to be able to develop test scripts in parallel with the code (test-driven development). Hg's default tests/run-tests.py script described in WritingTests is a little slow for running a single test repeatedly while refining your test and code. And it runs the test in a temporary dir with an ever changing name. This is not useful for manually inspecting the state left by the test, or to run hg from within a debugger on an interim state.

Debugging test scripts (>= 1.4)

Starting with Mercurial 1.4, run-tests.py has a "debug" mode that disables the default "capture and diff" behavior and works with Python test scripts in addition to shell test scripts. In debug mode, run-tests.py simply echos its child's stdout (and stderr). Naturally, this makes it impossible for run-tests.py to tell if a test passed or failed: that's up to you to do by reading its output. As above, this is particularly useful for running hg under the Python debugger.

Use the --debug option to activate debug mode:

./run-tests.py --debug test-something

(You can use debug mode on any number of test scripts, but in practice it's most useful on a single script.)

Debug mode has no effect on the temporary directory used to run tests; it will remain $TMPDIR/hgtests.XXXXXX. If you want to preserve and inspect the test environment, use --tmpdir to specify a different temporary directory. Note that the meaning of --tmpdir changed in Mercurial 1.4: formerly, the test directory (hgtests.XXXXXX) was created inside the temp dir. Now, --tmpdir is the test directory. So if you are in the habit of running ./run-tests.py --tmpdir /mnt/ramdisk, you'll have to break that habit. Instead, use TMPDIR=/mnt/ramdisk ./run-tests.py. run-tests.py will abort if the temp dir already exists to prevent accidents for people used to working this way.

Putting it all together, then, the way I most commonly use debug mode is this:

rm -rf tmp && ./run-tests.py --local --debug --tmpdir tmp test-something

where:

  • the rm -rf is necessary because run-tests.py requires that the tmp dir not exist, and will not blow it away for you (to prevent accidents)

  • --local is optional, but speeds things up (saves the overhead of a throwaway build+install of Mercurial)

  • --debug enables debug mode

  • --tmpdir sets the test directory

If you need to sift through the wreckage after one run, it's in tmp/test-something. Also, keep in mind that run-tests.py creates a test-specific tmp/.hgrc, so if you need to manually duplicate exactly what happened in the test script, you'll want to do something like this:

cd tmp
export HGRCPATH=$PWD/.hgrc
cd test-something

And of course, most test scripts create one or more test repositories under that, so another level of cd is generally required.

Stepping into the python debugger

Assuming a test is run as explained above, the python debugger can be activated by adding the --debugger option to hg in the test file. For instance in tests/test-basic.t

  $ hg add a

could be changed for

  $ hg --debugger add a

and when the test is run it will break as follows:

loic@bet:~/software/mercurial/mercurial/tests$ rm -rf tmp && python run-tests.py --local --debug --tmpdir tmp test-basic.t
SALT1300291206.57 2 0
SALT1300291206.57 3 0
SALT1300291206.57 4 0
> /home/loic/software/mercurial/mercurial/mercurial/dispatch.py(601)_dispatch()
-> return runcommand(lui, repo, cmd, fullargs, ui, options, d,
(Pdb) 

An alternative to using the --debugger option is to force a breakpoint. For instance if

        import pdb
        pdb.set_trace()

is added just before the runcommand call of mercurial/mercurial/dispatch.py it will break as follows, even if the test script is not modified.

> /home/loic/software/mercurial/mercurial/mercurial/dispatch.py(601)_dispatch()
-> return runcommand(lui, repo, cmd, fullargs, ui, options, d,
(Pdb) 

You can also read more about the built in debugging features of hg.

Using Eclipse Pydev

With the open-source Eclipse Pydev plugin, you can only launch hg directly from within Eclipse. So you need to stop the script before the command you want to debug (as described above) and setup a corresponding launch configuration in Eclipse.

Main module:

${workspace_loc:my-hg-project/hg}

Arguments (... is the command in question):

--cwd ~/dev/hg/tests/tmp/mytestrepo
...

Working dir (I had to switch from the default settings to this to make the debugger work):

${workspace_loc:my-hg-project}

Environment:

HGRCPATH = ~/dev/hg/tests/tmp/hgrc

Using Eclipse Pydev extensions

With the commercially licensed Pydev Extensions, you can attach to a running instance of hg. This relies on a Pydev supplied module, whose path you need to set up. Amend the Python path in debug-test as follows:

PYDEVDIR=/path/to/eclipse/plugins/org.python.pydev.debug_1.3.17/pysrc
PYTHONPATH=$HGDIR:$PYDEVDIR:$PYTHONPATH

Then you can proceed as described in Pydev's remote debugging instructions. Beware, though, that it did not work for me when placing pydevd.settrace() top-level in dispatch.py. It did work within actual commands in commands.py and also deeper within the code. Don't despair if Pydev tries to locate demandimport.py and cannot find it. Simply set a breakpoint at your desired location and continue the debugger.

If your test script contains something like:

hg stat
...
hg stat

and you want to break on the second instance of hg stat only, you could use a flag file to signal this. I have the following in my debug-test script:

rm /tmp/enable-hg-debugger
DBG() {
    touch /tmp/enable-hg-debugger
}

In the code I then do:

   1 if os.path.exists('/tmp/enable-hg-debugger'):
   2     import pydevd; pydevd.settrace()

and in the script I do:

hg stat
...
DBG; hg stat

Debugging shell test scripts (pre-1.4)

The following script, if placed in hg's tests folder, will run a single, simple shell test script much more quickly and in a predictable location, namely tests/tmp. It does not support test scripts involving servers, however. It assumes that it is run from within tests. Example:

~/dev/hg/tests$ ./debug-test test-guessrenames
...

And here is the debug-test script:

TESTDIR=`pwd`
cd ..; HGDIR=`pwd`; cd tests # want an absolute path
HGRCPATH=$TESTDIR/tmp/hgrc
PYTHONPATH=$HGDIR:$PYTHONPATH
PATH=$HGDIR:$PATH
export HGRCPATH
export PYTHONPATH
export PATH
rm -rf tmp; mkdir tmp; cd tmp
echo "[ui]" >$HGRCPATH
echo "username = John Doe <john@example.com>" >>$HGRCPATH
. ../$1

Hg provides a number of DebuggingFeatures you can use here. For example, you might want to change a particular call from hg to hg --debugger in your testscript.

You can also add echo DEBUG; return instructions anywhere in your test script to stop and inspect the state manually or to run a particular hg command under control of an external debugger (see below). The echo DEBUG part is just a reminder so you don't forget to remove the return again. The hg --cwd option is particularly handy for switching the debuggee to tests/tmp/mytestrepo or wherever.


CategoryTesting CategoryDeveloper

DebuggingTests (last edited 2011-05-03 21:59:12 by DaleWijnand)