| // Copyright 2016 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 <memory> |
| #include <set> |
| #include <utility> |
| |
| #include <fuchsia/modular/cpp/fidl.h> |
| #include <fuchsia/sys/cpp/fidl.h> |
| #include <fuchsia/ui/viewsv1token/cpp/fidl.h> |
| #include <lib/async/cpp/task.h> |
| #include <lib/async/default.h> |
| #include <lib/component/cpp/startup_context.h> |
| #include <lib/fidl/cpp/binding.h> |
| #include <lib/fidl/cpp/binding_set.h> |
| #include <lib/fsl/vmo/sized_vmo.h> |
| #include <lib/fsl/vmo/strings.h> |
| #include <lib/fxl/command_line.h> |
| #include <lib/fxl/logging.h> |
| #include <lib/fxl/macros.h> |
| #include <lib/fxl/time/time_delta.h> |
| |
| #include "peridot/lib/rapidjson/rapidjson.h" |
| #include "peridot/lib/testing/component_main.h" |
| #include "peridot/lib/testing/session_shell_base.h" |
| #include "peridot/public/lib/integration_testing/cpp/reporting.h" |
| #include "peridot/public/lib/integration_testing/cpp/testing.h" |
| #include "peridot/tests/common/defs.h" |
| #include "peridot/tests/session_shell/defs.h" |
| |
| using modular::testing::Await; |
| using modular::testing::Fail; |
| using modular::testing::Signal; |
| using modular::testing::TestPoint; |
| |
| using fuchsia::modular::ViewIdentifier; |
| |
| namespace { |
| |
| // A simple story provider watcher implementation. Just logs observed state |
| // transitions. |
| class StoryProviderStateWatcherImpl : fuchsia::modular::StoryProviderWatcher { |
| public: |
| StoryProviderStateWatcherImpl() : binding_(this) {} |
| ~StoryProviderStateWatcherImpl() override = default; |
| |
| // Registers itself a watcher on the given story provider. Only one story |
| // provider can be watched at a time. |
| void Watch(fuchsia::modular::StoryProvider* const story_provider) { |
| story_provider->Watch(binding_.NewBinding()); |
| } |
| |
| // Deregisters itself from the watched story provider. |
| void Reset() { binding_.Unbind(); } |
| |
| void SetKindOfProtoStory(fidl::StringPtr story_id) { |
| kind_of_proto_stories_.insert(story_id); |
| } |
| |
| private: |
| TestPoint on_delete_called_once_{"OnDelete() Called"}; |
| int on_delete_called_{}; |
| |
| // |fuchsia::modular::StoryProviderWatcher| |
| void OnDelete(std::string story_id) override { |
| FXL_LOG(INFO) << "StoryProviderStateWatcherImpl::OnDelete() " << story_id; |
| |
| if (++on_delete_called_ == 1) { |
| on_delete_called_once_.Pass(); |
| } |
| |
| deleted_stories_.emplace(story_id); |
| } |
| |
| TestPoint on_running_called_once_{"OnChange() RUNNING Called"}; |
| int on_running_called_{}; |
| |
| TestPoint on_stopping_called_once_{"OnChange() STOPPING Called"}; |
| int on_stopping_called_{}; |
| |
| TestPoint on_stopped_called_once_{"OnChange() STOPPED Called"}; |
| int on_stopped_called_{}; |
| |
| // |fuchsia::modular::StoryProviderWatcher| |
| void OnChange(const fuchsia::modular::StoryInfo story_info, |
| const fuchsia::modular::StoryState story_state, |
| const fuchsia::modular::StoryVisibilityState |
| story_visibility_state) override { |
| FXL_LOG(INFO) << "StoryProviderStateWatcherImpl::OnChange() " |
| << " id " << story_info.id << " state " |
| << fidl::ToUnderlying(story_state) << " visibility state " |
| << fidl::ToUnderlying(story_visibility_state) << " url " |
| << story_info.url; |
| |
| if (deleted_stories_.find(story_info.id) != deleted_stories_.end()) { |
| FXL_LOG(ERROR) << "Status change notification for deleted story " |
| << story_info.id; |
| modular::testing::Fail("Status change notification for deleted story"); |
| } |
| |
| if (kind_of_proto_stories_.find(story_info.id) != |
| kind_of_proto_stories_.end()) { |
| modular::testing::Fail( |
| "Stories with kind_of_proto_story option set shouldn't notify " |
| "OnChange"); |
| } |
| |
| // Just check that all states are covered at least once, proving that we get |
| // state notifications at all from the story provider. |
| switch (story_state) { |
| case fuchsia::modular::StoryState::RUNNING: |
| if (++on_running_called_ == 1) { |
| on_running_called_once_.Pass(); |
| } |
| break; |
| case fuchsia::modular::StoryState::STOPPING: |
| if (++on_stopping_called_ == 1) { |
| on_stopping_called_once_.Pass(); |
| } |
| break; |
| case fuchsia::modular::StoryState::STOPPED: |
| if (++on_stopped_called_ == 1) { |
| on_stopped_called_once_.Pass(); |
| } |
| break; |
| } |
| } |
| |
| fidl::Binding<fuchsia::modular::StoryProviderWatcher> binding_; |
| |
| // Remember deleted stories. After a story is deleted, there must be no state |
| // change notifications for it. |
| std::set<std::string> deleted_stories_; |
| |
| std::set<std::string> kind_of_proto_stories_; |
| |
| FXL_DISALLOW_COPY_AND_ASSIGN(StoryProviderStateWatcherImpl); |
| }; |
| |
| // Cf. README.md for what this test does in general and how. The test cases are |
| // described in detail in comments below. |
| class TestApp : public modular::testing::SessionShellBase { |
| public: |
| using ViewId = fuchsia::modular::ViewIdentifier; |
| |
| explicit TestApp(component::StartupContext* const startup_context) |
| : SessionShellBase(startup_context) { |
| TestInit(__FILE__); |
| |
| startup_context->ConnectToEnvironmentService(puppet_master_.NewRequest()); |
| startup_context->ConnectToEnvironmentService( |
| component_context_.NewRequest()); |
| story_provider_state_watcher_.Watch(story_provider()); |
| |
| TestComponentContext_GetPackageName_Works(); |
| } |
| |
| ~TestApp() override = default; |
| |
| private: |
| TestPoint create_view_{"CreateView()"}; |
| |
| // |SingleServiceApp| |
| void CreateView( |
| zx::eventpair /*view_token*/, |
| fidl::InterfaceRequest< |
| fuchsia::sys::ServiceProvider> /*incoming_services*/, |
| fidl::InterfaceHandle< |
| fuchsia::sys::ServiceProvider> /*outgoing_services*/) override { |
| create_view_.Pass(); |
| } |
| |
| // Test Case GetPackageName: |
| // |
| // When we call GetPackageName() on ComponentContext acquired from our |
| // environment, it works. |
| |
| TestPoint component_context_package_name_{ |
| "ComponentContext.GetPackageName() works"}; |
| |
| void TestComponentContext_GetPackageName_Works() { |
| component_context_->GetPackageName([this](fidl::StringPtr name) { |
| if (name) { |
| component_context_package_name_.Pass(); |
| } |
| |
| TestStoryProvider_GetStoryInfo_Null(); |
| }); |
| } |
| |
| // Test Case GetStoryInfo Null: |
| // |
| // The story info of a story that does not exist is null. |
| |
| TestPoint get_story_info_null_{"StoryProvider.GetStoryInfo() is null"}; |
| |
| void TestStoryProvider_GetStoryInfo_Null() { |
| story_provider()->GetStoryInfo( |
| "X", [this](fuchsia::modular::StoryInfoPtr story_info) { |
| if (!story_info) { |
| get_story_info_null_.Pass(); |
| } |
| |
| TestSessionShellContext_GetLink(); |
| }); |
| } |
| |
| // Test Case SessionShellContext: |
| // |
| // The session shell can access a Link. |
| |
| TestPoint get_link_{"SessionShellContext.GetLink()"}; |
| |
| void TestSessionShellContext_GetLink() { |
| session_shell_context()->GetLink(session_shell_link_.NewRequest()); |
| session_shell_link_->Get( |
| nullptr, [this](std::unique_ptr<fuchsia::mem::Buffer> value) { |
| get_link_.Pass(); |
| TestStoryProvider_GetStories(); |
| }); |
| } |
| |
| // Test Case StoryProvider: |
| // |
| // The session shell can access the list of existing stories. This list is |
| // empty at the outset. |
| |
| TestPoint previous_stories_{"StoryProvider.GetStories()"}; |
| |
| void TestStoryProvider_GetStories() { |
| story_provider()->GetStories( |
| nullptr, [this](std::vector<fuchsia::modular::StoryInfo> stories) { |
| previous_stories_.Pass(); |
| TestStoryProvider_GetStoryInfo(std::move(stories)); |
| }); |
| } |
| |
| TestPoint get_story_info_{"StoryProvider.GetStoryInfo()"}; |
| |
| void TestStoryProvider_GetStoryInfo( |
| std::vector<fuchsia::modular::StoryInfo> stories) { |
| if (stories.empty()) { |
| get_story_info_.Pass(); |
| } else { |
| FXL_LOG(ERROR) << "StoryProvider.GetStoryInfo() " << stories.size(); |
| for (const auto& item : stories) { |
| FXL_LOG(INFO) << item.id; |
| } |
| } |
| |
| TestStory1(); |
| } |
| |
| // Test Case Story1: |
| // |
| // Create a story with extra information, start, and stop it. |
| |
| TestPoint story1_create_{"Story1 Create"}; |
| |
| void TestStory1() { |
| const std::string initial_json = R"({"created-with-info": true})"; |
| puppet_master_->ControlStory("story1", story_puppet_master_.NewRequest()); |
| |
| fuchsia::modular::AddMod add_mod; |
| add_mod.mod_name.push_back("mod1"); |
| add_mod.intent.handler = kCommonActiveModule; |
| |
| fuchsia::modular::IntentParameter param; |
| param.name = "root"; |
| fsl::SizedVmo vmo; |
| FXL_CHECK(fsl::VmoFromString(initial_json, &vmo)); |
| param.data.set_json(std::move(vmo).ToTransport()); |
| add_mod.intent.parameters.push_back(std::move(param)); |
| |
| fuchsia::modular::StoryCommand command; |
| command.set_add_mod(std::move(add_mod)); |
| |
| std::vector<fuchsia::modular::StoryCommand> commands; |
| commands.push_back(std::move(command)); |
| |
| story_puppet_master_->Enqueue(std::move(commands)); |
| story_puppet_master_->Execute( |
| [this](fuchsia::modular::ExecuteResult result) { |
| story1_create_.Pass(); |
| TestStory1_GetController("story1"); |
| }); |
| } |
| |
| TestPoint story1_get_controller_{"Story1 GetController"}; |
| |
| void TestStory1_GetController(fidl::StringPtr story_id) { |
| story_provider()->GetController(story_id, story_controller_.NewRequest()); |
| story_controller_->GetInfo([this](fuchsia::modular::StoryInfo story_info, |
| fuchsia::modular::StoryState state) { |
| story1_get_controller_.Pass(); |
| story_info_ = std::move(story_info); |
| TestStory1_Run(); |
| }); |
| } |
| |
| TestPoint story1_run_{"Story1 Run"}; |
| void TestStory1_Run() { |
| // Start and show the new story. |
| story_controller_->RequestStart(); |
| story1_run_.Pass(); |
| TestStory1_Stop(); |
| } |
| |
| TestPoint story1_stop_{"Story1 Stop"}; |
| |
| void TestStory1_Stop() { |
| story_controller_->Stop([this] { |
| TeardownStoryController(); |
| story1_stop_.Pass(); |
| |
| // When the story is done, we start the next one. |
| TestStory2(); |
| }); |
| } |
| |
| // Test Case Story2: |
| // |
| // Verify that when pipelining Start() and GetInfo() calls, GetInfo() yields |
| // the run state after Start(). |
| // |
| // Verify that after DeleteStory(), GetInfo() returns null again. |
| |
| TestPoint story2_create_{"Story2 Create"}; |
| |
| void TestStory2() { |
| puppet_master_->ControlStory("story2", story_puppet_master_.NewRequest()); |
| |
| fuchsia::modular::AddMod add_mod; |
| add_mod.mod_name.push_back("mod1"); |
| add_mod.intent.handler = kCommonNullModule; |
| |
| fuchsia::modular::StoryCommand command; |
| command.set_add_mod(std::move(add_mod)); |
| |
| std::vector<fuchsia::modular::StoryCommand> commands; |
| commands.push_back(std::move(command)); |
| |
| story_puppet_master_->Enqueue(std::move(commands)); |
| story_puppet_master_->Execute( |
| [this](fuchsia::modular::ExecuteResult result) { |
| story2_create_.Pass(); |
| TestStory2_GetController("story2"); |
| }); |
| } |
| |
| TestPoint story2_get_controller_{"Story2 Get Controller"}; |
| |
| void TestStory2_GetController(fidl::StringPtr story_id) { |
| story_provider()->GetController(story_id, story_controller_.NewRequest()); |
| story_controller_->GetInfo([this](fuchsia::modular::StoryInfo story_info, |
| fuchsia::modular::StoryState state) { |
| story_info_ = std::move(story_info); |
| story2_get_controller_.Pass(); |
| TestStory2_GetModules(); |
| }); |
| } |
| |
| TestPoint story2_get_modules_{"Story2 Get Modules"}; |
| |
| void TestStory2_GetModules() { |
| story_controller_->GetModules( |
| [this](std::vector<fuchsia::modular::ModuleData> modules) { |
| if (modules.size() == 1) { |
| story2_get_modules_.Pass(); |
| } |
| |
| TestStory2_Run(); |
| }); |
| } |
| |
| TestPoint story2_state_before_run_{"Story2 State before Run"}; |
| TestPoint story2_state_after_run_{"Story2 State after Run"}; |
| |
| void TestStory2_Run() { |
| story_controller_->GetInfo([this](fuchsia::modular::StoryInfo info, |
| fuchsia::modular::StoryState state) { |
| if (state == fuchsia::modular::StoryState::STOPPED) { |
| story2_state_before_run_.Pass(); |
| } |
| }); |
| |
| // Start and show the new story *while* the GetInfo() call above is in |
| // flight. |
| story_controller_->RequestStart(); |
| |
| story_controller_->GetInfo([this](fuchsia::modular::StoryInfo info, |
| fuchsia::modular::StoryState state) { |
| if (state == fuchsia::modular::StoryState::RUNNING) { |
| story2_state_after_run_.Pass(); |
| } |
| |
| TestStory2_DeleteStory(); |
| }); |
| } |
| |
| TestPoint story2_delete_{"Story2 Delete"}; |
| |
| void TestStory2_DeleteStory() { |
| puppet_master_->DeleteStory(story_info_.id, |
| [this] { story2_delete_.Pass(); }); |
| |
| story_provider()->GetStoryInfo( |
| story_info_.id, [this](fuchsia::modular::StoryInfoPtr info) { |
| TestStory2_InfoAfterDeleteIsNull(std::move(info)); |
| }); |
| } |
| |
| TestPoint story2_info_after_delete_{"Story2 Info After Delete"}; |
| |
| void TestStory2_InfoAfterDeleteIsNull(fuchsia::modular::StoryInfoPtr info) { |
| story2_info_after_delete_.Pass(); |
| if (info) { |
| modular::testing::Fail("StoryInfo after DeleteStory() must return null."); |
| } |
| |
| TestStory3(); |
| } |
| |
| // Test Case Story3: |
| // |
| // Verify that a "kind of proto" story doesn't appear in the list of stories |
| // of the story provider. |
| |
| TestPoint story3_create_{"Story3 Create"}; |
| |
| void TestStory3() { |
| story_provider_state_watcher_.Reset(); |
| story_provider_state_watcher_.Watch(story_provider()); |
| |
| puppet_master_->ControlStory("story3", story_puppet_master_.NewRequest()); |
| |
| fuchsia::modular::StoryOptions story_options; |
| story_options.kind_of_proto_story = true; |
| story_puppet_master_->SetCreateOptions(std::move(story_options)); |
| |
| story_puppet_master_->Execute( |
| [this](fuchsia::modular::ExecuteResult result) { |
| story_provider_state_watcher_.SetKindOfProtoStory("story3"); |
| story3_create_.Pass(); |
| TestStory3_GetController("story3"); |
| }); |
| } |
| |
| TestPoint story3_get_controller_{"Story3 GetController"}; |
| |
| void TestStory3_GetController(fidl::StringPtr story_id) { |
| story_provider()->GetController(story_id, story_controller_.NewRequest()); |
| story_controller_->GetInfo([this](fuchsia::modular::StoryInfo story_info, |
| fuchsia::modular::StoryState state) { |
| story_info_ = std::move(story_info); |
| story3_get_controller_.Pass(); |
| TestStory3_GetStories(); |
| }); |
| } |
| |
| TestPoint story3_previous_stories_{"Story3 GetGetStories"}; |
| |
| void TestStory3_GetStories() { |
| story_provider()->GetStories( |
| nullptr, [this](std::vector<fuchsia::modular::StoryInfo> stories) { |
| // Since this is a kind-of-proto story, it shouldn't appear in |
| // GetStories calls. Note that we still expect 1 story to be here |
| // since Story1 wasn't deleted. |
| if (stories.size() == 1 && stories.at(0).id != story_info_.id) { |
| story3_previous_stories_.Pass(); |
| } else { |
| FXL_LOG(ERROR) << "StoryProvider.GetStories() " << stories.size(); |
| for (const auto& item : stories) { |
| FXL_LOG(INFO) << item.id; |
| } |
| } |
| TestStory3_Run(); |
| }); |
| } |
| |
| TestPoint story3_run_{"Story3 Run"}; |
| |
| void TestStory3_Run() { |
| story_controller_->RequestStart(); |
| |
| story_controller_->GetInfo([this](fuchsia::modular::StoryInfo info, |
| fuchsia::modular::StoryState state) { |
| if (state == fuchsia::modular::StoryState::RUNNING) { |
| story3_run_.Pass(); |
| } |
| |
| TestStory3_Stop(); |
| }); |
| } |
| |
| TestPoint story3_stop_{"Story3 Stop"}; |
| |
| void TestStory3_Stop() { |
| story_controller_->Stop([this] { |
| TeardownStoryController(); |
| story3_stop_.Pass(); |
| TestStory3_DeleteStory(); |
| }); |
| } |
| |
| TestPoint story3_delete_{"Story3 Delete"}; |
| |
| void TestStory3_DeleteStory() { |
| puppet_master_->DeleteStory(story_info_.id, |
| [this] { story3_delete_.Pass(); }); |
| |
| story_provider()->GetStoryInfo( |
| story_info_.id, [this](fuchsia::modular::StoryInfoPtr info) { |
| TestStory3_InfoAfterDeleteIsNull(std::move(info)); |
| }); |
| } |
| |
| TestPoint story3_info_after_delete_{"Story3 InfoAfterDeleteIsNull"}; |
| |
| void TestStory3_InfoAfterDeleteIsNull(fuchsia::modular::StoryInfoPtr info) { |
| if (!info) { |
| story3_info_after_delete_.Pass(); |
| } |
| |
| TestStory4(); |
| } |
| |
| // Test Case Story4: |
| // |
| // Create a story and start it with RequestStart() rather than Start(). |
| // |
| // Verify the view is received through SessionShell.AttachView(). |
| // |
| // Verify that, when the story is stopped, a request for |
| // SessionShell.DetachView() is received. |
| |
| TestPoint story4_create_{"Story4 Create"}; |
| |
| void TestStory4() { |
| puppet_master_->ControlStory("story4", story_puppet_master_.NewRequest()); |
| |
| fuchsia::modular::AddMod add_mod; |
| add_mod.mod_name.push_back("mod1"); |
| add_mod.intent.handler = kCommonNullModule; |
| |
| fuchsia::modular::StoryCommand command; |
| command.set_add_mod(std::move(add_mod)); |
| |
| std::vector<fuchsia::modular::StoryCommand> commands; |
| commands.push_back(std::move(command)); |
| |
| story_puppet_master_->Enqueue(std::move(commands)); |
| story_puppet_master_->Execute( |
| [this](fuchsia::modular::ExecuteResult result) { |
| story4_create_.Pass(); |
| TestStory4_Run(); |
| }); |
| } |
| |
| TestPoint story4_state_before_run_{"Story4 State before Run"}; |
| TestPoint story4_state_after_run_{"Story4 State after Run"}; |
| TestPoint story4_attach_view_{"Story4 attach View"}; |
| |
| void TestStory4_Run() { |
| story_provider()->GetController("story4", story_controller_.NewRequest()); |
| story_controller_->GetInfo([this](fuchsia::modular::StoryInfo info, |
| fuchsia::modular::StoryState state) { |
| story_info_ = std::move(info); |
| if (state == fuchsia::modular::StoryState::STOPPED) { |
| story4_state_before_run_.Pass(); |
| } |
| }); |
| |
| // Start and show the new story using RequestStart(). |
| story_controller_->RequestStart(); |
| |
| session_shell_impl()->set_on_attach_view( |
| [this](ViewId) { story4_attach_view_.Pass(); }); |
| |
| story_controller_->GetInfo([this](fuchsia::modular::StoryInfo info, |
| fuchsia::modular::StoryState state) { |
| if (state == fuchsia::modular::StoryState::RUNNING) { |
| story4_state_after_run_.Pass(); |
| TestStory4_Stop(); |
| } |
| }); |
| } |
| |
| TestPoint story4_detach_view_{"Story4 detach View"}; |
| TestPoint story4_stop_{"Story4 Stop"}; |
| |
| void TestStory4_Stop() { |
| session_shell_impl()->set_on_detach_view( |
| [this](ViewId) { story4_detach_view_.Pass(); }); |
| |
| story_controller_->Stop([this] { |
| TeardownStoryController(); |
| story4_stop_.Pass(); |
| |
| TestStory4_DeleteStory(); |
| }); |
| } |
| |
| TestPoint story4_delete_{"Story4 Delete"}; |
| |
| void TestStory4_DeleteStory() { |
| puppet_master_->DeleteStory(story_info_.id, |
| [this] { story4_delete_.Pass(); }); |
| |
| story_provider()->GetStoryInfo( |
| story_info_.id, [this](fuchsia::modular::StoryInfoPtr info) { |
| TestStory4_InfoAfterDeleteIsNull(std::move(info)); |
| }); |
| } |
| |
| TestPoint story4_info_after_delete_{"Story4 Info after Delete is null"}; |
| |
| void TestStory4_InfoAfterDeleteIsNull(fuchsia::modular::StoryInfoPtr info) { |
| if (!info) { |
| story4_info_after_delete_.Pass(); |
| } |
| |
| TestStory5(); |
| } |
| |
| // Test Case Story5: |
| // |
| // Create a story and start it with RequestStart() rather than Start(). |
| // |
| // Verify that, when the story is stopped, a request for |
| // SessionShell.DetachView() is received, and if the request is not answered, |
| // the Stop() request proceeds anyway. |
| |
| TestPoint story5_create_{"Story5 Create"}; |
| |
| void TestStory5() { |
| puppet_master_->ControlStory("story5", story_puppet_master_.NewRequest()); |
| |
| fuchsia::modular::AddMod add_mod; |
| add_mod.mod_name.push_back("mod1"); |
| add_mod.intent.handler = kCommonNullModule; |
| |
| fuchsia::modular::StoryCommand command; |
| command.set_add_mod(std::move(add_mod)); |
| |
| std::vector<fuchsia::modular::StoryCommand> commands; |
| commands.push_back(std::move(command)); |
| |
| story_puppet_master_->Enqueue(std::move(commands)); |
| story_puppet_master_->Execute( |
| [this](fuchsia::modular::ExecuteResult result) { |
| story5_create_.Pass(); |
| TestStory5_Run(); |
| }); |
| } |
| |
| TestPoint story5_state_before_run_{"Story5 State before Run"}; |
| TestPoint story5_state_after_run_{"Story5 State after Run"}; |
| |
| TestPoint story5_attach_view_{"Story5 attach View"}; |
| |
| void TestStory5_Run() { |
| story_provider()->GetController("story5", story_controller_.NewRequest()); |
| |
| story_controller_->GetInfo([this](fuchsia::modular::StoryInfo info, |
| fuchsia::modular::StoryState state) { |
| story_info_ = std::move(info); |
| if (state == fuchsia::modular::StoryState::STOPPED) { |
| story5_state_before_run_.Pass(); |
| } |
| }); |
| |
| // Start and show the new story using RequestStart(). |
| story_controller_->RequestStart(); |
| |
| session_shell_impl()->set_on_attach_view( |
| [this](ViewId) { story5_attach_view_.Pass(); }); |
| |
| story_controller_->GetInfo([this](fuchsia::modular::StoryInfo info, |
| fuchsia::modular::StoryState state) { |
| if (state == fuchsia::modular::StoryState::RUNNING) { |
| story5_state_after_run_.Pass(); |
| TestStory5_Stop(); |
| } |
| }); |
| } |
| |
| TestPoint story5_stop_{"Story5 Stop"}; |
| |
| void TestStory5_Stop() { |
| // Ignore the detach view. The delay is larger than the timeout for the |
| // whole test configured in dev_base_shell.cc, so an attempt to wait for |
| // this timeout would fail the whole test. |
| session_shell_impl()->set_detach_delay( |
| zx::msec(modular::testing::kTestTimeoutMilliseconds * 2)); |
| session_shell_impl()->set_on_detach_view([](ViewId) {}); |
| |
| story_controller_->Stop([this] { |
| TeardownStoryController(); |
| story5_stop_.Pass(); |
| |
| TestStory5_DeleteStory(); |
| }); |
| } |
| |
| TestPoint story5_delete_{"Story5 Delete"}; |
| |
| void TestStory5_DeleteStory() { |
| puppet_master_->DeleteStory(story_info_.id, |
| [this] { story5_delete_.Pass(); }); |
| |
| story_provider()->GetStoryInfo( |
| story_info_.id, [this](fuchsia::modular::StoryInfoPtr info) { |
| TestStory5_InfoAfterDeleteIsNull(std::move(info)); |
| }); |
| } |
| |
| TestPoint story5_info_after_delete_{"Story5 Info after Delete is null"}; |
| |
| void TestStory5_InfoAfterDeleteIsNull(fuchsia::modular::StoryInfoPtr info) { |
| if (!info) { |
| story5_info_after_delete_.Pass(); |
| } |
| |
| TestStory6(); |
| } |
| |
| // Test Case Story6: |
| // |
| // Create a story and start it with RequestStart() rather than Start(). |
| // |
| // Verify that, when the story is NOT stopped when the SessionShell is stopped |
| // (such as at Logout) NO request for SessionShell.DetachView() is received. |
| |
| TestPoint story6_create_{"Story6 Create"}; |
| |
| void TestStory6() { |
| puppet_master_->ControlStory("story6", story_puppet_master_.NewRequest()); |
| |
| fuchsia::modular::AddMod add_mod; |
| add_mod.mod_name.push_back("mod1"); |
| add_mod.intent.handler = kCommonNullModule; |
| |
| fuchsia::modular::StoryCommand command; |
| command.set_add_mod(std::move(add_mod)); |
| |
| std::vector<fuchsia::modular::StoryCommand> commands; |
| commands.push_back(std::move(command)); |
| |
| story_puppet_master_->Enqueue(std::move(commands)); |
| story_puppet_master_->Execute( |
| [this](fuchsia::modular::ExecuteResult result) { |
| story6_create_.Pass(); |
| TestStory6_Run(); |
| }); |
| } |
| |
| TestPoint story6_state_before_run_{"Story6 State before Run"}; |
| TestPoint story6_state_after_run_{"Story6 State after Run"}; |
| |
| TestPoint story6_attach_view_{"Story6 attach View"}; |
| |
| void TestStory6_Run() { |
| story_provider()->GetController("story6", story_controller_.NewRequest()); |
| |
| story_controller_->GetInfo([this](fuchsia::modular::StoryInfo info, |
| fuchsia::modular::StoryState state) { |
| story_info_ = std::move(info); |
| if (state == fuchsia::modular::StoryState::STOPPED) { |
| story6_state_before_run_.Pass(); |
| } |
| }); |
| |
| // Start and show the new story using RequestStart(). |
| story_controller_->RequestStart(); |
| |
| session_shell_impl()->set_on_attach_view( |
| [this](ViewId) { story6_attach_view_.Pass(); }); |
| |
| story_controller_->GetInfo([this](fuchsia::modular::StoryInfo info, |
| fuchsia::modular::StoryState state) { |
| if (state == fuchsia::modular::StoryState::RUNNING) { |
| story6_state_after_run_.Pass(); |
| TestStory6_Logout(); |
| } |
| }); |
| } |
| |
| void TestStory6_Logout() { |
| // If we get a DetachView() call during logout, that's a failure. |
| session_shell_impl()->set_detach_delay(zx::sec(0)); |
| session_shell_impl()->set_on_detach_view( |
| [](ViewId) { Fail("DetachView() Received on Logout"); }); |
| |
| Signal(modular::testing::kTestShutdown); |
| } |
| |
| void TeardownStoryController() { story_controller_.Unbind(); } |
| |
| StoryProviderStateWatcherImpl story_provider_state_watcher_; |
| |
| fuchsia::modular::PuppetMasterPtr puppet_master_; |
| fuchsia::modular::ComponentContextPtr component_context_; |
| fuchsia::modular::StoryPuppetMasterPtr story_puppet_master_; |
| fuchsia::modular::StoryControllerPtr story_controller_; |
| fuchsia::modular::LinkPtr session_shell_link_; |
| fuchsia::modular::StoryInfo story_info_; |
| |
| FXL_DISALLOW_COPY_AND_ASSIGN(TestApp); |
| }; |
| |
| } // namespace |
| |
| int main(int argc, const char** argv) { |
| auto command_line = fxl::CommandLineFromArgcArgv(argc, argv); |
| modular::testing::ComponentMain<TestApp>(); |
| return 0; |
| } |