diff --git a/garnet/bin/component_index/BUILD.gn b/garnet/bin/component_index/BUILD.gn index 7247d01db20989f6fe39ce5ea4e60b79280d1066..65c6f9e4eb0b3f122f739cf2f6a8144303b64137 100644 --- a/garnet/bin/component_index/BUILD.gn +++ b/garnet/bin/component_index/BUILD.gn @@ -2,8 +2,11 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/fidl/fidl.gni") import("//build/package.gni") import("//build/rust/rustc_binary.gni") +import("//build/test/test_package.gni") +import("//build/testing/environments.gni") package("component_index") { testonly = true @@ -24,7 +27,19 @@ package("component_index") { rustc_binary("component_index_bin") { name = "component_index" + with_unit_tests = true edition = "2018" + + deps = [ + "//garnet/bin/component_index/fidl:index-rustc", + "//garnet/public/lib/fidl/rust/fidl", + "//garnet/public/rust/fuchsia-async", + "//garnet/public/rust/fuchsia-component", + "//garnet/public/rust/fuchsia-runtime", + "//garnet/public/rust/fuchsia-zircon", + "//third_party/rust_crates:failure", + "//third_party/rust_crates:futures-preview", + ] } fuchsia_component("component_index_component") { @@ -35,3 +50,17 @@ fuchsia_component("component_index_component") { binary = "component_index" } + +test_package("component_index_tests") { + deps = [ + ":component_index_bin", + ] + + tests = [ + { + name = "component_index_bin_test" + dest = "component_index_tests" + environments = basic_envs + }, + ] +} diff --git a/garnet/bin/component_index/meta/component_index_tests.cmx b/garnet/bin/component_index/meta/component_index_tests.cmx new file mode 100644 index 0000000000000000000000000000000000000000..d0111340c9a650bbf7e241bd466ca431581ba317 --- /dev/null +++ b/garnet/bin/component_index/meta/component_index_tests.cmx @@ -0,0 +1,5 @@ +{ + "program": { + "binary": "test/component_index_tests" + } +} diff --git a/garnet/bin/component_index/src/main.rs b/garnet/bin/component_index/src/main.rs index 8b95e50c7ef449b84f28475a78416a7bee9c12f4..c9c9fba4e6afb52c5ddb79439ce8dcc36e5bd7a3 100644 --- a/garnet/bin/component_index/src/main.rs +++ b/garnet/bin/component_index/src/main.rs @@ -2,6 +2,109 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -fn main() { - // TODO(CF-162): Implement component index. +#![feature(async_await, await_macro, futures_api)] + +use failure::{Error, ResultExt}; +use fidl_fuchsia_sys_index::{ComponentIndexRequest, ComponentIndexRequestStream}; +use fuchsia_async as fasync; +use fuchsia_component::server::ServiceFs; +use futures::prelude::*; +use std::fs; +use std::sync::Arc; + +async fn run_fuzzy_search_server( + mut stream: ComponentIndexRequestStream, + index: Arc<Vec<String>>, +) -> Result<(), Error> { + while let Some(ComponentIndexRequest::FuzzySearch { needle, responder }) = + await!(stream.try_next()).context("Error serving fuzzy search")? + { + let mut ret = vec![]; + + if check_needle(&needle) { + let mut iter = index.iter(); + while let Some(c) = iter.next() { + if c.contains(&needle) { + ret.push(c.as_str()); + } + } + } + + responder.send(&mut ret.into_iter()).context("error sending response")?; + } + Ok(()) +} + +/// Needle only accepts a-zA-Z0-9 _ - / +fn check_needle(needle: &str) -> bool { + return needle.chars().all(|c| c.is_alphanumeric() || c == '/' || c == '_' || c == '-'); +} + +enum IncomingServices { + ComponentIndex(ComponentIndexRequestStream), +} + +#[fasync::run_singlethreaded] +async fn main() -> Result<(), Error> { + let mut fs = ServiceFs::new_local(); + + let index_string = fs::read_to_string("/pkg/data/component_index.txt") + .expect("Error reading component_index.txt"); + let index_vec: Vec<String> = index_string.lines().map(|l| l.to_string()).collect(); + let index = Arc::new(index_vec); + + fs.dir("public").add_fidl_service(IncomingServices::ComponentIndex); + fs.take_and_serve_directory_handle()?; + + const MAX_CONCURRENT: usize = 10_000; + let fut = fs.for_each_concurrent(MAX_CONCURRENT, |IncomingServices::ComponentIndex(stream)| { + run_fuzzy_search_server(stream, index.clone()).unwrap_or_else(|e| println!("{:?}", e)) + }); + await!(fut); + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + macro_rules! test_check_needle { + ( + $( + $test_name:ident => { + needle = $fuzzy_needle:expr, + accept = $fuzzy_accept:expr, + } + )+ + ) => { + $( + mod $test_name { + use super::*; + #[test] + fn test_eq() { + assert_eq!(check_needle($fuzzy_needle), $fuzzy_accept); + } + } + )+ + } + } + + test_check_needle! { + test_parse_alphanumeric => { + needle = "F00bar", + accept = true, + } + test_parse_dashes => { + needle = "foo_bar-baz", + accept = true, + } + test_parse_forward_slash => { + needle = "foo/bar", + accept = true, + } + test_parse_false => { + needle = "foo bar", + accept = false, + } + } } diff --git a/garnet/packages/tests/BUILD.gn b/garnet/packages/tests/BUILD.gn index 75c2347c8fb1ef576d96c37a2c7b9c52ff35cf73..aa0d3951b6d92789375aa4108c8b0bd5ffb8e4ee 100644 --- a/garnet/packages/tests/BUILD.gn +++ b/garnet/packages/tests/BUILD.gn @@ -289,6 +289,7 @@ group("all") { "//garnet/packages/tests:cmx", "//garnet/packages/tests:cobalt_client", "//garnet/packages/tests:component_cpp", + "//garnet/packages/tests:component_index", "//garnet/packages/tests:components_binary_test", "//garnet/packages/tests:containers_cpp", "//garnet/packages/tests:cpuperf", @@ -1079,6 +1080,13 @@ group("run_test_component") { ] } +group("component_index") { + testonly = true + public_deps = [ + "//garnet/bin/component_index:component_index_tests", + ] +} + group("bluetooth") { testonly = true public_deps = [