| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "garnet/bin/run_test_component/test_metadata.h" |
| |
| #include <fuchsia/boot/cpp/fidl.h> |
| #include <fuchsia/camera2/cpp/fidl.h> |
| #include <fuchsia/device/cpp/fidl.h> |
| #include <fuchsia/diagnostics/cpp/fidl.h> |
| #include <fuchsia/hardware/pty/cpp/fidl.h> |
| #include <fuchsia/kernel/cpp/fidl.h> |
| #include <fuchsia/media/cpp/fidl.h> |
| #include <fuchsia/net/cpp/fidl.h> |
| #include <fuchsia/posix/socket/cpp/fidl.h> |
| #include <fuchsia/scheduler/cpp/fidl.h> |
| #include <fuchsia/sys/internal/cpp/fidl.h> |
| #include <fuchsia/sys/test/cpp/fidl.h> |
| #include <fuchsia/sysinfo/cpp/fidl.h> |
| #include <fuchsia/sysmem/cpp/fidl.h> |
| #include <fuchsia/time/cpp/fidl.h> |
| #include <fuchsia/tracing/kernel/cpp/fidl.h> |
| #include <fuchsia/tracing/provider/cpp/fidl.h> |
| #include <fuchsia/vulkan/loader/cpp/fidl.h> |
| |
| #include <filesystem> |
| #include <unordered_set> |
| |
| #include "src/lib/cmx/cmx.h" |
| #include "src/lib/fxl/strings/substitute.h" |
| |
| namespace run { |
| namespace { |
| |
| using fxl::Substitute; |
| |
| constexpr char kInjectedServices[] = "injected-services"; |
| constexpr char kSystemServices[] = "system-services"; |
| |
| // Services below were reported by their owners to be impractical to fake in a test environment |
| // because they depend on devices. Appmgr's test support does not offer the ability to fake the |
| // device namespace. |
| // |
| // Component Manager is able to route and fake devices and early boot capabilities. |
| // |
| // At this time the body of tests largely depends on appmgr, so we maintain this list as a necessary |
| // compromise. |
| // |
| // Please add items to this list only if you believe that no other pragmatic alternative is |
| // currently present. |
| // |
| // Please document the rationale for each entry added. See also: |
| // docs/concepts/testing/v1_test_component.md |
| const std::unordered_set<std::string> kAllowedSystemServices = { |
| fuchsia::boot::FactoryItems::Name_, |
| fuchsia::boot::Items::Name_, |
| fuchsia::boot::ReadOnlyLog::Name_, |
| fuchsia::boot::RootResource::Name_, |
| fuchsia::boot::WriteOnlyLog::Name_, |
| fuchsia::camera2::Manager::Name_, |
| fuchsia::device::NameProvider::Name_, |
| fuchsia::diagnostics::ArchiveAccessor::Name_, |
| fuchsia::hardware::pty::Device::Name_, |
| fuchsia::kernel::Counter::Name_, |
| fuchsia::kernel::DebugResource::Name_, |
| fuchsia::kernel::HypervisorResource::Name_, |
| fuchsia::kernel::InfoResource::Name_, |
| fuchsia::kernel::IoportResource::Name_, |
| fuchsia::kernel::IrqResource::Name_, |
| fuchsia::kernel::MmioResource::Name_, |
| fuchsia::kernel::RootJob::Name_, |
| fuchsia::kernel::RootJobForInspect::Name_, |
| fuchsia::kernel::SmcResource::Name_, |
| fuchsia::kernel::Stats::Name_, |
| fuchsia::kernel::VmexResource::Name_, |
| fuchsia::media::Audio::Name_, |
| fuchsia::media::AudioCore::Name_, |
| fuchsia::media::ProfileProvider::Name_, |
| fuchsia::scheduler::ProfileProvider::Name_, |
| fuchsia::sys::internal::CrashIntrospect::Name_, |
| fuchsia::sys::test::CacheControl::Name_, |
| fuchsia::sysinfo::SysInfo::Name_, |
| fuchsia::sysmem::Allocator::Name_, |
| fuchsia::time::Utc::Name_, |
| fuchsia::tracing::provider::Registry::Name_, |
| fuchsia::tracing::kernel::Controller::Name_, |
| fuchsia::tracing::kernel::Reader::Name_, |
| fuchsia::ui::policy::Presenter::Name_, |
| fuchsia::ui::scenic::Scenic::Name_, |
| fuchsia::vulkan::loader::Loader::Name_, |
| }; |
| |
| // These tests do not run in continuous integration because they make real network requests. Do not |
| // add to this list under any circumstances. If your tests require real network access, consider |
| // writing them as end-to-end tests. See docs/development/testing/create_a_new_end_to_end_test.md. |
| // |
| // TODO(fxbug.dev/57076): migrate these tests and remove this list. |
| const std::unordered_set<std::string> kNetworkUsingTestsThatShouldBeE2E = { |
| "aml_widevine_test.cmx", |
| "cdm_app_test", |
| "cobalt_testapp_for_prober_do_not_run_manually.cmx", |
| "playready_cdm_test.cmx", |
| }; |
| |
| const std::unordered_set<std::string> kRealNetworkServices = { |
| fuchsia::net::NameLookup::Name_, |
| fuchsia::posix::socket::Provider::Name_, |
| }; |
| |
| } // namespace |
| |
| TestMetadata::TestMetadata() {} |
| TestMetadata::~TestMetadata() {} |
| |
| fuchsia::sys::LaunchInfo TestMetadata::GetLaunchInfo(const rapidjson::Document::ValueType& value, |
| const std::string& name) { |
| fuchsia::sys::LaunchInfo launch_info; |
| if (value.IsString()) { |
| launch_info.url = value.GetString(); |
| return launch_info; |
| } |
| |
| if (value.IsArray()) { |
| const auto& array = value.GetArray(); |
| // If the element is an array, ensure it is non-empty and all values are |
| // strings. |
| if (!array.Empty() && std::all_of(array.begin(), array.end(), |
| [](const rapidjson::Value& val) { return val.IsString(); })) { |
| launch_info.url = array[0].GetString(); |
| launch_info.arguments.emplace(); |
| for (size_t i = 1; i < array.Size(); ++i) { |
| launch_info.arguments->push_back(array[i].GetString()); |
| } |
| return launch_info; |
| } |
| } |
| |
| json_parser_.ReportError( |
| Substitute("'$0' must be a string or a non-empty array of strings.", name)); |
| return launch_info; |
| } |
| |
| bool TestMetadata::ParseFromString(const std::string& cmx_data, const std::string& filename) { |
| component::CmxMetadata cmx; |
| cmx.ParseFromString(cmx_data, filename, &json_parser_); |
| if (json_parser_.HasError()) { |
| return false; |
| } |
| auto& fuchsia_test = cmx.GetFacet(kFuchsiaTest); |
| if (!fuchsia_test.IsNull()) { |
| null_ = false; |
| if (!fuchsia_test.IsObject()) { |
| json_parser_.ReportError(Substitute("'$0' in 'facets' should be an object.", kFuchsiaTest)); |
| return false; |
| } |
| auto system_services = fuchsia_test.FindMember(kSystemServices); |
| if (system_services != fuchsia_test.MemberEnd()) { |
| if (!system_services->value.IsArray()) { |
| json_parser_.ReportError( |
| Substitute("'$0' in '$1' should be a string array.", kSystemServices, kFuchsiaTest)); |
| } else { |
| for (const rapidjson::Value& val : system_services->value.GetArray()) { |
| if (!val.IsString()) { |
| json_parser_.ReportError(Substitute("'$0' in '$1' should be a string array.", |
| kSystemServices, kFuchsiaTest)); |
| return false; |
| } |
| std::string service = val.GetString(); |
| if (kAllowedSystemServices.count(service) == 0) { |
| if ((kRealNetworkServices.count(service) == 0 || |
| kNetworkUsingTestsThatShouldBeE2E.count( |
| std::filesystem::path(filename).filename()) == 0)) { |
| json_parser_.ReportError( |
| fxl::Substitute("'$0' cannot contain '$1'.", kSystemServices, service)); |
| return false; |
| } |
| } |
| system_services_.push_back(service); |
| }; |
| } |
| } |
| auto services = fuchsia_test.FindMember(kInjectedServices); |
| if (services != fuchsia_test.MemberEnd()) { |
| if (!services->value.IsObject()) { |
| json_parser_.ReportError( |
| Substitute("'$0' in '$1' should be an object.", kInjectedServices, kFuchsiaTest)); |
| return false; |
| } |
| for (auto itr = services->value.MemberBegin(); itr != services->value.MemberEnd(); ++itr) { |
| if (!itr->name.IsString()) { |
| json_parser_.ReportError(Substitute("'$0' in '$1' should define string service names.", |
| kInjectedServices, kFuchsiaTest)); |
| return false; |
| } |
| auto name = itr->name.GetString(); |
| auto launch_info = GetLaunchInfo(itr->value, name); |
| service_url_pair_.push_back(std::make_pair(name, std::move(launch_info))); |
| } |
| } |
| } |
| return !HasError(); |
| } |
| |
| } // namespace run |