1 // Copyright 2017 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/vr/test/ui_test.h"
6
7 #include "chrome/browser/vr/elements/rect.h"
8 #include "chrome/browser/vr/model/model.h"
9 #include "chrome/browser/vr/render_info.h"
10 #include "chrome/browser/vr/test/animation_utils.h"
11 #include "chrome/browser/vr/test/constants.h"
12 #include "chrome/browser/vr/ui.h"
13 #include "chrome/browser/vr/ui_scene.h"
14 #include "chrome/browser/vr/ui_scene_creator.h"
15 #include "ui/gfx/geometry/vector3d_f.h"
16
17 namespace vr {
18
19 namespace {
20
ComputeNormal(const gfx::Transform & transform)21 gfx::Vector3dF ComputeNormal(const gfx::Transform& transform) {
22 gfx::Vector3dF x_axis(1, 0, 0);
23 gfx::Vector3dF y_axis(0, 1, 0);
24 transform.TransformVector(&x_axis);
25 transform.TransformVector(&y_axis);
26 gfx::Vector3dF normal = CrossProduct(x_axis, y_axis);
27 normal.GetNormalized(&normal);
28 return normal;
29 }
30
WillElementFaceCamera(const UiElement * element)31 bool WillElementFaceCamera(const UiElement* element) {
32 // Element might become invisible due to incorrect rotation, i.e when rotation
33 // cause the visible side of the element flip.
34 // Here we calculate the dot product of (origin - center) and normal. If the
35 // result is greater than 0, it means the visible side of this element is
36 // facing camera.
37 gfx::Point3F center;
38 gfx::Transform transform = element->ComputeTargetWorldSpaceTransform();
39 transform.TransformPoint(¢er);
40
41 gfx::Point3F origin;
42 gfx::Vector3dF normal = ComputeNormal(transform);
43 if (center == origin) {
44 // If the center of element is at origin, as our camera facing negative z.
45 // we only need to make sure the normal of the element have positive z.
46 return normal.z() > 0.f;
47 }
48 return gfx::DotProduct(origin - center, normal) > 0.f;
49 }
50
51 // This method tests whether an element will be visible after all pending scene
52 // animations complete.
WillElementBeVisible(const UiElement * element)53 bool WillElementBeVisible(const UiElement* element) {
54 if (!element)
55 return false;
56 if (element->ComputeTargetOpacity() == 0.f)
57 return false;
58 if (!element->IsWorldPositioned())
59 return true;
60 return WillElementFaceCamera(element);
61 }
62
NumVisibleInTreeRecursive(const UiElement * element)63 int NumVisibleInTreeRecursive(const UiElement* element) {
64 int visible = WillElementBeVisible(element) ? 1 : 0;
65 for (auto& child : element->children())
66 visible += NumVisibleInTreeRecursive(child.get());
67 return visible;
68 }
69
70 } // namespace
71
UiTest()72 UiTest::UiTest() {}
~UiTest()73 UiTest::~UiTest() {}
74
SetUp()75 void UiTest::SetUp() {
76 browser_ = std::make_unique<testing::NiceMock<MockUiBrowserInterface>>();
77 }
78
CreateSceneInternal(const UiInitialState & state,std::unique_ptr<MockContentInputDelegate> content_input_delegate)79 void UiTest::CreateSceneInternal(
80 const UiInitialState& state,
81 std::unique_ptr<MockContentInputDelegate> content_input_delegate) {
82 content_input_delegate_ = content_input_delegate.get();
83 ui_instance_ = std::make_unique<Ui>(std::move(browser_.get()),
84 std::move(content_input_delegate),
85 nullptr, nullptr, nullptr, state);
86 ui_ = ui_instance_.get();
87 scene_ = ui_instance_->scene();
88 model_ = ui_instance_->model_for_test();
89 model_->controllers[0].transform.Translate3d(kStartControllerPosition);
90
91 OnBeginFrame();
92 // Need a second BeginFrame here because the first one will add controllers
93 // to the scene, which need an additional frame to get into a good state.
94 OnBeginFrame();
95 }
96
CreateScene(const UiInitialState & state)97 void UiTest::CreateScene(const UiInitialState& state) {
98 auto content_input_delegate =
99 std::make_unique<testing::NiceMock<MockContentInputDelegate>>();
100 CreateSceneInternal(state, std::move(content_input_delegate));
101 }
102
CreateScene(InWebVr in_web_vr)103 void UiTest::CreateScene(InWebVr in_web_vr) {
104 UiInitialState state;
105 state.in_web_vr = in_web_vr;
106 state.gvr_input_support = true;
107 CreateScene(state);
108 }
109
SetIncognito(bool incognito)110 void UiTest::SetIncognito(bool incognito) {
111 model_->incognito = incognito;
112 }
113
IsVisible(UiElementName name) const114 bool UiTest::IsVisible(UiElementName name) const {
115 OnBeginFrame();
116 UiElement* element = scene_->GetUiElementByName(name);
117 return WillElementBeVisible(element);
118 }
119
VerifyVisibility(const std::set<UiElementName> & names,bool expected_visibility) const120 bool UiTest::VerifyVisibility(const std::set<UiElementName>& names,
121 bool expected_visibility) const {
122 OnBeginFrame();
123 for (auto name : names) {
124 SCOPED_TRACE(UiElementNameToString(name));
125 UiElement* element = scene_->GetUiElementByName(name);
126 bool will_be_visible = WillElementBeVisible(element);
127 EXPECT_EQ(will_be_visible, expected_visibility);
128 if (will_be_visible != expected_visibility)
129 return false;
130 }
131 return true;
132 }
133
VerifyOnlyElementsVisible(const std::string & trace_context,const std::set<UiElementName> & names) const134 void UiTest::VerifyOnlyElementsVisible(
135 const std::string& trace_context,
136 const std::set<UiElementName>& names) const {
137 OnBeginFrame();
138 SCOPED_TRACE(trace_context);
139 for (auto* element : scene_->GetAllElements()) {
140 SCOPED_TRACE(element->DebugName());
141 UiElementName name = element->name();
142 UiElementName owner_name = element->owner_name_for_test();
143 if (element->draw_phase() == kPhaseNone && owner_name == kNone) {
144 EXPECT_TRUE(names.find(name) == names.end());
145 continue;
146 }
147 if (name == kNone)
148 name = owner_name;
149 bool should_be_visible = (names.find(name) != names.end());
150 EXPECT_EQ(WillElementBeVisible(element), should_be_visible);
151 }
152 }
153
NumVisibleInTree(UiElementName name) const154 int UiTest::NumVisibleInTree(UiElementName name) const {
155 OnBeginFrame();
156 auto* root = scene_->GetUiElementByName(name);
157 EXPECT_NE(root, nullptr);
158 if (!root) {
159 return 0;
160 }
161 return NumVisibleInTreeRecursive(root);
162 }
163
VerifyIsAnimating(const std::set<UiElementName> & names,const std::vector<TargetProperty> & properties,bool animating) const164 bool UiTest::VerifyIsAnimating(const std::set<UiElementName>& names,
165 const std::vector<TargetProperty>& properties,
166 bool animating) const {
167 OnBeginFrame();
168 for (auto name : names) {
169 auto* element = scene_->GetUiElementByName(name);
170 EXPECT_NE(nullptr, element);
171 SCOPED_TRACE(element->DebugName());
172 if (IsAnimating(element, properties) != animating) {
173 return false;
174 }
175 }
176 return true;
177 }
178
VerifyRequiresLayout(const std::set<UiElementName> & names,bool requires_layout) const179 bool UiTest::VerifyRequiresLayout(const std::set<UiElementName>& names,
180 bool requires_layout) const {
181 OnBeginFrame();
182 for (auto name : names) {
183 SCOPED_TRACE(name);
184 auto* element = scene_->GetUiElementByName(name);
185 EXPECT_NE(nullptr, element);
186 if (!element || element->requires_layout() != requires_layout) {
187 return false;
188 }
189 }
190 return true;
191 }
192
RunForMs(float milliseconds)193 bool UiTest::RunForMs(float milliseconds) {
194 return RunFor(base::TimeDelta::FromMilliseconds(milliseconds));
195 }
196
RunForSeconds(float seconds)197 bool UiTest::RunForSeconds(float seconds) {
198 return RunFor(base::TimeDelta::FromSecondsD(seconds));
199 }
200
AdvanceFrame()201 bool UiTest::AdvanceFrame() {
202 current_time_ += base::TimeDelta::FromMilliseconds(16);
203 return OnBeginFrame();
204 }
205
GetBackgroundColor(SkColor * background_color) const206 void UiTest::GetBackgroundColor(SkColor* background_color) const {
207 OnBeginFrame();
208 Rect* background =
209 static_cast<Rect*>(scene_->GetUiElementByName(kSolidBackground));
210 ASSERT_NE(nullptr, background);
211 EXPECT_EQ(background->center_color(), background->edge_color());
212 *background_color = background->edge_color();
213 }
214
ClickElement(UiElement * element)215 void UiTest::ClickElement(UiElement* element) {
216 // Synthesize a controller vector targeting the element.
217 gfx::Point3F target;
218 element->ComputeTargetWorldSpaceTransform().TransformPoint(&target);
219 gfx::Point3F origin;
220 gfx::Vector3dF direction(target - origin);
221 direction.GetNormalized(&direction);
222
223 RenderInfo render_info;
224 ReticleModel reticle_model;
225 InputEventList input_event_list;
226 ControllerModel controller_model;
227 controller_model.laser_direction = direction;
228 controller_model.laser_origin = origin;
229
230 controller_model.touchpad_button_state = ControllerModel::ButtonState::kDown;
231 ui_instance_->input_manager()->HandleInput(current_time_, render_info,
232 controller_model, &reticle_model,
233 &input_event_list);
234 OnBeginFrame();
235
236 controller_model.touchpad_button_state = ControllerModel::ButtonState::kUp;
237 ui_instance_->input_manager()->HandleInput(current_time_, render_info,
238 controller_model, &reticle_model,
239 &input_event_list);
240 OnBeginFrame();
241 }
242
RunFor(base::TimeDelta delta)243 bool UiTest::RunFor(base::TimeDelta delta) {
244 base::TimeTicks target_time = current_time_ + delta;
245 base::TimeDelta frame_time = base::TimeDelta::FromMilliseconds(16);
246 bool changed = false;
247
248 // Run a frame in the near future to trigger new state changes.
249 current_time_ += frame_time;
250 changed |= OnBeginFrame();
251
252 // If needed, skip ahead and run another frame at the target time.
253 if (current_time_ < target_time) {
254 current_time_ = target_time;
255 changed |= OnBeginFrame();
256 }
257
258 return changed;
259 }
260
OnBeginFrame() const261 bool UiTest::OnBeginFrame() const {
262 bool changed = false;
263 model_->current_time = current_time_;
264 changed |= scene_->OnBeginFrame(current_time_, kStartHeadPose);
265 if (scene_->HasDirtyTextures()) {
266 scene_->UpdateTextures();
267 changed = true;
268 }
269 return changed;
270 }
271
272 } // namespace vr
273