Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
bb283e6
test: initial build statement testing
Mar 19, 2026
8d87681
test: move to export_e2e build rule
Mar 19, 2026
4a41449
test: add test for custom build def and rework build stmt checked
Mar 20, 2026
15ea8ce
test: targets with deps, multiple targets, export outputs and notrim
Mar 23, 2026
53629ca
test: export go binary including rework of build_def for multiple plz…
Mar 24, 2026
d60973d
test: genrule with go subrepo and go binary with go dependency
Mar 24, 2026
c7bf712
test: build def with children targets
Mar 24, 2026
a164a90
test: preload build def
Mar 24, 2026
5038929
lint: run plz format
Mar 24, 2026
6c5ce6d
fix: plz command replacement for tests cmd
Mar 24, 2026
78a9ff0
nit comments
Mar 25, 2026
2ac149c
test export build def
Mar 25, 2026
439b721
first golden-master test and moved export_e2e to generic test
Mar 25, 2026
5579a83
update test simple custom build def to use repo diff
Mar 25, 2026
a375e24
update test build defs for in file and extra child
Mar 25, 2026
10a2441
update deps test
Mar 25, 2026
c90e3ba
update multiple targets and preload tests
Mar 25, 2026
91da154
update go tests
Mar 25, 2026
d0cd6b7
update generic tests
Mar 25, 2026
41a345e
remove old repos
Mar 25, 2026
b66f632
doc string
Mar 26, 2026
0f8622f
linter
Mar 26, 2026
c7f86a4
improve performance of whatinputs (#3495)
goddenrich Mar 11, 2026
9bfc747
Update golang version in testing docker images (#3499)
SpangleLabs Mar 18, 2026
8f83272
Update the image pushing docs because the github docs suck (#3501)
toastwaffle Mar 19, 2026
160e332
Add `ljust` and `rjust` methods to `str` built-in type (#3502)
chrisnovakovic Mar 23, 2026
1ac7ff4
Tag v17.29.0 (#3503)
chrisnovakovic Mar 23, 2026
1579e2c
Fix padding direction in `str.ljust` and `str.rjust` (#3504)
chrisnovakovic Mar 23, 2026
8535c08
Tag v17.29.1 (#3505)
chrisnovakovic Mar 23, 2026
3809a71
Update homebrew-please CircleCI trigger from v1.1 to v2 API (#3507)
sean- Mar 25, 2026
3e3b738
Bump the perf-test to 2xlarge (#3508)
toastwaffle Mar 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions test/build_defs/test.build_defs
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
# Runs e2e tests against please in a specified repo
def please_repo_e2e_test(
name: str,
plz_command: str,
plz_command: str|list,
repo: str,
data: dict={},
deps: list=[],
defer_cmd=[],
tools: dict={},
tool_content_checker = ["//test/build_defs:content_checker"],
expected_failure: bool = False,
expected_output: dict = {},
expect_output_contains: dict = {},
expect_output_doesnt_contain: dict = {},
labels: list = [],
):
plz_command = plz_command.replace("plz ", "$TOOLS_PLEASE ")
if expected_failure:
plz_command += "; [ ! $? -eq 0 ]"

test_cmd = [
"cp $DATA_BASE_CONFIG $DATA_REPO",
"cd $DATA_REPO",
plz_command,
]

if isinstance(plz_command, str):
plz_command = [plz_command]

for cmd in plz_command:
cmd = cmd.replace("plz ", "$TOOLS_PLEASE ")
if expected_failure:
cmd += "; [ ! $? -eq 0 ]"
test_cmd += [cmd]

if expected_output:
test_cmd += [f"$TOOLS_CONTENT_CHECKER '{o}' '{c}'" for o, c in expected_output.items()]

Expand All @@ -31,13 +37,17 @@ def please_repo_e2e_test(
if expect_output_doesnt_contain:
test_cmd += [f'_STR="$(cat {o})" _SUBSTR="{c}" && if [ -z "${_STR##*$_SUBSTR*}" ]; then echo "$_STR"; exit 1; fi' for o, c in expect_output_doesnt_contain.items()]

# defer commands should be added last
if defer_cmd:
test_cmd += defer_cmd

test_cmd = ' && '.join(test_cmd)

data["REPO"] = [repo]
data["BASE_CONFIG"] = ["//test/build_defs:base_config"]

tools["PLEASE"] = ["//package:installed_files|please"]
tools["CONTENT_CHECKER"] = ["//test/build_defs:content_checker"]
tools["CONTENT_CHECKER"] = tool_content_checker

return gentest(
name = name,
Expand Down
180 changes: 180 additions & 0 deletions test/export/BUILD
Original file line number Diff line number Diff line change
@@ -1,6 +1,186 @@
subinclude("//test/build_defs")

sh_binary(
name = "build_statement_checker",
main = "build_statement_checker.sh",
)

# Runs an export e2e test. It purposely tests for different aspect (e.g. BUILD files, sources)
# within the same test to avoid the overhead of exporting multiple times.
def please_export_e2e_test(
name:str,
export_targets:list,
repo:str,
cmd_on_export:list=[],
expect_output_contains:dict={},
include_go:bool=False,
strict_check_build_stmt:bool=False, # requires match for all BUILD statements in exported file
EXPORT_DIR="plz-out/plzexport",
BUILD_FILE="BUILD_FILE"):
please_targets = ["//" + t for t in export_targets]
plz_command_targets = " ".join(please_targets)
tools = {}
config_override = []
strict_opt = "--strict" if strict_check_build_stmt else ""

if include_go:
config_override += ["-o plugin.go.gotool:$TOOLS_GO"]
tools["GO"] = [CONFIG.GO.GO_TOOL]
config_override = " ".join(config_override)

plz_command = [
# Export command
f"plz {config_override} export --output {EXPORT_DIR} {plz_command_targets}",
# Tests building the exported target which in turn ensures the sources are included
f"plz {config_override} --repo_root=$(plz query reporoot)/{EXPORT_DIR} build {plz_command_targets}",
] + [f"plz {config_override} --repo_root=$(plz query reporoot)/{EXPORT_DIR} {cmd}" for cmd in cmd_on_export]

defer_cmd = []
for export_target in export_targets:
file_path, target_name = export_target.split(":")
build_file = f"{file_path}/{BUILD_FILE}"

# Tests the contents of the exported BUILD file
defer_cmd += [f'$TOOLS_BUILD_STATEMENT_CHECKER {strict_opt} --exported "{EXPORT_DIR}/{build_file}" --original "{build_file}" --target "{target_name}"']
tools["BUILD_STATEMENT_CHECKER"] = "//test/export:build_statement_checker"

if expect_output_contains:
updated_expect_output = {}
for key, val in expect_output_contains.items():
updated_expect_output[f"{EXPORT_DIR}/{key}"] = val
expect_output_contains = updated_expect_output

return please_repo_e2e_test(
name = name,
defer_cmd = defer_cmd,
expect_output_contains = expect_output_contains,
plz_command = plz_command,
repo = repo,
tools = tools,
)

# Export a target generated by a native genrule target and trim unused build statements.
please_export_e2e_test(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please could you move each of these please_export_e2e_test targets to a BUILD file alongside the test/expected repos?

name = "export_native_genrule",
export_targets = ["test_builtins:native_genrule"],
repo = "repo",
strict_check_build_stmt = True,
)

# Export a target generated by a custom build def.
please_export_e2e_test(
name = "export_simple_custom_target",
export_targets = ["test_custom_defs:simple_custom_target"],
repo = "repo",
# TODO Enable strict check after after support for trimming subincludes #3496
# strict_check_build_stmt = True,
)

# Export a target generated by a custom build def.
please_export_e2e_test(
name = "export_standard_children_custom_target",
cmd_on_export = [
# Child of build def
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's avoid referring to children, as that's not well-defined terminology. adjacent_targets_in_build_def is probably better naming for this case, and the fact that one is a "child" of the other doesn't make a difference.

What you're essentially capturing here is that a target A created in a build def alongside the target B that we're trying to export will result in both A and B being buildable

"build //test_custom_defs:standard_children_custom_target#child",
],
export_targets = ["test_custom_defs:standard_children_custom_target"],
repo = "repo",
# TODO Enable strict check after after support for trimming subincludes #3496
# strict_check_build_stmt = True,
)

# Export a target that depends on another target.
please_export_e2e_test(
name = "export_deps",
export_targets = ["test_deps:dep2"],
repo = "repo",
)

# Test multiple targets.
please_export_e2e_test(
name = "export_multiple",
export_targets = [
"test_multiple:target1",
"test_multiple:target2",
],
repo = "repo",
)

# Export a target from a repo that preloads a build def.
# This test purposely doesn't use the custom def but checks that the source files are still included.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# This test purposely doesn't use the custom def but checks that the source files are still included.
# The exported target doesn't use the preloaded build def, but the preloaded build def is still present in the export.

please_export_e2e_test(
name = "export_preload_genrule",
expect_output_contains = {
# validating that export includes preloaded build def files
"build_defs/preloaded.build_defs": "preloaded_target",
"build_defs/BUILD_FILE": "preloaded_build_def",
},
export_targets = ["test:native_not_including_preloaded"],
repo = "repo_preload",
strict_check_build_stmt = True,
)

# Export a custom target from a repo that preloads the build def used.
please_export_e2e_test(
name = "export_preload_use_preloaded_def",
export_targets = ["test:use_preloaded_def"],
repo = "repo_preload",
strict_check_build_stmt = True,
)

# Test go binary target.
please_export_e2e_test(
name = "export_go_binary",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other than the target being exported moving to a subdirectory, I don't see any behavioural difference between this and export_go_target_with_go_dep

export_targets = ["test_go_bin:dummy"],
include_go = True,
repo = "repo_go",
strict_check_build_stmt = True,
)

# Test native target with go third_party dependency.
please_export_e2e_test(
name = "export_native_target_with_go_dep",
export_targets = ["test_go_dep:genrule_go_dep"],
include_go = True,
repo = "repo_go",
# TODO Enable strict check after after support for trimming subincludes #3496
# strict_check_build_stmt = True,
)

# Test go target with go third_party dependency.
please_export_e2e_test(
name = "export_go_target_with_go_dep",
export_targets = ["test_go_dep:bin_go_dep"],
include_go = True,
repo = "repo_go",
strict_check_build_stmt = True,
)

# Generic catch-all test on internal repo.
plz_e2e_test(
name = "export_src_please_test",
cmd = "plz export --output plz-out/plzexport //src/core && plz --repo_root=$(plz query reporoot)/plz-out/plzexport build //src/core",
)

# Test outputs export.
please_repo_e2e_test(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you not use please_export_e2e_test here? I think both would be clearer as golden-master tests

name = "export_outputs",
defer_cmd = [
'test -f "plz-out/plzexport/some_output.txt"',
'test ! -f "plz-out/plzexport/BUILD_FILE"',
],
plz_command = "plz export outputs --output plz-out/plzexport //test_outputs:out_target",
repo = "repo",
)

# Test --notrim flag.
please_repo_e2e_test(
name = "export_notrim",
defer_cmd = [
'test -f "plz-out/plzexport/test_notrim/file2.txt"',
'test -f "plz-out/plzexport/test_notrim/BUILD_FILE"',
'grep -q \'name = "unrelated"\' "plz-out/plzexport/test_notrim/BUILD_FILE"',
],
plz_command = "plz export --notrim --output plz-out/plzexport //test_notrim:target1",
repo = "repo",
)
119 changes: 119 additions & 0 deletions test/export/build_statement_checker.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#!/usr/bin/env bash
set -euo pipefail

strict_match=false
exported_file=""
original_file=""
target_name=""

while [[ "$#" -gt 0 ]]; do
case $1 in
--strict)
strict_match=true
shift
;;
--exported)
exported_file=$2
shift 2
;;
--original)
original_file=$2
shift 2
;;
--target)
target_name=$2
shift 2
;;
-*)
echo "Unknown option: $1" >&2
exit 1
;;
*)
echo "Unknown argument: $1" >&2
exit 1
;;
esac
done

if [[ -z "$exported_file" ]] || [[ -z "$original_file" ]] || [[ -z "$target_name" ]]; then
echo "Usage: $0 [--strict] --exported <file> --original <file> --target <name>" >&2
exit 1
fi

if [[ ! -f "$exported_file" ]]; then
echo "$exported_file doesnt exist" >&2
exit 1
fi

if [[ ! -f "$original_file" ]]; then
echo "$original_file doesnt exist" >&2
exit 1
fi

readonly START_DELIM="# BUILD_STMT_START"
readonly END_DELIM="# BUILD_STMT_END"

# Using awk to extract statement blocks directly into an array.
blocks=()
while IFS= read -r -d '' block; do
blocks+=("$block")
done < <(awk -v id="$target_name" -v start_delim="$START_DELIM" -v end_delim="$END_DELIM" '
BEGIN { in_block = 0; block = "" }
$0 == start_delim " " id {
in_block = 1;
block = "";
next;
}
$0 == end_delim {
if (in_block) {
in_block = 0;
printf "%s\0", block;
}
next;
}
in_block {
block = block ? block "\n" $0 : $0
}
' "$original_file")

if [[ ${#blocks[@]} -eq 0 ]]; then
echo "Failed to pull original content for $target_name" >&2
exit 1
fi

# Ensure that ALL required blocks are present in the generated file
for block_content in "${blocks[@]}"; do
if ! grep -Fq "$block_content" "$exported_file"; then
printf '%s\n%s\n%s\n%s\n%s\n%s\n' \
"BUILD statements mismatch" \
"${exported_file} doesnt contain" \
"${block_content}" \
"---- it contains ----" \
"$(cat "$exported_file")" \
"---- EOF ----" >&2
exit 1
fi
done

# If --strict is enabled, ensure ONLY these blocks are present
# (ignoring all whitespace, newlines and comments).
if [[ "$strict_match" == true ]]; then
concatenated_blocks=""
for block_content in "${blocks[@]}"; do
concatenated_blocks="${concatenated_blocks}${block_content}"
done

stripped_blocks=$(echo -n "$concatenated_blocks" | sed 's/#.*//' | tr -d ' \t\n\r')
stripped_exported_file=$(sed 's/#.*//' "$exported_file" | tr -d ' \t\n\r')

if [[ "$stripped_blocks" != "$stripped_exported_file" ]]; then
printf '%s\n' "Strict match failed: exported file contains extra or out-of-order content." >&2
printf '%s\n' "---- Expected (stripped) ----" >&2
printf '%s\n' "$stripped_blocks" >&2
printf '%s\n' "---- Got (stripped) ----" >&2
printf '%s\n' "$stripped_exported_file" >&2
exit 1
fi
fi

exit 0
Empty file added test/export/repo/.plzconfig
Empty file.
16 changes: 16 additions & 0 deletions test/export/repo/test_builtins/BUILD_FILE
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# BUILD_STMT_START native_genrule
genrule(
name = "native_genrule",
srcs = ["file.txt"],
outs = ["file.wordcount"],
cmd = "wc $SRCS > $OUT",
)
# BUILD_STMT_END

# Du mmy target to be trimmed
genrule(
name = "dummy",
srcs = ["file.txt"],
outs = ["dummy"],
cmd = "cat $SRCS > $OUT",
)
1 change: 1 addition & 0 deletions test/export/repo/test_builtins/file.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Test source file
Loading