Chapter 14 Debugging and testing drake projects

This chapter is a guide to debugging and testing drake projects.

14.0.1 Start small

If your workflow is large, consider running a downsized version to debug and test things first. That way, you can find the bugs early on without wasting as much time. Create a test plan with drake_plan(max_expand = SMALL_NUMBER) before scaling up to the full set of targets. See this section on plans for details.

14.1 Dependencies

drake automatically detects dependency relationships among your targets and imports. While this is convenient most of the time, it can lead to some pitfalls. This section describes techniques to understand you project’s dependency structure and diagnose and debug issues.

14.1.1 Visualize your dependency graph.

To avoid frustration early on, please use drake’s dependency graph visualizations to see how the steps of your workflow fit together. drake resolves the dependency relationships in the graph by analyzing the code in your commands and the functions in your environment.

14.1.2 Check specific dependency information.

With the deps_code() function, you can see for yourself how drake detects first-order dependencies from code.

With deps_target(), you can see the dependencies that drake has already detected for your targets and imports.

And with tracked(), you can list all the reproducibly tracked objects and files.

14.1.4 But why are my targets out of date?

drake has the option to produce a cache log with the fingerprint of every target and import.

We highly recommend that you automatically produce a cache log file on every make() and put it under version control with the rest of your project.

Suppose we go back and add input checking to one of our functions.

Then, we forget to run make() again, and we leave the the project for several months. When we come back, all our targets are suddenly out of date.

At first, we may not know why all our targets are outdated. But we can generate another cache log and check any hashes that changed. Our call to outdated() already re-cached the imports, so any changed imports will show up in the new cache log.

Now, we see that random_rows() has changed since last time, and we have a new dependency stopifnot(). simulate() shows up in the changes too because random_rows() is nested in the body of simulate(). If we revert random_rows() to its original state, all our targets are up to date again.

14.2 Diagnose failures.

drake records diagnostic metadata on all your targets, including the latest errors, warnings, messages, and other bits of context.

To figure out what went wrong, you could try to build the failed target interactively. To do that, simply call drake_build() or drake_debug(). These functions first call loadd(deps = TRUE) to load any missing dependencies (see the replace argument here) and then build your target. drake_build() simply runs the command, and drake_debug() runs the command in debug mode using debugonce().

14.4 More help

Please also see the compendium of cautionary notes, which addresses drake’s known edge cases, pitfalls, and weaknesses that may or may not be fixed in future releases. For the most up-to-date information on unhandled edge cases, please visit the issue tracker, where you can submit your own bug reports as well. Be sure to search the closed issues too, especially if you are not using the most up-to-date development version.

Copyright Eli Lilly and Company