blob: 0a63bb8d6069114d785eafae6144bed60e55c225 [file] [log] [blame]
// Copyright 2020 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.
use {
anyhow::{Context as _, Error},
futures::{channel::mpsc, prelude::*},
pretty_assertions::assert_eq,
regex::Regex,
std::collections::HashMap,
test_executor::GroupByTestCase,
test_executor::{DisabledTestHandling, TestEvent, TestResult},
};
async fn run_test(
test_url: &str,
disabled_tests: DisabledTestHandling,
parallel: Option<u16>,
test_args: Vec<String>,
) -> Result<Vec<TestEvent>, Error> {
let time_taken = Regex::new(r" \(.*?\)$").unwrap();
let harness = test_runners_test_lib::connect_to_test_manager().await?;
let suite_instance = test_executor::SuiteInstance::new(test_executor::SuiteInstanceOpts {
harness: &harness,
test_url,
force_log_protocol: None,
})
.await?;
let (sender, recv) = mpsc::channel(1);
let run_options =
test_executor::TestRunOptions { disabled_tests, parallel, arguments: test_args };
let (events, ()) = futures::future::try_join(
recv.collect::<Vec<_>>().map(Ok),
suite_instance.run_and_collect_results(sender, None, run_options),
)
.await
.context("running test")?;
let mut test_events = test_runners_test_lib::process_events(events, false);
for event in test_events.iter_mut() {
match event {
TestEvent::StdoutMessage { test_case_name, msg } => {
let log = time_taken.replace(&msg, "");
*event = TestEvent::stdout_message(test_case_name, &log);
}
_ => {}
}
}
Ok(test_events)
}
#[fuchsia_async::run_singlethreaded(test)]
async fn launch_and_test_echo_test() {
let test_url = "fuchsia-pkg://fuchsia.com/go-test-runner-example#meta/echo-test-realm.cm";
let events = run_test(test_url, DisabledTestHandling::Exclude, Some(10), vec![]).await.unwrap();
let expected_events = vec![
TestEvent::test_case_started("TestEcho"),
TestEvent::test_case_finished("TestEcho", TestResult::Passed),
TestEvent::test_finished(),
];
assert_eq!(expected_events, events);
}
#[fuchsia_async::run_singlethreaded(test)]
async fn launch_and_test_file_with_no_test() {
let test_url = "fuchsia-pkg://fuchsia.com/go-test-runner-example#meta/empty_go_test.cm";
let events = run_test(test_url, DisabledTestHandling::Exclude, Some(10), vec![]).await.unwrap();
let expected_events = vec![TestEvent::test_finished()];
assert_eq!(expected_events, events);
}
async fn launch_and_run_sample_test_helper(parallel: Option<u16>) {
let test_url = "fuchsia-pkg://fuchsia.com/go-test-runner-example#meta/sample_go_test.cm";
let mut events = run_test(
test_url,
DisabledTestHandling::Exclude,
parallel,
vec!["-my_custom_flag_2".to_owned()],
)
.await
.unwrap();
assert_eq!(events.pop(), Some(TestEvent::test_finished()));
#[derive(Debug)]
enum TestEventMatch {
Started,
Finished(TestResult),
StdoutMatch(&'static str),
AnyStdout,
}
let mut expectations = vec![
(
"TestFailing",
vec![
TestEventMatch::Started,
TestEventMatch::StdoutMatch(" sample_go_test.go:25: This will fail"),
TestEventMatch::Finished(TestResult::Failed),
],
),
(
"TestPassing",
vec![
TestEventMatch::Started,
TestEventMatch::StdoutMatch("This test will pass"),
TestEventMatch::StdoutMatch("It will also print this line"),
TestEventMatch::StdoutMatch("And this line"),
TestEventMatch::Finished(TestResult::Passed),
],
),
(
"TestPrefix",
vec![
TestEventMatch::Started,
TestEventMatch::StdoutMatch("Testing that given two tests where one test is prefix of another can execute independently."),
TestEventMatch::Finished(TestResult::Passed),
],
),
(
"TestSkipped",
vec![
TestEventMatch::Started,
TestEventMatch::StdoutMatch(" sample_go_test.go:33: Skipping this test"),
TestEventMatch::Finished(TestResult::Skipped),
],
),
(
"TestSubtests",
vec![
TestEventMatch::Started,
TestEventMatch::StdoutMatch("=== RUN TestSubtests/Subtest1"),
TestEventMatch::StdoutMatch("=== RUN TestSubtests/Subtest2"),
TestEventMatch::StdoutMatch("=== RUN TestSubtests/Subtest3"),
TestEventMatch::StdoutMatch(" --- PASS: TestSubtests/Subtest1"),
TestEventMatch::StdoutMatch(" --- PASS: TestSubtests/Subtest2"),
TestEventMatch::StdoutMatch(" --- PASS: TestSubtests/Subtest3"),
TestEventMatch::Finished(TestResult::Passed),
],
),
(
"TestPrefixExtra",
vec![
TestEventMatch::Started,
TestEventMatch::StdoutMatch("Testing that given two tests where one test is prefix of another can execute independently."),
TestEventMatch::Finished(TestResult::Passed),
],
),
(
"TestPrintMultiline",
vec![
TestEventMatch::Started,
TestEventMatch::StdoutMatch("This test will print the msg in multi-line."),
TestEventMatch::Finished(TestResult::Passed),
],
),
(
"TestCustomArg",
vec![
TestEventMatch::Started,
TestEventMatch::Finished(TestResult::Passed),
],
),
(
"TestCustomArg2",
vec![
TestEventMatch::Started,
TestEventMatch::Finished(TestResult::Passed),
],
),
(
"TestCrashing",
vec![
TestEventMatch::Started,
TestEventMatch::StdoutMatch("panic: This will crash"),
TestEventMatch::StdoutMatch(" [recovered]"),
TestEventMatch::StdoutMatch("\tpanic: This will crash"),
TestEventMatch::StdoutMatch(""),
TestEventMatch::StdoutMatch(""),
// This test will print a stack trace, we avoid matching on it.
TestEventMatch::AnyStdout,
TestEventMatch::AnyStdout,
TestEventMatch::AnyStdout,
TestEventMatch::AnyStdout,
TestEventMatch::AnyStdout,
TestEventMatch::AnyStdout,
TestEventMatch::AnyStdout,
TestEventMatch::AnyStdout,
TestEventMatch::AnyStdout,
TestEventMatch::AnyStdout,
TestEventMatch::AnyStdout,
TestEventMatch::AnyStdout,
TestEventMatch::AnyStdout,
TestEventMatch::StdoutMatch("Test exited abnormally"),
TestEventMatch::Finished(TestResult::Failed),
],
),
]
.into_iter()
.collect::<HashMap<_, _>>();
// Walk through all the events and match against expectations based on test case.
for event in events {
let test_case = event
.test_case_name()
.unwrap_or_else(|| panic!("unexpected event {:?} without a test case", event));
let expect = expectations
.get_mut(test_case.as_str())
.unwrap_or_else(|| {
panic!("test case {} from event {:?} not covered by expectations", test_case, event)
})
.drain(0..1)
.next()
.unwrap_or_else(|| panic!("unexpected event {:?}", event));
match expect {
TestEventMatch::Started => assert_eq!(event, TestEvent::test_case_started(test_case)),
TestEventMatch::Finished(result) => {
assert_eq!(event, TestEvent::test_case_finished(test_case, result))
}
TestEventMatch::StdoutMatch(msg) => {
assert_eq!(event, TestEvent::stdout_message(test_case, msg))
}
TestEventMatch::AnyStdout => {
matches::assert_matches!(event, TestEvent::StdoutMessage { .. })
}
}
}
// Check that we have no leftovers in expectations:
let leftovers = expectations
.into_iter()
.map(|(test, expect)| expect.into_iter().map(move |e| (test, e)))
.flatten()
.collect::<Vec<_>>();
assert!(leftovers.is_empty(), "leftover expectations: {:?}", leftovers);
}
#[fuchsia_async::run_singlethreaded(test)]
async fn launch_and_run_sample_test() {
launch_and_run_sample_test_helper(Some(10)).await;
}
#[fuchsia_async::run_singlethreaded(test)]
async fn launch_and_run_sample_test_no_concurrent() {
launch_and_run_sample_test_helper(None).await;
}
// This test will hang if test cases are not executed in parallel.
#[fuchsia_async::run_singlethreaded(test)]
async fn test_parallel_execution() {
let test_url = "fuchsia-pkg://fuchsia.com/go-test-runner-example#meta/concurrency-test.cm";
let events = run_test(test_url, DisabledTestHandling::Exclude, Some(5), vec![])
.await
.unwrap()
.into_iter()
.group_by_test_case_unordered();
let mut expected_events = vec![];
for i in 1..=5 {
let s = format!("Test{}", i);
expected_events.extend(vec![
TestEvent::test_case_started(&s),
TestEvent::test_case_finished(&s, TestResult::Passed),
])
}
expected_events.push(TestEvent::test_finished());
let expected_events = expected_events.into_iter().group_by_test_case_unordered();
assert_eq!(events, expected_events);
}