diff --git a/libprocessgroup/profiles/Android.bp b/libprocessgroup/profiles/Android.bp index 9ed31b026..15d017290 100644 --- a/libprocessgroup/profiles/Android.bp +++ b/libprocessgroup/profiles/Android.bp @@ -28,3 +28,53 @@ prebuilt_etc { name: "task_profiles.json", src: "task_profiles.json", } + +cc_library_static { + name: "libprocessgroup_proto", + host_supported: true, + srcs: [ + "cgroups.proto", + "task_profiles.proto", + ], + proto: { + type: "full", + export_proto_headers: true, + }, + cflags: [ + "-Wall", + "-Werror", + "-Wno-unused-parameter", + ], +} + +cc_test_host { + name: "libprocessgroup_proto_test", + srcs: [ + "test.cpp", + ], + static_libs: [ + "libbase", + "libgmock", + "liblog", + "libjsoncpp", + "libjsonpbverify", + "libjsonpbparse", + "libprocessgroup_proto", + ], + shared_libs: [ + "libprotobuf-cpp-full", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wno-unused-parameter", + ], + data: [ + "cgroups.json", + "cgroups.recovery.json", + "task_profiles.json", + ], + test_suites: [ + "general-tests", + ], +} diff --git a/libprocessgroup/profiles/TEST_MAPPING b/libprocessgroup/profiles/TEST_MAPPING new file mode 100644 index 000000000..5ff41123e --- /dev/null +++ b/libprocessgroup/profiles/TEST_MAPPING @@ -0,0 +1,8 @@ +{ + "presubmit": [ + { + "name": "libprocessgroup_proto_test", + "host": true + } + ] +} diff --git a/libprocessgroup/profiles/cgroups.proto b/libprocessgroup/profiles/cgroups.proto new file mode 100644 index 000000000..f4070c550 --- /dev/null +++ b/libprocessgroup/profiles/cgroups.proto @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2019 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. + */ + +syntax = "proto3"; + +package android.profiles; + +// Next: 3 +message Cgroups { + repeated Cgroup cgroups = 1 [json_name = "Cgroups"]; + Cgroups2 cgroups2 = 2 [json_name = "Cgroups2"]; +} + +// Next: 6 +message Cgroup { + string controller = 1 [json_name = "Controller"]; + string path = 2 [json_name = "Path"]; + string mode = 3 [json_name = "Mode"]; + string uid = 4 [json_name = "UID"]; + string gid = 5 [json_name = "GID"]; +} + +// Next: 5 +message Cgroups2 { + string path = 1 [json_name = "Path"]; + string mode = 2 [json_name = "Mode"]; + string uid = 3 [json_name = "UID"]; + string gid = 4 [json_name = "GID"]; +} diff --git a/libprocessgroup/profiles/task_profiles.proto b/libprocessgroup/profiles/task_profiles.proto new file mode 100644 index 000000000..578f0d3c1 --- /dev/null +++ b/libprocessgroup/profiles/task_profiles.proto @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019 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. + */ + +syntax = "proto3"; + +package android.profiles; + +// Next: 3 +message TaskProfiles { + repeated Attribute attributes = 1 [json_name = "Attributes"]; + repeated Profile profiles = 2 [json_name = "Profiles"]; +} + +// Next: 4 +message Attribute { + string name = 1 [json_name = "Name"]; + string controller = 2 [json_name = "Controller"]; + string file = 3 [json_name = "File"]; +} + +// Next: 3 +message Profile { + string name = 1 [json_name = "Name"]; + repeated Action actions = 2 [json_name = "Actions"]; +} + +// Next: 3 +message Action { + string name = 1 [json_name = "Name"]; + map params = 2 [json_name = "Params"]; +} diff --git a/libprocessgroup/profiles/test.cpp b/libprocessgroup/profiles/test.cpp new file mode 100644 index 000000000..8ba14d603 --- /dev/null +++ b/libprocessgroup/profiles/test.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2019 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. + */ + +#include + +#include +#include +#include + +#include "cgroups.pb.h" +#include "task_profiles.pb.h" + +using namespace ::android::jsonpb; +using ::android::base::GetExecutableDirectory; +using ::testing::MatchesRegex; + +namespace android { +namespace profiles { + +template +JsonSchemaTestConfigFactory MakeTestParam(const std::string& path) { + return jsonpb::MakeTestParam(GetExecutableDirectory() + path); +} + +TEST(LibProcessgroupProto, EmptyMode) { + EXPECT_EQ(0, strtoul("", nullptr, 8)) + << "Empty mode string cannot be silently converted to 0; this should not happen"; +} + +class CgroupsTest : public JsonSchemaTest { + public: + void SetUp() override { + JsonSchemaTest::SetUp(); + cgroups_ = static_cast(message()); + } + Cgroups* cgroups_; +}; + +TEST_P(CgroupsTest, CgroupRequiredFields) { + for (int i = 0; i < cgroups_->cgroups_size(); ++i) { + auto&& cgroup = cgroups_->cgroups(i); + EXPECT_FALSE(cgroup.controller().empty()) + << "No controller name for cgroup #" << i << " in " << file_path_; + EXPECT_FALSE(cgroup.path().empty()) << "No path for cgroup #" << i << " in " << file_path_; + } +} + +TEST_P(CgroupsTest, Cgroup2RequiredFields) { + if (cgroups_->has_cgroups2()) { + EXPECT_FALSE(cgroups_->cgroups2().path().empty()) + << "No path for cgroup2 in " << file_path_; + } +} + +// "Mode" field must be in the format of "0xxx". +static constexpr const char* REGEX_MODE = "(0[0-7]{3})?"; +TEST_P(CgroupsTest, CgroupMode) { + for (int i = 0; i < cgroups_->cgroups_size(); ++i) { + EXPECT_THAT(cgroups_->cgroups(i).mode(), MatchesRegex(REGEX_MODE)) + << "For cgroup controller #" << i << " in " << file_path_; + } +} + +TEST_P(CgroupsTest, Cgroup2Mode) { + EXPECT_THAT(cgroups_->cgroups2().mode(), MatchesRegex(REGEX_MODE)) + << "For cgroups2 in " << file_path_; +} + +class TaskProfilesTest : public JsonSchemaTest { + public: + void SetUp() override { + JsonSchemaTest::SetUp(); + task_profiles_ = static_cast(message()); + } + TaskProfiles* task_profiles_; +}; + +TEST_P(TaskProfilesTest, AttributeRequiredFields) { + for (int i = 0; i < task_profiles_->attributes_size(); ++i) { + auto&& attribute = task_profiles_->attributes(i); + EXPECT_FALSE(attribute.name().empty()) + << "No name for attribute #" << i << " in " << file_path_; + EXPECT_FALSE(attribute.controller().empty()) + << "No controller for attribute #" << i << " in " << file_path_; + EXPECT_FALSE(attribute.file().empty()) + << "No file for attribute #" << i << " in " << file_path_; + } +} + +TEST_P(TaskProfilesTest, ProfileRequiredFields) { + for (int profile_idx = 0; profile_idx < task_profiles_->profiles_size(); ++profile_idx) { + auto&& profile = task_profiles_->profiles(profile_idx); + EXPECT_FALSE(profile.name().empty()) + << "No name for profile #" << profile_idx << " in " << file_path_; + for (int action_idx = 0; action_idx < profile.actions_size(); ++action_idx) { + auto&& action = profile.actions(action_idx); + EXPECT_FALSE(action.name().empty()) + << "No name for profiles[" << profile_idx << "].actions[" << action_idx + << "] in " << file_path_; + } + } +} + +// Test suite instantiations + +INSTANTIATE_TEST_SUITE_P(, JsonSchemaTest, + ::testing::Values(MakeTestParam("/cgroups.json"), + MakeTestParam("/cgroups.recovery.json"), + MakeTestParam("/task_profiles.json"))); +INSTANTIATE_TEST_SUITE_P(, CgroupsTest, + ::testing::Values(MakeTestParam("/cgroups.json"), + MakeTestParam("/cgroups.recovery.json"))); +INSTANTIATE_TEST_SUITE_P(, TaskProfilesTest, + ::testing::Values(MakeTestParam("/task_profiles.json"))); + +} // namespace profiles +} // namespace android + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}