Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b3d989c0 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "finalization-test: add SDK finalization tests" into main

parents 185b8563 8e5e020b
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
package {
    default_applicable_licenses: ["Android-Apache-2.0"],
}

rust_test_host {
    name: "finalization-test",
    edition: "2021",
    clippy_lints: "android",
    lints: "android",
    srcs: [
        "test.rs",
    ],
    data: [
        ":all_release_configs",
    ],
    rustlibs: [
        "liball_release_configs_proto",
        "libprotobuf",
    ],
    test_suites: ["general-tests"],
}
+7 −0
Original line number Diff line number Diff line
{
  "postsubmit": [
    {
      "name": "finalization-test"
    }
  ]
}
+81 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.
 */

use all_release_configs_proto::build_flags_out::{ReleaseConfigArtifact, ReleaseConfigsArtifact};
use protobuf::Message;
use std::collections::HashMap;
use std::fs;

#[allow(dead_code)]
pub const FLAGS_WE_CARE_ABOUT: [&str; 4] = [
    "RELEASE_PLATFORM_SDK_VERSION",
    "RELEASE_PLATFORM_SDK_VERSION_FULL",
    "RELEASE_PLATFORM_VERSION",
    "RELEASE_PLATFORM_VERSION_CODENAME",
];

// A map of release-config -name -> map of flag-name -> flag-value
//
// Example access:
//
//   assert_eq!(BUILD_FLAGS["trunk"]["RELEASE_PLATFORM_SDK_VERSION"], 36)
#[allow(dead_code)]
pub type BuildFlagMap = HashMap<String, HashMap<String, String>>;

#[allow(dead_code)]
pub struct ReleaseConfigs {
    pub flags: BuildFlagMap,
    pub next: String,
}

impl ReleaseConfigs {
    #[allow(dead_code)]
    pub fn init() -> Self {
        let protobuf =
            fs::read("all_release_configs.pb").expect("Could not read all_release_configs.pb");
        let all_release_configs = ReleaseConfigsArtifact::parse_from_bytes(&protobuf[..])
            .expect("failed to parse protobuf as ReleaseConfigArtifact");

        let mut flags = HashMap::new();
        let mut next: Option<String> = None;

        // parse currently active release config
        parse_release_config(&mut flags, &all_release_configs.release_config);

        // parse the other release configs
        for release_config in all_release_configs.other_release_configs {
            parse_release_config(&mut flags, &release_config);
            if release_config.other_names.contains(&"next".to_string()) {
                assert!(next.is_none(), "next: multiple aliases");
                next = Some(release_config.name().to_string());
            }
        }

        ReleaseConfigs { flags, next: next.expect("next: missing alias") }
    }
}

fn parse_release_config(build_flag_map: &mut BuildFlagMap, release_config: &ReleaseConfigArtifact) {
    let x: HashMap<String, String> = release_config
        .flags
        .iter()
        .filter(|flag| FLAGS_WE_CARE_ABOUT.contains(&flag.flag_declaration.name()))
        .map(|flag| {
            (flag.flag_declaration.name().to_string(), flag.value.string_value().to_string())
        })
        .collect();
    build_flag_map.insert(release_config.name().to_string(), x);
}
+120 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.
 */

mod build_flags;

#[cfg(test)]
mod tests {
    use crate::build_flags::{ReleaseConfigs, FLAGS_WE_CARE_ABOUT};
    use std::sync::LazyLock;

    // the subset of build flags relevant for SDK finalization
    static RELEASE_CONFIGS: LazyLock<ReleaseConfigs> = LazyLock::new(ReleaseConfigs::init);

    fn sdk_version(release_config: &str) -> f32 {
        // use SDK_INT_FULL if set, otherwise fall back to SDK_INT
        let s = &RELEASE_CONFIGS.flags[release_config]["RELEASE_PLATFORM_SDK_VERSION_FULL"];
        if !s.is_empty() {
            s.parse::<f32>().unwrap_or_else(|_| {
                panic!(
                    "failed to parse RELEASE_PLATFORM_SDK_VERSION_FULL for {} ({}) as f32",
                    release_config, s
                )
            })
        } else {
            let s = &RELEASE_CONFIGS.flags[release_config]["RELEASE_PLATFORM_SDK_VERSION"];
            s.parse::<f32>().unwrap_or_else(|_| {
                panic!(
                    "failed to parse RELEASE_PLATFORM_SDK_VERSION for {} ({}) as f32",
                    release_config, s
                )
            })
        }
    }

    #[test]
    fn test_build_flags_in_trunk_and_trunk_staging_are_equal() {
        // invariant: the values of the flags (that this test cares about) in RELEASE_CONFIGS.flags are equal
        // across trunk and trunk_staging release configs
        //
        // this means that the rest of the tests can focus on trunk and ignore trunk_staging
        for flag in FLAGS_WE_CARE_ABOUT {
            assert_eq!(
                RELEASE_CONFIGS.flags["trunk"][flag], RELEASE_CONFIGS.flags["trunk_staging"][flag],
                "flag {} differenct across trunk and trunk_staging",
                flag,
            );
        }
    }

    #[test]
    fn test_trunk_is_never_rel() {
        // invariant: the codename in trunk is never REL: trunk is always bleeding edge and thus
        // always something later than the latest finalized (REL) platform
        assert_ne!(RELEASE_CONFIGS.flags["trunk"]["RELEASE_PLATFORM_VERSION_CODENAME"], "REL");
    }

    #[test]
    fn test_version_parity_if_next_is_not_rel() {
        // invariant: the version code of trunk and next are identical, unless next is REL: then
        // the version in trunk can be one less than the version in next (during the intermediate
        // state where next is REL but we haven't created prebuilts/sdk/<new-version> yet), or the
        // version in trunk is identical to the one in next
        let next = &RELEASE_CONFIGS.next;
        if RELEASE_CONFIGS.flags[next]["RELEASE_PLATFORM_VERSION_CODENAME"] != "REL" {
            // expect the versions to be identical
            assert_eq!(
                RELEASE_CONFIGS.flags[next]["RELEASE_PLATFORM_SDK_VERSION_FULL"],
                RELEASE_CONFIGS.flags["trunk"]["RELEASE_PLATFORM_SDK_VERSION_FULL"]
            );
        } else {
            // make sure the version in trunk is less or equal to that of next
            //
            // ideally this should check that trunk is at most one version behind next, but we
            // can't tell what that means, so let's settle for the weaker guarantee of "less or
            // equal"
            assert!(sdk_version("trunk") <= sdk_version(next));
        }
    }

    #[test]
    fn test_version_and_version_full_parity() {
        // invariant: for the release configs that set RELEASE_PLATFORM_SDK_VERSION_FULL:
        //   - the value can be parsed as a float
        //   - the value contains a decimal separator
        //   - the value before the decimal separator is identical to RELEASE_PLATFORM_SDK_VERSION
        //     (e.g. 36.0 and 36)
        for release_config in RELEASE_CONFIGS.flags.keys() {
            let version_full =
                &RELEASE_CONFIGS.flags[release_config]["RELEASE_PLATFORM_SDK_VERSION_FULL"];
            if version_full.is_empty() {
                // skip this release config if it doesn't set RELEASE_PLATFORM_SDK_VERSION_FULL
                continue;
            }
            assert!(
                version_full.parse::<f32>().is_ok(),
                "failed to convert value ({}) of RELEASE_PLATFORM_SDK_VERSION_FULL for {} to f32",
                version_full,
                release_config
            );
            let (integer_part, _) = version_full.split_once(".").unwrap_or_else(|| panic!("value of RELEASE_PLATFORM_SDK_VERSION_FULL ({}) for {} doesn't have expected format", version_full, release_config));
            assert_eq!(
                integer_part,
                RELEASE_CONFIGS.flags[release_config]["RELEASE_PLATFORM_SDK_VERSION"]
            );
        }
    }
}