Merge "Export some board config vars to autogenerate android_info.prop" into main
diff --git a/ci/test_discovery_agent.py b/ci/test_discovery_agent.py
index 89d35d7..4eed28d 100644
--- a/ci/test_discovery_agent.py
+++ b/ci/test_discovery_agent.py
@@ -11,18 +11,33 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+"""Test discovery agent that uses TradeFed to discover test artifacts."""
+import glob
+import json
+import logging
+import os
+import subprocess
+import buildbot
+
+
class TestDiscoveryAgent:
"""Test discovery agent."""
- _AOSP_TRADEFED_PREBUILT_JAR_RELATIVE_PATH = (
- "tools/tradefederation/prebuilts/filegroups/tradefed/"
+ _TRADEFED_PREBUILT_JAR_RELATIVE_PATH = (
+ "vendor/google_tradefederation/prebuilts/filegroups/google-tradefed/"
)
+ _TRADEFED_NO_POSSIBLE_TEST_DISCOVERY_KEY = "NoPossibleTestDiscovery"
+
+ _TRADEFED_TEST_ZIP_REGEXES_LIST_KEY = "TestZipRegexes"
+
+ _TRADEFED_DISCOVERY_OUTPUT_FILE_NAME = "test_discovery_agent.txt"
+
def __init__(
self,
tradefed_args: list[str],
- test_mapping_zip_path: str,
- tradefed_jar_revelant_files_path: str = _AOSP_TRADEFED_PREBUILT_JAR_RELATIVE_PATH,
+ test_mapping_zip_path: str = "",
+ tradefed_jar_revelant_files_path: str = _TRADEFED_PREBUILT_JAR_RELATIVE_PATH,
):
self.tradefed_args = tradefed_args
self.test_mapping_zip_path = test_mapping_zip_path
@@ -34,7 +49,46 @@
Returns:
A list of test zip regexes that TF is going to try to pull files from.
"""
- return []
+ test_discovery_output_file_name = os.path.join(
+ buildbot.OutDir(), self._TRADEFED_DISCOVERY_OUTPUT_FILE_NAME
+ )
+ with open(
+ test_discovery_output_file_name, mode="w+t"
+ ) as test_discovery_output_file:
+ java_args = []
+ java_args.append("prebuilts/jdk/jdk21/linux-x86/bin/java")
+ java_args.append("-cp")
+ java_args.append(
+ self.create_classpath(self.tradefed_jar_relevant_files_path)
+ )
+ java_args.append(
+ "com.android.tradefed.observatory.TestZipDiscoveryExecutor"
+ )
+ java_args.extend(self.tradefed_args)
+ env = os.environ.copy()
+ env.update({"DISCOVERY_OUTPUT_FILE": test_discovery_output_file.name})
+ logging.info(f"Calling test discovery with args: {java_args}")
+ try:
+ result = subprocess.run(args=java_args, env=env, text=True, check=True)
+ logging.info(f"Test zip discovery output: {result.stdout}")
+ except subprocess.CalledProcessError as e:
+ raise TestDiscoveryError(
+ f"Failed to run test discovery, strout: {e.stdout}, strerr:"
+ f" {e.stderr}, returncode: {e.returncode}"
+ )
+ data = json.loads(test_discovery_output_file.read())
+ logging.info(f"Test discovery result file content: {data}")
+ if (
+ self._TRADEFED_NO_POSSIBLE_TEST_DISCOVERY_KEY in data
+ and data[self._TRADEFED_NO_POSSIBLE_TEST_DISCOVERY_KEY]
+ ):
+ raise TestDiscoveryError("No possible test discovery")
+ if (
+ data[self._TRADEFED_TEST_ZIP_REGEXES_LIST_KEY] is None
+ or data[self._TRADEFED_TEST_ZIP_REGEXES_LIST_KEY] is []
+ ):
+ raise TestDiscoveryError("No test zip regexes returned")
+ return data[self._TRADEFED_TEST_ZIP_REGEXES_LIST_KEY]
def discover_test_modules(self) -> list[str]:
"""Discover test modules from TradeFed.
@@ -44,3 +98,24 @@
TradeFed test args.
"""
return []
+
+ def create_classpath(self, directory):
+ """Creates a classpath string from all .jar files in the given directory.
+
+ Args:
+ directory: The directory to search for .jar files.
+
+ Returns:
+ A string representing the classpath, with jar files separated by the
+ OS-specific path separator (e.g., ':' on Linux/macOS, ';' on Windows).
+ """
+ jar_files = glob.glob(os.path.join(directory, "*.jar"))
+ return os.pathsep.join(jar_files)
+
+
+class TestDiscoveryError(Exception):
+ """A TestDiscoveryErrorclass."""
+
+ def __init__(self, message):
+ super().__init__(message)
+ self.message = message
diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk
index 35d35cd..26fe1da 100644
--- a/core/android_soong_config_vars.mk
+++ b/core/android_soong_config_vars.mk
@@ -71,6 +71,11 @@
# The default value of ART_BUILD_HOST_DEBUG is true
$(call soong_config_set_bool,art_module,art_build_host_debug,$(if $(filter false,$(ART_BUILD_HOST_DEBUG)),false,true))
+# For chre
+$(call soong_config_set_bool,chre,chre_daemon_lama_enabled,$(if $(filter true,$(CHRE_DAEMON_LPMA_ENABLED)),true,false))
+$(call soong_config_set_bool,chre,chre_dedicated_transport_channel_enabled,$(if $(filter true,$(CHRE_DEDICATED_TRANSPORT_CHANNEL_ENABLED)),true,false))
+$(call soong_config_set_bool,chre,chre_log_atom_extension_enabled,$(if $(filter true,$(CHRE_LOG_ATOM_EXTENSION_ENABLED)),true,false))
+
ifdef TARGET_BOARD_AUTO
$(call add_soong_config_var_value, ANDROID, target_board_auto, $(TARGET_BOARD_AUTO))
endif
diff --git a/core/tasks/general-tests-shared-libs.mk b/core/tasks/general-tests-shared-libs.mk
new file mode 100644
index 0000000..2405140
--- /dev/null
+++ b/core/tasks/general-tests-shared-libs.mk
@@ -0,0 +1,52 @@
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.PHONY: general-tests-shared-libs
+
+intermediates_dir := $(call intermediates-dir-for,PACKAGING,general-tests-shared-libs)
+
+general_tests_shared_libs_zip := $(PRODUCT_OUT)/general-tests_host-shared-libs.zip
+
+# Filter shared entries between general-tests and device-tests's HOST_SHARED_LIBRARY.FILES,
+# to avoid warning about overriding commands.
+my_host_shared_lib_for_general_tests := \
+ $(foreach m,$(filter $(COMPATIBILITY.device-tests.HOST_SHARED_LIBRARY.FILES),\
+ $(COMPATIBILITY.general-tests.HOST_SHARED_LIBRARY.FILES)),$(call word-colon,2,$(m)))
+my_general_tests_shared_lib_files := \
+ $(filter-out $(COMPATIBILITY.device-tests.HOST_SHARED_LIBRARY.FILES),\
+ $(COMPATIBILITY.general-tests.HOST_SHARED_LIBRARY.FILES))
+
+my_host_shared_lib_for_general_tests += $(call copy-many-files,$(my_general_tests_shared_lib_files))
+
+$(general_tests_shared_libs_zip) : PRIVATE_INTERMEDIATES_DIR := $(intermediates_dir)
+$(general_tests_shared_libs_zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_general_tests)
+$(general_tests_shared_libs_zip) : PRIVATE_general_host_shared_libs_zip := $(general_tests_shared_libs_zip)
+$(general_tests_shared_libs_zip) : $(my_host_shared_lib_for_general_tests) $(SOONG_ZIP)
+ rm -rf $(PRIVATE_INTERMEDIATES_DIR)
+ mkdir -p $(PRIVATE_INTERMEDIATES_DIR) $(PRIVATE_INTERMEDIATES_DIR)/tools
+ $(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \
+ echo $$shared_lib >> $(PRIVATE_INTERMEDIATES_DIR)/shared-libs.list; \
+ done
+ grep $(HOST_OUT_TESTCASES) $(PRIVATE_INTERMEDIATES_DIR)/shared-libs.list > $(PRIVATE_INTERMEDIATES_DIR)/host-shared-libs.list || true
+ $(SOONG_ZIP) -d -o $(PRIVATE_general_host_shared_libs_zip) \
+ -P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host-shared-libs.list
+
+general-tests-shared-libs: $(general_tests_shared_libs_zip)
+$(call dist-for-goals, general-tests-shared-libs, $(general_tests_shared_libs_zip))
+
+$(call declare-1p-container,$(general_tests_shared_libs_zip),)
+$(call declare-container-license-deps,$(general_tests_shared_libs_zip),$(my_host_shared_lib_for_general_tests),$(PRODUCT_OUT)/:/)
+
+intermediates_dir :=
+general_tests_shared_libs_zip :=
diff --git a/core/tasks/general-tests.mk b/core/tasks/general-tests.mk
index d3e653f..d6fc072 100644
--- a/core/tasks/general-tests.mk
+++ b/core/tasks/general-tests.mk
@@ -37,53 +37,22 @@
.PHONY: vts_kernel_ltp_tests
vts_kernel_ltp_tests: $(copy_ltp_tests)
-# Filter shared entries between general-tests and device-tests's HOST_SHARED_LIBRARY.FILES,
-# to avoid warning about overriding commands.
-my_host_shared_lib_for_general_tests := \
- $(foreach m,$(filter $(COMPATIBILITY.device-tests.HOST_SHARED_LIBRARY.FILES),\
- $(COMPATIBILITY.general-tests.HOST_SHARED_LIBRARY.FILES)),$(call word-colon,2,$(m)))
-my_general_tests_shared_lib_files := \
- $(filter-out $(COMPATIBILITY.device-tests.HOST_SHARED_LIBRARY.FILES),\
- $(COMPATIBILITY.general-tests.HOST_SHARED_LIBRARY.FILES))
+general_tests_shared_libs_zip := $(PRODUCT_OUT)/general-tests_host-shared-libs.zip
-my_host_shared_lib_for_general_tests += $(call copy-many-files,$(my_general_tests_shared_lib_files))
-
-my_general_tests_symlinks := \
- $(filter-out $(COMPATIBILITY.camera-hal-tests.SYMLINKS),\
- $(filter-out $(COMPATIBILITY.host-unit-tests.SYMLINKS),\
- $(COMPATIBILITY.general-tests.SYMLINKS)))
-
-my_symlinks_for_general_tests := $(foreach f,$(my_general_tests_symlinks),\
- $(strip $(eval _cmf_tuple := $(subst :, ,$(f))) \
- $(eval _cmf_dep := $(word 1,$(_cmf_tuple))) \
- $(eval _cmf_src := $(word 2,$(_cmf_tuple))) \
- $(eval _cmf_dest := $(word 3,$(_cmf_tuple))) \
- $(call symlink-file,$(_cmf_dep),$(_cmf_src),$(_cmf_dest)) \
- $(_cmf_dest)))
-
-
+$(general_tests_zip) : $(general_tests_shared_libs_zip)
$(general_tests_zip) : $(copy_ltp_tests)
$(general_tests_zip) : PRIVATE_KERNEL_LTP_HOST_OUT := $(kernel_ltp_host_out)
$(general_tests_zip) : PRIVATE_general_tests_list_zip := $(general_tests_list_zip)
$(general_tests_zip) : .KATI_IMPLICIT_OUTPUTS := $(general_tests_list_zip) $(general_tests_configs_zip)
$(general_tests_zip) : PRIVATE_TOOLS := $(general_tests_tools)
$(general_tests_zip) : PRIVATE_INTERMEDIATES_DIR := $(intermediates_dir)
-$(general_tests_zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_general_tests)
-$(general_tests_zip) : PRIVATE_SYMLINKS := $(my_symlinks_for_general_tests)
$(general_tests_zip) : PRIVATE_general_tests_configs_zip := $(general_tests_configs_zip)
-$(general_tests_zip) : $(COMPATIBILITY.general-tests.FILES) $(my_host_shared_lib_for_general_tests) $(COMPATIBILITY.general-tests.SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES) $(general_tests_tools) $(my_symlinks_for_general_tests) $(SOONG_ZIP)
+$(general_tests_zip) : $(COMPATIBILITY.general-tests.FILES) $(COMPATIBILITY.general-tests.SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES) $(general_tests_tools) $(SOONG_ZIP)
rm -rf $(PRIVATE_INTERMEDIATES_DIR)
rm -f $@ $(PRIVATE_general_tests_list_zip)
mkdir -p $(PRIVATE_INTERMEDIATES_DIR) $(PRIVATE_INTERMEDIATES_DIR)/tools
echo $(sort $(COMPATIBILITY.general-tests.FILES) $(COMPATIBILITY.general-tests.SOONG_INSTALLED_COMPATIBILITY_SUPPORT_FILES)) | tr " " "\n" > $(PRIVATE_INTERMEDIATES_DIR)/list
find $(PRIVATE_KERNEL_LTP_HOST_OUT) >> $(PRIVATE_INTERMEDIATES_DIR)/list
- for symlink in $(PRIVATE_SYMLINKS); do \
- echo $$symlink >> $(PRIVATE_INTERMEDIATES_DIR)/list; \
- done
- $(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \
- echo $$shared_lib >> $(PRIVATE_INTERMEDIATES_DIR)/shared-libs.list; \
- done
- grep $(HOST_OUT_TESTCASES) $(PRIVATE_INTERMEDIATES_DIR)/shared-libs.list > $(PRIVATE_INTERMEDIATES_DIR)/host-shared-libs.list || true
grep $(HOST_OUT_TESTCASES) $(PRIVATE_INTERMEDIATES_DIR)/list > $(PRIVATE_INTERMEDIATES_DIR)/host.list || true
grep $(TARGET_OUT_TESTCASES) $(PRIVATE_INTERMEDIATES_DIR)/list > $(PRIVATE_INTERMEDIATES_DIR)/target.list || true
grep -e .*\\.config$$ $(PRIVATE_INTERMEDIATES_DIR)/host.list > $(PRIVATE_INTERMEDIATES_DIR)/host-test-configs.list || true
@@ -93,7 +62,6 @@
-P host -C $(PRIVATE_INTERMEDIATES_DIR) -D $(PRIVATE_INTERMEDIATES_DIR)/tools \
-P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host.list \
-P target -C $(PRODUCT_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/target.list \
- -P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host-shared-libs.list \
-sha256
$(SOONG_ZIP) -d -o $(PRIVATE_general_tests_configs_zip) \
-P host -C $(HOST_OUT) -l $(PRIVATE_INTERMEDIATES_DIR)/host-test-configs.list \
@@ -114,6 +82,3 @@
general_tests_list_zip :=
general_tests_configs_zip :=
general_tests_shared_libs_zip :=
-my_host_shared_lib_for_general_tests :=
-my_symlinks_for_general_tests :=
-my_general_tests_shared_lib_files :=
\ No newline at end of file
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index 8f2f7e3..3a8eb72 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -18,6 +18,7 @@
PRODUCT_PACKAGES += \
abx \
aconfigd \
+ aconfigd-system \
adbd_system_api \
aflags \
am \
diff --git a/target/product/generic/Android.bp b/target/product/generic/Android.bp
index 017f8fb..80b84de 100644
--- a/target/product/generic/Android.bp
+++ b/target/product/generic/Android.bp
@@ -379,6 +379,7 @@
deps: [
"abx",
"aconfigd",
+ "aconfigd-system",
"aflags",
"am",
"android.software.credentials.prebuilt.xml", // generic_system
diff --git a/target/product/security/Android.bp b/target/product/security/Android.bp
index 69d19a3..ffbec06 100644
--- a/target/product/security/Android.bp
+++ b/target/product/security/Android.bp
@@ -40,4 +40,5 @@
adb_keys {
name: "adb_keys",
+ product_specific: true,
}
diff --git a/tools/aconfig/aconfig_storage_file/src/lib.rs b/tools/aconfig/aconfig_storage_file/src/lib.rs
index 1d92ba4..5dc5ef7 100644
--- a/tools/aconfig/aconfig_storage_file/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_file/src/lib.rs
@@ -228,10 +228,14 @@
/// Read and parse bytes as u8
pub fn read_u8_from_bytes(buf: &[u8], head: &mut usize) -> Result<u8, AconfigStorageError> {
- let val =
- u8::from_le_bytes(buf[*head..*head + 1].try_into().map_err(|errmsg| {
- BytesParseFail(anyhow!("fail to parse u8 from bytes: {}", errmsg))
- })?);
+ let val = u8::from_le_bytes(
+ buf.get(*head..*head + 1)
+ .ok_or(AconfigStorageError::BytesParseFail(anyhow!(
+ "fail to parse u8 from bytes: access out of bounds"
+ )))?
+ .try_into()
+ .map_err(|errmsg| BytesParseFail(anyhow!("fail to parse u8 from bytes: {}", errmsg)))?,
+ );
*head += 1;
Ok(val)
}
@@ -241,10 +245,16 @@
buf: &[u8],
head: &mut usize,
) -> Result<u16, AconfigStorageError> {
- let val =
- u16::from_le_bytes(buf[*head..*head + 2].try_into().map_err(|errmsg| {
- BytesParseFail(anyhow!("fail to parse u16 from bytes: {}", errmsg))
- })?);
+ let val = u16::from_le_bytes(
+ buf.get(*head..*head + 2)
+ .ok_or(AconfigStorageError::BytesParseFail(anyhow!(
+ "fail to parse u16 from bytes: access out of bounds"
+ )))?
+ .try_into()
+ .map_err(|errmsg| {
+ BytesParseFail(anyhow!("fail to parse u16 from bytes: {}", errmsg))
+ })?,
+ );
*head += 2;
Ok(val)
}
@@ -256,20 +266,32 @@
/// Read and parse bytes as u32
pub fn read_u32_from_bytes(buf: &[u8], head: &mut usize) -> Result<u32, AconfigStorageError> {
- let val =
- u32::from_le_bytes(buf[*head..*head + 4].try_into().map_err(|errmsg| {
- BytesParseFail(anyhow!("fail to parse u32 from bytes: {}", errmsg))
- })?);
+ let val = u32::from_le_bytes(
+ buf.get(*head..*head + 4)
+ .ok_or(AconfigStorageError::BytesParseFail(anyhow!(
+ "fail to parse u32 from bytes: access out of bounds"
+ )))?
+ .try_into()
+ .map_err(|errmsg| {
+ BytesParseFail(anyhow!("fail to parse u32 from bytes: {}", errmsg))
+ })?,
+ );
*head += 4;
Ok(val)
}
// Read and parse bytes as u64
pub fn read_u64_from_bytes(buf: &[u8], head: &mut usize) -> Result<u64, AconfigStorageError> {
- let val =
- u64::from_le_bytes(buf[*head..*head + 8].try_into().map_err(|errmsg| {
- BytesParseFail(anyhow!("fail to parse u64 from bytes: {}", errmsg))
- })?);
+ let val = u64::from_le_bytes(
+ buf.get(*head..*head + 8)
+ .ok_or(AconfigStorageError::BytesParseFail(anyhow!(
+ "fail to parse u64 from bytes: access out of bounds"
+ )))?
+ .try_into()
+ .map_err(|errmsg| {
+ BytesParseFail(anyhow!("fail to parse u64 from bytes: {}", errmsg))
+ })?,
+ );
*head += 8;
Ok(val)
}
@@ -280,8 +302,21 @@
head: &mut usize,
) -> Result<String, AconfigStorageError> {
let num_bytes = read_u32_from_bytes(buf, head)? as usize;
- let val = String::from_utf8(buf[*head..*head + num_bytes].to_vec())
- .map_err(|errmsg| BytesParseFail(anyhow!("fail to parse string from bytes: {}", errmsg)))?;
+ // TODO(opg): Document this limitation and check it when creating files.
+ if num_bytes > 1024 {
+ return Err(AconfigStorageError::BytesParseFail(anyhow!(
+ "fail to parse string from bytes, string is too long (found {}, max is 1024)",
+ num_bytes
+ )));
+ }
+ let val = String::from_utf8(
+ buf.get(*head..*head + num_bytes)
+ .ok_or(AconfigStorageError::BytesParseFail(anyhow!(
+ "fail to parse string from bytes: access out of bounds"
+ )))?
+ .to_vec(),
+ )
+ .map_err(|errmsg| BytesParseFail(anyhow!("fail to parse string from bytes: {}", errmsg)))?;
*head += num_bytes;
Ok(val)
}
@@ -533,6 +568,34 @@
};
#[test]
+ fn test_list_flags_with_missing_files_error() {
+ let flag_list_error = list_flags("does", "not", "exist").unwrap_err();
+ assert_eq!(
+ format!("{:?}", flag_list_error),
+ format!(
+ "FileReadFail(Failed to open file does: No such file or directory (os error 2))"
+ )
+ );
+ }
+
+ #[test]
+ fn test_list_flags_with_invalid_files_error() {
+ let invalid_bytes: [u8; 3] = [0; 3];
+ let package_table = write_bytes_to_temp_file(&invalid_bytes).unwrap();
+ let flag_table = write_bytes_to_temp_file(&invalid_bytes).unwrap();
+ let flag_value_list = write_bytes_to_temp_file(&invalid_bytes).unwrap();
+ let package_table_path = package_table.path().display().to_string();
+ let flag_table_path = flag_table.path().display().to_string();
+ let flag_value_list_path = flag_value_list.path().display().to_string();
+ let flag_list_error =
+ list_flags(&package_table_path, &flag_table_path, &flag_value_list_path).unwrap_err();
+ assert_eq!(
+ format!("{:?}", flag_list_error),
+ format!("BytesParseFail(fail to parse u32 from bytes: access out of bounds)")
+ );
+ }
+
+ #[test]
// this test point locks down the flag list api
fn test_list_flag() {
let package_table =
diff --git a/tools/edit_monitor/daemon_manager.py b/tools/edit_monitor/daemon_manager.py
index 9a0abb6..2775b58 100644
--- a/tools/edit_monitor/daemon_manager.py
+++ b/tools/edit_monitor/daemon_manager.py
@@ -84,7 +84,7 @@
"edit_monitor",
self.user_name,
"ENABLE_EDIT_MONITOR",
- "EDIT_MONITOR_ROLLOUT_PERCENTAGE",
+ 10,
):
logging.warning("Edit monitor is disabled, exiting...")
return
diff --git a/tools/edit_monitor/utils.py b/tools/edit_monitor/utils.py
index 1a3275c..b88949d 100644
--- a/tools/edit_monitor/utils.py
+++ b/tools/edit_monitor/utils.py
@@ -21,7 +21,7 @@
feature_name: str,
user_name: str,
enable_flag: str = None,
- rollout_flag: str = None,
+ rollout_percent: int = 100,
) -> bool:
"""Determine whether the given feature is enabled.
@@ -46,26 +46,8 @@
logging.info("feature: %s is enabled", feature_name)
return True
- if not rollout_flag:
- return True
-
hash_object = hashlib.sha256()
hash_object.update((user_name + feature_name).encode("utf-8"))
hash_number = int(hash_object.hexdigest(), 16) % 100
- roll_out_percentage = os.environ.get(rollout_flag, "0")
- try:
- percentage = int(roll_out_percentage)
- if percentage < 0 or percentage > 100:
- logging.warning(
- "Rollout percentage: %s out of range, disable the feature.",
- roll_out_percentage,
- )
- return False
- return hash_number < percentage
- except ValueError:
- logging.warning(
- "Invalid rollout percentage: %s, disable the feature.",
- roll_out_percentage,
- )
- return False
+ return hash_number < rollout_percent
diff --git a/tools/edit_monitor/utils_test.py b/tools/edit_monitor/utils_test.py
index 7d7e4b2..1c30aa1 100644
--- a/tools/edit_monitor/utils_test.py
+++ b/tools/edit_monitor/utils_test.py
@@ -46,60 +46,23 @@
)
)
- @mock.patch.dict(
- os.environ, {ROLLOUT_TEST_FEATURE_FLAG: 'invalid'}, clear=True
- )
- def test_feature_disabled_with_invalid_rollout_percentage(self):
- self.assertFalse(
- utils.is_feature_enabled(
- TEST_FEATURE,
- TEST_USER,
- ENABLE_TEST_FEATURE_FLAG,
- ROLLOUT_TEST_FEATURE_FLAG,
- )
- )
-
- @mock.patch.dict(os.environ, {ROLLOUT_TEST_FEATURE_FLAG: '101'}, clear=True)
- def test_feature_disabled_with_rollout_percentage_too_high(self):
- self.assertFalse(
- utils.is_feature_enabled(
- TEST_FEATURE,
- TEST_USER,
- ENABLE_TEST_FEATURE_FLAG,
- ROLLOUT_TEST_FEATURE_FLAG,
- )
- )
-
- @mock.patch.dict(os.environ, {ROLLOUT_TEST_FEATURE_FLAG: '-1'}, clear=True)
- def test_feature_disabled_with_rollout_percentage_too_low(self):
- self.assertFalse(
- utils.is_feature_enabled(
- TEST_FEATURE,
- TEST_USER,
- ENABLE_TEST_FEATURE_FLAG,
- ROLLOUT_TEST_FEATURE_FLAG,
- )
- )
-
- @mock.patch.dict(os.environ, {ROLLOUT_TEST_FEATURE_FLAG: '90'}, clear=True)
def test_feature_enabled_with_rollout_percentage(self):
self.assertTrue(
utils.is_feature_enabled(
TEST_FEATURE,
TEST_USER,
ENABLE_TEST_FEATURE_FLAG,
- ROLLOUT_TEST_FEATURE_FLAG,
+ 90,
)
)
- @mock.patch.dict(os.environ, {ROLLOUT_TEST_FEATURE_FLAG: '10'}, clear=True)
def test_feature_disabled_with_rollout_percentage(self):
self.assertFalse(
utils.is_feature_enabled(
TEST_FEATURE,
TEST_USER,
ENABLE_TEST_FEATURE_FLAG,
- ROLLOUT_TEST_FEATURE_FLAG,
+ 10,
)
)