/** * Returns mutable map of default testing shell environment. By itself it is incomplete and is * modified further by the specific test strategy implementations (mostly due to the fact that * environments used locally and remotely are different). */ protected Map<String, String> getDefaultTestEnvironment(TestRunnerAction action) { Map<String, String> env = new HashMap<>(); env.putAll(action.getConfiguration().getLocalShellEnvironment()); env.remove("LANG"); env.put("TZ", "UTC"); env.put("TEST_SIZE", action.getTestProperties().getSize().toString()); env.put("TEST_TIMEOUT", Integer.toString(getTimeout(action))); if (action.isSharded()) { env.put("TEST_SHARD_INDEX", Integer.toString(action.getShardNum())); env.put( "TEST_TOTAL_SHARDS", Integer.toString(action.getExecutionSettings().getTotalShards())); } // When we run test multiple times, set different TEST_RANDOM_SEED values for each run. if (action.getConfiguration().getRunsPerTestForLabel(action.getOwner().getLabel()) > 1) { env.put("TEST_RANDOM_SEED", Integer.toString(action.getRunNumber() + 1)); } String testFilter = action.getExecutionSettings().getTestFilter(); if (testFilter != null) { env.put(TEST_BRIDGE_TEST_FILTER_ENV, testFilter); } return env; }
/** * Generates a command line to run for the test action, taking into account coverage and {@code * --run_under} settings. * * @param testScript the setup script that invokes the test * @param coverageScript a script interjected between setup script and rest of command line to * collect coverage data. If this is an empty string, it is ignored. * @param testAction The test action. * @return the command line as string list. */ protected List<String> getArgs( String testScript, String coverageScript, TestRunnerAction testAction) { List<String> args = Lists.newArrayList(); if (OS.getCurrent() == OS.WINDOWS) { args.add(testAction.getShExecutable().getPathString()); args.add("-c"); args.add("$0 $*"); } args.add(testScript); TestTargetExecutionSettings execSettings = testAction.getExecutionSettings(); List<String> execArgs = new ArrayList<>(); if (!coverageScript.isEmpty() && isCoverageMode(testAction)) { execArgs.add(coverageScript); } // Execute the test using the alias in the runfiles tree, as mandated by // the Test Encyclopedia. execArgs.add(execSettings.getExecutable().getRootRelativePath().getPathString()); execArgs.addAll(execSettings.getArgs()); // Insert the command prefix specified by the "--run_under=<command-prefix>" option, // if any. if (execSettings.getRunUnder() == null) { args.addAll(execArgs); } else if (execSettings.getRunUnderExecutable() != null) { args.add(execSettings.getRunUnderExecutable().getRootRelativePath().getPathString()); args.addAll(execSettings.getRunUnder().getOptions()); args.addAll(execArgs); } else { args.add(testAction.getConfiguration().getShellExecutable().getPathString()); args.add("-c"); String runUnderCommand = ShellEscaper.escapeString(execSettings.getRunUnder().getCommand()); Path fullySpecified = SearchPath.which( SearchPath.parse( testAction.getTestLog().getPath().getFileSystem(), clientEnv.get("PATH")), runUnderCommand); if (fullySpecified != null) { runUnderCommand = fullySpecified.toString(); } args.add( runUnderCommand + ' ' + ShellEscaper.escapeJoinAll( Iterables.concat(execSettings.getRunUnder().getOptions(), execArgs))); } return args; }
/** * Returns the number of attempts specific test action can be retried. * * <p>For rules with "flaky = 1" attribute, this method will return 3 unless --flaky_test_attempts * option is given and specifies another value. */ @VisibleForTesting /* protected */ public int getTestAttempts(TestRunnerAction action) { if (executionOptions.testAttempts == -1) { return action.getTestProperties().isFlaky() ? 3 : 1; } else { return executionOptions.testAttempts; } }
/** * Ensure the runfiles tree exists and is consistent with the TestAction's manifest * ($0.runfiles_manifest), bringing it into consistency if not. The contents of the output file * $0.runfiles/MANIFEST, if it exists, are used a proxy for the set of existing symlinks, to avoid * the need for recursion. */ private static void updateLocalRunfilesDirectory( TestRunnerAction testAction, Path runfilesDir, ActionExecutionContext actionExecutionContext, BinTools binTools, ImmutableMap<String, String> shellEnvironment, boolean enableRunfiles) throws ExecException, InterruptedException { Executor executor = actionExecutionContext.getExecutor(); TestTargetExecutionSettings execSettings = testAction.getExecutionSettings(); try { // Avoid rebuilding the runfiles directory if the manifest in it matches the input manifest, // implying the symlinks exist and are already up to date. if (Arrays.equals( runfilesDir.getRelative("MANIFEST").getMD5Digest(), execSettings.getInputManifest().getPath().getMD5Digest())) { return; } } catch (IOException e1) { // Ignore it - we will just try to create runfiles directory. } executor .getEventHandler() .handle( Event.progress( "Building runfiles directory for '" + execSettings.getExecutable().prettyPrint() + "'.")); new SymlinkTreeHelper(execSettings.getInputManifest().getPath(), runfilesDir, false) .createSymlinks( testAction, actionExecutionContext, binTools, shellEnvironment, enableRunfiles); executor.getEventHandler().handle(Event.progress(testAction.getProgressMessage())); }
/** * Returns the runfiles directory associated with the test executable, creating/updating it if * necessary and --build_runfile_links is specified. */ protected static Path getLocalRunfilesDirectory( TestRunnerAction testAction, ActionExecutionContext actionExecutionContext, BinTools binTools, ImmutableMap<String, String> shellEnvironment, boolean enableRunfiles) throws ExecException, InterruptedException { TestTargetExecutionSettings execSettings = testAction.getExecutionSettings(); // If the symlink farm is already created then return the existing directory. If not we // need to explicitly build it. This can happen when --nobuild_runfile_links is supplied // as a flag to the build. if (execSettings.getRunfilesSymlinksCreated()) { return execSettings.getRunfilesDir(); } // TODO(bazel-team): Should we be using TestTargetExecutionSettings#getRunfilesDir() here over // generating the directory ourselves? Path program = execSettings.getExecutable().getPath(); Path runfilesDir = program.getParentDirectory().getChild(program.getBaseName() + ".runfiles"); // Synchronize runfiles tree generation on the runfiles manifest artifact. // This is necessary, because we might end up with multiple test runner actions // trying to generate same runfiles tree in case of --runs_per_test > 1 or // local test sharding. long startTime = Profiler.nanoTimeMaybe(); synchronized (execSettings.getInputManifest()) { Profiler.instance().logSimpleTask(startTime, ProfilerTask.WAIT, testAction); updateLocalRunfilesDirectory( testAction, runfilesDir, actionExecutionContext, binTools, shellEnvironment, enableRunfiles); } return runfilesDir; }
/** @return The test label. */ public String getLabel() { return Label.print(testAction.getOwner().getLabel()); }
/** * Gets the test name in a user-friendly format. Will generally include the target name and shard * number, if applicable. * * @return The test name. */ public String getTestName() { return testAction.getTestName(); }
/** @return The test status artifact. */ public Artifact getTestStatusArtifact() { // these artifacts are used to keep track of the number of pending and completed tests. return testAction.getCacheStatusArtifact(); }
/** @return Coverage data artifact, if available and null otherwise. */ public PathFragment getCoverageData() { if (data.getHasCoverage()) { return testAction.getCoverageData().getExecPath(); } return null; }
/** * @return The test log path. Note, that actual log file may no longer correspond to this artifact * - use getActualLogPath() method if you need log location. */ public Path getTestLogPath() { return testAction.getTestLog().getPath(); }
/** * @return Total number of test shards. 0 means no sharding, whereas 1 means degenerate sharding. */ public int getTotalShards() { return testAction.getExecutionSettings().getTotalShards(); }
/** @return The test shard number. */ public int getShardNum() { return testAction.getShardNum(); }
/** Returns true if coverage data should be gathered. */ protected static boolean isCoverageMode(TestRunnerAction action) { return action.getCoverageData() != null; }
/** * Returns timeout value in seconds that should be used for the given test action. We always use * the "categorical timeouts" which are based on the --test_timeout flag. A rule picks its timeout * but ends up with the same effective value as all other rules in that bucket. */ protected final int getTimeout(TestRunnerAction testAction) { return executionOptions.testTimeout.get(testAction.getTestProperties().getTimeout()); }