[raw_io] Make missing placeholder files testable

If an `OutputTextPlaceholder` backing file doesn't exist when `result()`
is called, `result()` will return None. Most calling code does not
handle this case, in part because the testing-specific code in
`OutputTextPlaceholder.result()` didn't have a way to mock a missing
backing file. This caused occasional InfraFailures in downstream repos
that assumed that the return value of `result()` was non-null.

This change adds support for mocking a missing backing file by passing
`None` as the data to `RawIOTestApi.output()`. This instructs
`OutputTextPlaceHolder.result()` to return None, as it does when the
backing file is missing. This will let downstream repos add tests for
the missing backing file case.

For maximal backwards compatibility, a missing backing file can only be
simulated if the `leak_to` argument to the placeholder is unset. There
are a lot of steps in downstream repos that use output placeholders but
don't mock the placeholder result in the tests. In these cases, the
placeholder used to evaluate to an empty string, but now evaluates to
None. This would break a lot of code that assumes the result is a
string. To avoid this, when in testing mode, the placeholder will
default to the old value of returning an empty string *unless* leak_to
is specified (which is rare), in which case it will return None.  This
is reasonable because it's entirely possible that the `leak_to` file
path won't exist.  On the other hand, it's impossible that the temporary
file created if `leak_to` is unset would be missing, unless the recipe
explicitly deletes it before `result()` gets called.

Bug: fuchsia:35478
Change-Id: Ia4312e157945a18a3f81c33789f41a405e1b6dfa
Reviewed-on: https://chromium-review.googlesource.com/c/infra/luci/recipes-py/+/1777143
Commit-Queue: Oliver Newman <olivernewman@google.com>
Reviewed-by: Robbie Iannucci <iannucci@chromium.org>
11 files changed
tree: e25d960fbbe45ee2e27ee23732968caafcec6a0b
  1. doc/
  2. infra/
  3. misc/
  4. recipe_engine/
  5. recipe_modules/
  6. recipe_proto/
  7. recipes/
  8. unittests/
  9. .gitattributes
  10. .gitignore
  11. .style.yapf
  12. .vpython
  13. AUTHORS
  14. codereview.settings
  15. CONTRIBUTORS
  16. LICENSE
  17. OWNERS
  18. PRESUBMIT.py
  19. README.md
  20. README.recipes.md
  21. recipes.py
README.md

Recipes

Recipes are a domain-specific language (embedded in python) for specifying sequences of subprocess calls in a cross-platform and testable way.

They allow writing build flows which integrate with the rest of LUCI.

Documentation for the recipe engine (including this file!). Take a look at the user guide for some hints on how to get started. See the implementation details doc for more detailed implementation information about the recipe engine.

Contributing

  • Sign the Google CLA.
  • Make sure your user.email and user.name are configured in git config.

Run the following to setup the code review tool and create your first review:

# Get `depot_tools` in $PATH if you don't have it
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git $HOME/src/depot_tools
export PATH="$PATH:$HOME/src/depot_tools"

# Check out the recipe engine repo
git clone https://chromium.googlesource.com/infra/luci/recipes-py $HOME/src/recipes-py

# make your change
cd $HOME/src/recipes-py
git new-branch cool_feature
# hack hack
git commit -a -m "This is awesome"

# This will ask for your Google Account credentials.
git cl upload -s -r joe@example.com
# Wait for approval over email.
# Click "Submit to CQ" button or ask reviewer to do it for you.
# Wait for the change to be tested and landed automatically.

Use git cl help and git cl help <cmd> for more details.