OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "chrome/browser/ui/test/test_browser_dialog.h" |
| 6 |
| 7 #include "base/command_line.h" |
| 8 #include "base/message_loop/message_loop.h" |
| 9 #include "chrome/browser/platform_util.h" |
| 10 #include "ui/base/material_design/material_design_controller.h" |
| 11 #include "ui/base/test/material_design_controller_test_api.h" |
| 12 #include "ui/base/test/user_interactive_test_case.h" |
| 13 #include "ui/base/ui_base_switches.h" |
| 14 #include "ui/views/widget/widget.h" |
| 15 #include "ui/views/widget/widget_observer.h" |
| 16 |
| 17 namespace { |
| 18 |
| 19 // An automatic action for WidgetCloser to post to the RunLoop. |
| 20 // TODO(tapted): Explore asynchronous Widget::Close() and DialogClientView:: |
| 21 // {Accept,Cancel}Window() approaches to test other dialog lifetimes. |
| 22 enum class DialogAction { |
| 23 INTERACTIVE, // Run interactively. |
| 24 CLOSE_NOW, // Call Widget::CloseNow(). |
| 25 }; |
| 26 |
| 27 // Helper to break out of the nested run loop that runs a test dialog. |
| 28 class WidgetCloser : public views::WidgetObserver { |
| 29 public: |
| 30 WidgetCloser(views::Widget* widget, DialogAction action) |
| 31 : widget_(widget), weak_ptr_factory_(this) { |
| 32 widget->AddObserver(this); |
| 33 if (action == DialogAction::INTERACTIVE) |
| 34 return; |
| 35 |
| 36 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 37 FROM_HERE, |
| 38 base::Bind(&WidgetCloser::CloseNow, weak_ptr_factory_.GetWeakPtr())); |
| 39 } |
| 40 |
| 41 // WidgetObserver: |
| 42 void OnWidgetDestroyed(views::Widget* widget) override { |
| 43 widget_->RemoveObserver(this); |
| 44 widget_ = nullptr; |
| 45 base::MessageLoop::current()->QuitNow(); |
| 46 } |
| 47 |
| 48 private: |
| 49 void CloseNow() { |
| 50 if (widget_) |
| 51 widget_->CloseNow(); |
| 52 } |
| 53 |
| 54 views::Widget* widget_; |
| 55 |
| 56 base::WeakPtrFactory<WidgetCloser> weak_ptr_factory_; |
| 57 |
| 58 DISALLOW_COPY_AND_ASSIGN(WidgetCloser); |
| 59 }; |
| 60 |
| 61 // Returns the test case portion of a --dialog= switch argument. |
| 62 std::string CaseFromDialogDescription(const std::string& argument) { |
| 63 std::string::size_type dot = argument.find('.'); |
| 64 return dot == std::string::npos ? std::string() : argument.substr(dot + 1); |
| 65 } |
| 66 |
| 67 } // namespace |
| 68 |
| 69 // static |
| 70 std::vector<std::string> TestDialogInterface::NameProvider() { |
| 71 return {"Default"}; |
| 72 } |
| 73 |
| 74 // static |
| 75 std::vector<std::string>& TestDialogInterface::GetDialogTestCases() { |
| 76 CR_DEFINE_STATIC_LOCAL(std::vector<std::string>, all_cases, ()); |
| 77 return all_cases; |
| 78 } |
| 79 |
| 80 // static |
| 81 int TestDialogInterface::Register(const char* harness, |
| 82 NameProviderFunction* name_provider) { |
| 83 const std::string prefix = harness + std::string("."); |
| 84 std::vector<std::string>& all_cases = GetDialogTestCases(); |
| 85 for (const std::string& name : name_provider()) |
| 86 all_cases.push_back(prefix + name); |
| 87 return 1; |
| 88 } |
| 89 |
| 90 // static |
| 91 void TestDialogInterface::TestBrowserDialogRun( |
| 92 TestDialogInterface* harness, |
| 93 const std::vector<std::string>& available_cases) { |
| 94 const base::CommandLine& command_line = |
| 95 *base::CommandLine::ForCurrentProcess(); |
| 96 |
| 97 #if defined(OS_MACOSX) |
| 98 // The rest of this method assumes the child dialog is toolkit-views. So, for |
| 99 // Mac, it will only work if --secondary-ui-md is passed. Without this, a |
| 100 // Cocoa dialog will be created, which TestDialogInterface doesn't support. |
| 101 // Force SecondaryUiMaterial() on Mac to get coverage on the bots. Leave it |
| 102 // optional elsewhere so that the non-MD dialog can be invoked to compare. |
| 103 ui::test::MaterialDesignControllerTestAPI md_test_api( |
| 104 ui::MaterialDesignController::GetMode()); |
| 105 md_test_api.SetSecondaryUiMaterial(true); |
| 106 #endif |
| 107 |
| 108 int dialog_index = 0; |
| 109 std::string dialog_name = |
| 110 command_line.GetSwitchValueASCII(internal::kDialogSwitch); |
| 111 if (!dialog_name.empty()) { |
| 112 const std::string case_name = CaseFromDialogDescription(dialog_name); |
| 113 auto it = |
| 114 std::find(available_cases.begin(), available_cases.end(), case_name); |
| 115 ASSERT_NE(it, available_cases.end()); |
| 116 dialog_index = std::distance(available_cases.begin(), it); |
| 117 } |
| 118 |
| 119 gfx::NativeView parent = |
| 120 platform_util::GetViewForWindow(harness->DialogParent()); |
| 121 views::Widget::Widgets widgets_before; |
| 122 views::Widget::GetAllChildWidgets(parent, &widgets_before); |
| 123 |
| 124 harness->ShowDialog(dialog_index); |
| 125 views::Widget::Widgets widgets_after; |
| 126 views::Widget::GetAllChildWidgets(parent, &widgets_after); |
| 127 |
| 128 auto added = base::STLSetDifference<std::vector<views::Widget*>>( |
| 129 widgets_after, widgets_before); |
| 130 |
| 131 // This can fail if no dialog was shown, if the dialog shown wasn't a toolkit- |
| 132 // views dialog, or if more than one child dialog was shown. |
| 133 ASSERT_EQ(1u, added.size()); |
| 134 |
| 135 const DialogAction action = |
| 136 command_line.HasSwitch(internal::kInteractiveSwitch) |
| 137 ? DialogAction::INTERACTIVE |
| 138 : DialogAction::CLOSE_NOW; |
| 139 |
| 140 WidgetCloser closer(added[0], action); |
| 141 ::test::RunTestInteractively(); |
| 142 } |
OLD | NEW |