diff --git a/net_util/tests/unix_tap.rs b/net_util/tests/unix_tap.rs index c874dd8f39..80681a3fa0 100644 --- a/net_util/tests/unix_tap.rs +++ b/net_util/tests/unix_tap.rs @@ -11,13 +11,11 @@ use net_util::MacAddress; use net_util::TapTCommon; #[test] -#[ignore = "Requires root privileges"] fn tap_create() { Tap::new(true, false).unwrap(); } #[test] -#[ignore = "Requires root privileges"] fn tap_configure() { let tap = Tap::new(true, false).unwrap(); let ip_addr: net::Ipv4Addr = "100.115.92.5".parse().unwrap(); @@ -32,7 +30,6 @@ fn tap_configure() { } #[test] -#[ignore = "Requires root privileges"] fn tap_enable() { let tap = Tap::new(true, false).unwrap(); diff --git a/tools/impl/test_config.py b/tools/impl/test_config.py index 4bc9ccdf70..a8196ecf80 100755 --- a/tools/impl/test_config.py +++ b/tools/impl/test_config.py @@ -42,6 +42,11 @@ class TestOption(enum.Enum): # This test needs longer than usual to run. LARGE = "large" + # Integration test that requires root privileges to execute. + # Note that this does not apply to unit tests, which will never be allowed privileged access + # to the system. + REQUIRES_ROOT = "requires_root" + # Configuration to restrict how and where tests of a certain crate can # be build and run. @@ -95,6 +100,7 @@ CRATE_OPTIONS: Dict[str, List[TestOption]] = { ], # b/181674144 "libvda": [TestOption.DO_NOT_RUN], # b/202293971 "sandbox": [TestOption.DO_NOT_RUN], + "net_util": [TestOption.REQUIRES_ROOT], } for name in WIN64_DISABLED_CRATES: diff --git a/tools/impl/test_runner.py b/tools/impl/test_runner.py index ba32d16604..02d503e02f 100644 --- a/tools/impl/test_runner.py +++ b/tools/impl/test_runner.py @@ -16,7 +16,7 @@ from typing import Dict, Iterable, List, NamedTuple, Optional from . import test_target, testvm from .common import all_tracked_files, very_verbose -from .test_config import BUILD_FEATURES, CRATE_OPTIONS, TestOption +from .test_config import CRATE_OPTIONS, TestOption from .test_target import TestTarget, Triple USAGE = """\ @@ -119,7 +119,9 @@ def get_workspace_excludes(build_triple: Triple): yield crate -def should_run_executable(executable: Executable, target: TestTarget, test_names: List[str]): +def should_run_executable( + executable: Executable, target: TestTarget, test_names: List[str], execute_as_root: bool +): arch = target.build_triple.arch options = CRATE_OPTIONS.get(executable.crate_name, []) if TestOption.DO_NOT_RUN in options: @@ -132,6 +134,8 @@ def should_run_executable(executable: Executable, target: TestTarget, test_names return False if TestOption.DO_NOT_RUN_ON_FOREIGN_KERNEL in options and not target.is_native: return False + if TestOption.REQUIRES_ROOT in options and not execute_as_root: + return False if test_names: for name in test_names: if fnmatch.fnmatch(executable.name, name): @@ -366,6 +370,8 @@ def execute_integration_test( """ args: List[str] = ["--test-threads=1"] binary_path = executable.binary_path + options = CRATE_OPTIONS.get(executable.crate_name, []) + execute_as_root = TestOption.REQUIRES_ROOT in options previous_attempts: List[ExecutableResults] = [] for i in range(1, attempts + 1): @@ -380,6 +386,7 @@ def execute_integration_test( generate_profile=True, stdout=None if VERBOSE else subprocess.PIPE, stderr=subprocess.STDOUT, + execute_as_root=execute_as_root, ) profile_files: List[Path] = [] if collect_coverage: @@ -528,6 +535,13 @@ def generate_lcov( ) +def sudo_is_passwordless(): + # Run with --askpass but no askpass set, succeeds only if passwordless sudo + # is available. + (ret, _) = subprocess.getstatusoutput("SUDO_ASKPASS=false sudo --askpass true") + return ret == 0 + + def main(): parser = argparse.ArgumentParser(usage=USAGE) parser.add_argument( @@ -581,6 +595,11 @@ def main(): "--crosvm-direct", action="store_true", ) + parser.add_argument( + "--no-root", + action="store_true", + help="Disables tests that require to be run as root.", + ) parser.add_argument( "--repeat", type=int, @@ -649,6 +668,7 @@ def main(): integration_test_target = test_target.TestTarget("vm:aarch64", build_target) elif str(build_target) == "x86_64-pc-windows-gnu" and os.name == "nt": integration_test_target = unit_test_target + args.no_root = True else: # Do not run integration tests in unrecognized scenarios. integration_test_target = None @@ -661,6 +681,14 @@ def main(): print("Unit Test target:", unit_test_target or "skip") print("Integration Test target:", integration_test_target or "skip") + if not args.no_root and integration_test_target and integration_test_target.is_host: + if not sudo_is_passwordless(): + print() + print("Prompting for sudo password to execute privileged tests.") + print("If you'd like to prevent this, use the --no-root option.") + subprocess.check_call(["sudo", "true"]) + print() + main_target = integration_test_target or unit_test_target if not main_target: return @@ -692,7 +720,7 @@ def main(): test_executables = [ e for e in executables - if e.is_test and should_run_executable(e, main_target, args.test_names) + if e.is_test and should_run_executable(e, main_target, args.test_names, not args.no_root) ] all_results: List[ExecutableResults] = [] diff --git a/tools/impl/test_target.py b/tools/impl/test_target.py index d6e47aa066..4f48033c15 100755 --- a/tools/impl/test_target.py +++ b/tools/impl/test_target.py @@ -392,6 +392,7 @@ def exec_file_on_target( args: List[str] = [], extra_files: List[Path] = [], generate_profile: bool = False, + execute_as_root: bool = False, **kwargs: Any, ): """Executes a file on the test target. @@ -432,11 +433,14 @@ def exec_file_on_target( env["LLVM_PROFILE_FILE"] = f"{profile_prefix}.%p" cmd_line = [*prefix, str(filepath), *args] + if execute_as_root: + cmd_line = ["sudo", *cmd_line] return subprocess.run( cmd_line, env=env, timeout=timeout, text=True, + shell=False, **kwargs, ) else: diff --git a/tools/presubmit b/tools/presubmit index 1540fe3c3f..75fb04777a 100755 --- a/tools/presubmit +++ b/tools/presubmit @@ -97,7 +97,7 @@ commands=( if [ "$ALL" == true ]; then commands+=( - "./tools/run_tests" + "./tools/run_tests --no-root" "./tools/run_tests --platform=mingw64" "./tools/clippy --platform=mingw64" "$(aarch64_wrapper) ./tools/run_tests --platform=aarch64" @@ -106,7 +106,7 @@ if [ "$ALL" == true ]; then ) elif [ "$QUICK" != true ]; then commands+=( - "./tools/run_tests" + "./tools/run_tests --no-root" "$(aarch64_wrapper) ./tools/run_tests --platform=aarch64 --unit-tests" "./tools/run_tests --platform=mingw64 --unit-tests" )