Index: ui/chromeos/touch_exploration_controller_unittest.cc |
diff --git a/ui/chromeos/touch_exploration_controller_unittest.cc b/ui/chromeos/touch_exploration_controller_unittest.cc |
index 3d0fbb69a86ad8052e03d58260c7a46c4ef879e1..c75491cf95adc3aabd4c4e8241fe193c2d6a0148 100644 |
--- a/ui/chromeos/touch_exploration_controller_unittest.cc |
+++ b/ui/chromeos/touch_exploration_controller_unittest.cc |
@@ -14,6 +14,7 @@ |
#include "ui/events/event.h" |
#include "ui/events/event_utils.h" |
#include "ui/events/gestures/gesture_provider_aura.h" |
+#include "ui/events/test/events_test_utils.h" |
#include "ui/gfx/geometry/point.h" |
#include "ui/gl/gl_implementation.h" |
#include "ui/gl/gl_surface.h" |
@@ -62,6 +63,14 @@ class EventCapturer : public ui::EventHandler { |
DISALLOW_COPY_AND_ASSIGN(EventCapturer); |
}; |
+int Factorial(int n) { |
+ if (n <= 0) |
+ return 0; |
+ if (n == 1) |
+ return 1; |
+ return n * Factorial(n - 1); |
+} |
+ |
} // namespace |
class TouchExplorationTest : public aura::test::AuraTestBase { |
@@ -141,6 +150,15 @@ class TouchExplorationTest : public aura::test::AuraTestBase { |
touch_exploration_controller_->CallTapTimerNowForTesting(); |
} |
+ void AdvanceSimulatedTimePastPotentialTapDelay() { |
+ simulated_clock_->Advance(base::TimeDelta::FromMilliseconds(1000)); |
+ touch_exploration_controller_->CallTapTimerNowIfRunningForTesting(); |
+ } |
+ |
+ void SuppressVLOGs(bool suppress) { |
+ touch_exploration_controller_->SuppressVLOGsForTesting(suppress); |
+ } |
+ |
void SwitchTouchExplorationMode(bool on) { |
if (!on && touch_exploration_controller_.get()) { |
touch_exploration_controller_.reset(); |
@@ -1139,6 +1157,123 @@ TEST_F(TouchExplorationTest, GestureSwipe) { |
} |
} |
+// Since there are so many permutations, this test is too slow in debug |
+// mode, so it will only be run in release mode. |
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) |
+#define MAYBE_AllFingerPermutations AllFingerPermutations |
+#else |
+#define MAYBE_AllFingerPermutations DISABLED_AllFingerPermutations |
+#endif |
+ |
+TEST_F(TouchExplorationTest, MAYBE_AllFingerPermutations) { |
+ SwitchTouchExplorationMode(true); |
+ SuppressVLOGs(true); |
+ // We will test all permutations of events from three different fingers |
+ // to ensure that we return to NO_FINGERS_DOWN when fingers have been |
+ // released. |
+ ScopedVector<ui::TouchEvent> all_events; |
+ for (int touch_id = 0; touch_id < 3; touch_id++){ |
+ int x = 10*touch_id + 1; |
+ int y = 10*touch_id + 2; |
+ all_events.push_back(new TouchEvent( |
+ ui::ET_TOUCH_PRESSED, gfx::Point(x++, y++), touch_id, Now())); |
+ all_events.push_back(new TouchEvent( |
+ ui::ET_TOUCH_MOVED, gfx::Point(x++, y++), touch_id, Now())); |
+ all_events.push_back(new TouchEvent( |
+ ui::ET_TOUCH_RELEASED, gfx::Point(x, y), touch_id, Now())); |
+ } |
+ |
+ // I'm going to explain this algorithm, and use an example in parentheses. |
+ // The example will be all permutations of a b c d. |
+ // There are four letters and 4! = 24 permutations. |
+ const int num_events = all_events.size(); |
+ const int num_permutations = Factorial(num_events); |
+ |
+ for (int p = 0; p < num_permutations; p++) { |
+ std::vector<ui::TouchEvent*> queued_events = all_events.get(); |
+ std::vector<bool> fingers_pressed(3, false); |
+ |
+ int current_num_permutations = num_permutations; |
+ for (int events_left = num_events; events_left > 0; events_left--) { |
+ // |p| indexes to each permutation when there are num_permutations |
+ // permutations. (e.g. 0 is abcd, 1 is abdc, 2 is acbd, 3 is acdb...) |
+ // But how do we find the index for the current number of permutations? |
+ // To find the permutation within the part of the sequence we're |
+ // currently looking at, we need a number between 0 and |
+ // |current_num_permutations| - 1. |
+ // (e.g. if we already chose the first letter, there are 3! = 6 |
+ // options left, so we do p % 6. So |current_permutation| would go |
+ // from 0 to 5 and then reset to 0 again, for all combinations of |
+ // whichever three letters are remaining, as we loop through the |
+ // permutations) |
+ int current_permutation = p % current_num_permutations; |
+ |
+ // Since this is is the total number of permutations starting with |
+ // this event and including future events, there could be multiple |
+ // values of current_permutation that will generate the same event |
+ // in this iteration. |
+ // (e.g. If we chose 'a' but have b c d to choose from, we choose b when |
+ // |current_permutation| = 0, 1 and c when |current_permutation| = 2, 3. |
+ // Note that each letter gets two numbers, which is the next |
+ // current_num_permutations, 2! for the two letters left.) |
+ |
+ // Branching out from the first event, there are num_permutations |
+ // permutations, and each value of |p| is associated with one of these |
+ // permutations. However, once the first event is chosen, there |
+ // are now |num_events| - 1 events left, so the number of permutations |
+ // for the rest of the events changes, and will always be equal to |
+ // the factorial of the events_left. |
+ // (e.g. There are 3! = 6 permutations that start with 'a', so if we |
+ // start with 'a' there will be 6 ways to then choose from b c d.) |
+ // So we now set-up for the next iteration by setting |
+ // current_num_permutations to the factorial of the next number of |
+ // events left. |
+ current_num_permutations /= events_left; |
+ |
+ // To figure out what current event we want to choose, we integer |
+ // divide the current permutation by the next current_num_permutations. |
+ // (e.g. If there are 4 letters a b c d and 24 permutations, we divide |
+ // by 24/4 = 6. Values 0 to 5 when divided by 6 equals 0, so the first |
+ // 6 permutations start with 'a', and the last 6 will start with 'd'. |
+ // Note that there are 6 that start with 'a' because there are 6 |
+ // permutations for the next three letters that follow 'a'.) |
+ int index = current_permutation / current_num_permutations; |
+ |
+ ui::TouchEvent* next_dispatch = queued_events[index]; |
+ ASSERT_TRUE(next_dispatch != NULL); |
+ |
+ // |next_dispatch| has to be put in this container so that its time |
+ // stamp can be changed to this point in the test, when it is being |
+ // dispatched.. |
+ EventTestApi test_dispatch(next_dispatch); |
+ test_dispatch.set_time_stamp(Now()); |
+ generator_->Dispatch(next_dispatch); |
+ queued_events.erase(queued_events.begin() + index); |
+ |
+ // Keep track of what fingers have been pressed, to release |
+ // only those fingers at the end, so the check for being in |
+ // no fingers down can be accurate. |
+ if (next_dispatch->type() == ET_TOUCH_PRESSED) { |
+ fingers_pressed[next_dispatch->touch_id()] = true; |
+ } else if (next_dispatch->type() == ET_TOUCH_RELEASED) { |
+ fingers_pressed[next_dispatch->touch_id()] = false; |
+ } |
+ } |
+ ASSERT_EQ(queued_events.size(), 0u); |
+ |
+ // Release fingers recorded as pressed. |
+ for(int j = 0; j < int(fingers_pressed.size()); j++){ |
+ if (fingers_pressed[j] == true) { |
+ generator_->ReleaseTouchId(j); |
+ fingers_pressed[j] = false; |
+ } |
+ } |
+ AdvanceSimulatedTimePastPotentialTapDelay(); |
+ EXPECT_TRUE(IsInNoFingersDownState()); |
+ ClearCapturedEvents(); |
+ } |
+} |
+ |
// With the simple swipe gestures, if additional fingers are added, then the |
// state should change to passthrough. |
TEST_F(TouchExplorationTest, FromGestureToPassthrough) { |