root/ui/display/chromeos/x11/native_display_event_dispatcher_x11_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. CreateOutput
  2. num_calls_update_xrandr_config
  3. num_calls_notify_observers
  4. set_cached_outputs
  5. num_calls_notify_observers_
  6. UpdateXRandRConfiguration
  7. GetCachedOutputs
  8. NotifyDisplayObservers
  9. test_tick_clock_
  10. DispatchScreenChangeEvent
  11. DispatchOutputChangeEvent
  12. TEST_F
  13. TEST_F
  14. TEST_F
  15. TEST_F
  16. TEST_F
  17. TEST_F
  18. TEST_F
  19. TEST_F
  20. TEST_F
  21. TEST_F
  22. TEST_F

// Copyright 2014 The Chromium 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 <X11/extensions/Xrandr.h>

#undef Bool
#undef None

#include "base/test/simple_test_tick_clock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/display/chromeos/x11/display_mode_x11.h"
#include "ui/display/chromeos/x11/display_snapshot_x11.h"
#include "ui/display/chromeos/x11/native_display_delegate_x11.h"
#include "ui/display/chromeos/x11/native_display_event_dispatcher_x11.h"

namespace ui {

namespace {

const DisplayModeX11 kDefaultDisplayMode(gfx::Size(1, 1), false, 60.0f, 20);

DisplaySnapshotX11* CreateOutput(RROutput output, RRCrtc crtc) {
  DisplaySnapshotX11* snapshot = new DisplaySnapshotX11(
      0,
      false,
      gfx::Point(0, 0),
      gfx::Size(0, 0),
      OUTPUT_TYPE_UNKNOWN,
      false,
      false,
      std::string(),
      std::vector<const DisplayMode*>(1, &kDefaultDisplayMode),
      &kDefaultDisplayMode,
      NULL,
      output,
      crtc,
      0);

  return snapshot;
}

class TestHelperDelegate : public NativeDisplayDelegateX11::HelperDelegate {
 public:
  TestHelperDelegate();
  virtual ~TestHelperDelegate();

  int num_calls_update_xrandr_config() const {
    return num_calls_update_xrandr_config_;
  }

  int num_calls_notify_observers() const { return num_calls_notify_observers_; }

  void set_cached_outputs(const std::vector<DisplaySnapshot*>& outputs) {
    cached_outputs_ = outputs;
  }

  // NativeDisplayDelegateX11::HelperDelegate overrides:
  virtual void UpdateXRandRConfiguration(const base::NativeEvent& event)
      OVERRIDE;
  virtual const std::vector<DisplaySnapshot*>& GetCachedOutputs() const
      OVERRIDE;
  virtual void NotifyDisplayObservers() OVERRIDE;

 private:
  int num_calls_update_xrandr_config_;
  int num_calls_notify_observers_;

  std::vector<DisplaySnapshot*> cached_outputs_;

  DISALLOW_COPY_AND_ASSIGN(TestHelperDelegate);
};

TestHelperDelegate::TestHelperDelegate()
    : num_calls_update_xrandr_config_(0), num_calls_notify_observers_(0) {}

TestHelperDelegate::~TestHelperDelegate() {}

void TestHelperDelegate::UpdateXRandRConfiguration(
    const base::NativeEvent& event) {
  ++num_calls_update_xrandr_config_;
}

const std::vector<DisplaySnapshot*>& TestHelperDelegate::GetCachedOutputs()
    const {
  return cached_outputs_;
}

void TestHelperDelegate::NotifyDisplayObservers() {
  ++num_calls_notify_observers_;
}

////////////////////////////////////////////////////////////////////////////////
// NativeDisplayEventDispatcherX11Test

class NativeDisplayEventDispatcherX11Test : public testing::Test {
 public:
  NativeDisplayEventDispatcherX11Test();
  virtual ~NativeDisplayEventDispatcherX11Test();

 protected:
  void DispatchScreenChangeEvent();
  void DispatchOutputChangeEvent(RROutput output,
                                 RRCrtc crtc,
                                 RRMode mode,
                                 bool connected);

  int xrandr_event_base_;
  scoped_ptr<TestHelperDelegate> helper_delegate_;
  scoped_ptr<NativeDisplayEventDispatcherX11> dispatcher_;
  base::SimpleTestTickClock* test_tick_clock_;  // Owned by |dispatcher_|.

 private:
  DISALLOW_COPY_AND_ASSIGN(NativeDisplayEventDispatcherX11Test);
};

NativeDisplayEventDispatcherX11Test::NativeDisplayEventDispatcherX11Test()
    : xrandr_event_base_(10),
      helper_delegate_(new TestHelperDelegate()),
      dispatcher_(new NativeDisplayEventDispatcherX11(helper_delegate_.get(),
                                                      xrandr_event_base_)),
      test_tick_clock_(new base::SimpleTestTickClock) {
  test_tick_clock_->Advance(base::TimeDelta::FromMilliseconds(1));
  dispatcher_->SetTickClockForTest(
      scoped_ptr<base::TickClock>(test_tick_clock_));
}

NativeDisplayEventDispatcherX11Test::~NativeDisplayEventDispatcherX11Test() {}

void NativeDisplayEventDispatcherX11Test::DispatchScreenChangeEvent() {
  XRRScreenChangeNotifyEvent event = {0};
  event.type = xrandr_event_base_ + RRScreenChangeNotify;

  dispatcher_->Dispatch(reinterpret_cast<const base::NativeEvent>(&event));
}

void NativeDisplayEventDispatcherX11Test::DispatchOutputChangeEvent(
    RROutput output,
    RRCrtc crtc,
    RRMode mode,
    bool connected) {
  XRROutputChangeNotifyEvent event = {0};
  event.type = xrandr_event_base_ + RRNotify;
  event.subtype = RRNotify_OutputChange;
  event.output = output;
  event.crtc = crtc;
  event.mode = mode;
  event.connection = connected ? RR_Connected : RR_Disconnected;

  dispatcher_->Dispatch(reinterpret_cast<const base::NativeEvent>(&event));
}

}  // namespace

TEST_F(NativeDisplayEventDispatcherX11Test, OnScreenChangedEvent) {
  DispatchScreenChangeEvent();
  EXPECT_EQ(1, helper_delegate_->num_calls_update_xrandr_config());
  EXPECT_EQ(0, helper_delegate_->num_calls_notify_observers());
}

TEST_F(NativeDisplayEventDispatcherX11Test, CheckNotificationOnFirstEvent) {
  DispatchOutputChangeEvent(1, 10, 20, true);
  EXPECT_EQ(0, helper_delegate_->num_calls_update_xrandr_config());
  EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers());
}

TEST_F(NativeDisplayEventDispatcherX11Test, CheckNotificationAfterSecondEvent) {
  DispatchOutputChangeEvent(1, 10, 20, true);

  // Simulate addition of the first output to the cached output list.
  ScopedVector<DisplaySnapshot> outputs;
  outputs.push_back(CreateOutput(1, 10));
  helper_delegate_->set_cached_outputs(outputs.get());

  DispatchOutputChangeEvent(2, 11, 20, true);
  EXPECT_EQ(2, helper_delegate_->num_calls_notify_observers());
}

TEST_F(NativeDisplayEventDispatcherX11Test, AvoidNotificationOnDuplicateEvent) {
  ScopedVector<DisplaySnapshot> outputs;
  outputs.push_back(CreateOutput(1, 10));
  helper_delegate_->set_cached_outputs(outputs.get());

  // Very first event will not be ignored.
  DispatchOutputChangeEvent(1, 10, 20, true);
  EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers());

  DispatchOutputChangeEvent(1, 10, 20, true);
  EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers());
}

TEST_F(NativeDisplayEventDispatcherX11Test, CheckNotificationOnDisconnect) {
  ScopedVector<DisplaySnapshot> outputs;
  outputs.push_back(CreateOutput(1, 10));
  helper_delegate_->set_cached_outputs(outputs.get());

  DispatchOutputChangeEvent(1, 10, 20, false);
  EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers());
}

TEST_F(NativeDisplayEventDispatcherX11Test, CheckNotificationOnModeChange) {
  ScopedVector<DisplaySnapshot> outputs;
  outputs.push_back(CreateOutput(1, 10));
  helper_delegate_->set_cached_outputs(outputs.get());

  DispatchOutputChangeEvent(1, 10, 21, true);
  EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers());
}

TEST_F(NativeDisplayEventDispatcherX11Test, CheckNotificationOnSecondOutput) {
  ScopedVector<DisplaySnapshot> outputs;
  outputs.push_back(CreateOutput(1, 10));
  helper_delegate_->set_cached_outputs(outputs.get());

  DispatchOutputChangeEvent(2, 11, 20, true);
  EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers());
}

TEST_F(NativeDisplayEventDispatcherX11Test, CheckNotificationOnDifferentCrtc) {
  ScopedVector<DisplaySnapshot> outputs;
  outputs.push_back(CreateOutput(1, 10));
  helper_delegate_->set_cached_outputs(outputs.get());

  DispatchOutputChangeEvent(1, 11, 20, true);
  EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers());
}

TEST_F(NativeDisplayEventDispatcherX11Test,
       CheckNotificationOnSecondOutputDisconnect) {
  ScopedVector<DisplaySnapshot> outputs;
  outputs.push_back(CreateOutput(1, 10));
  outputs.push_back(CreateOutput(2, 11));
  helper_delegate_->set_cached_outputs(outputs.get());

  DispatchOutputChangeEvent(2, 11, 20, false);
  EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers());
}

TEST_F(NativeDisplayEventDispatcherX11Test,
       AvoidDuplicateNotificationOnSecondOutputDisconnect) {
  ScopedVector<DisplaySnapshot> outputs;
  outputs.push_back(CreateOutput(1, 10));
  outputs.push_back(CreateOutput(2, 11));
  helper_delegate_->set_cached_outputs(outputs.get());

  DispatchOutputChangeEvent(2, 11, 20, false);
  EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers());

  // Simulate removal of second output from cached output list.
  outputs.erase(outputs.begin() + 1);
  helper_delegate_->set_cached_outputs(outputs.get());

  DispatchOutputChangeEvent(2, 11, 20, false);
  EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers());
}

TEST_F(NativeDisplayEventDispatcherX11Test,
       ForceUpdateAfterCacheExpiration) {
  // +1 to compenstate a possible rounding error.
  const int kHalfOfExpirationMs =
      NativeDisplayEventDispatcherX11::kCachedOutputsExpirationMs / 2 + 1;

  ScopedVector<DisplaySnapshot> outputs;
  outputs.push_back(CreateOutput(1, 10));
  outputs.push_back(CreateOutput(2, 11));
  helper_delegate_->set_cached_outputs(outputs.get());

  EXPECT_EQ(0, helper_delegate_->num_calls_notify_observers());

  DispatchOutputChangeEvent(2, 11, 20, true);
  EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers());

  // Duplicated event will be ignored.
  DispatchOutputChangeEvent(2, 11, 20, true);
  EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers());

  test_tick_clock_->Advance(base::TimeDelta::FromMilliseconds(
      kHalfOfExpirationMs));

  // Duplicated event will still be ignored.
  DispatchOutputChangeEvent(2, 11, 20, true);
  EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers());

  // Duplicated event does notify after expiration timeout.
  test_tick_clock_->Advance(
      base::TimeDelta::FromMilliseconds(kHalfOfExpirationMs));
  DispatchOutputChangeEvent(2, 11, 20, true);
  EXPECT_EQ(2, helper_delegate_->num_calls_notify_observers());

  // Last update time has been updated, so next duplicated change event
  // will be ignored.
  DispatchOutputChangeEvent(2, 11, 20, true);
  EXPECT_EQ(2, helper_delegate_->num_calls_notify_observers());

  // Another duplicated change event arrived within expiration time will
  // be ignored again.
  test_tick_clock_->Advance(base::TimeDelta::FromMilliseconds(
      kHalfOfExpirationMs));
  DispatchOutputChangeEvent(2, 11, 20, true);
  EXPECT_EQ(2, helper_delegate_->num_calls_notify_observers());
}

}  // namespace ui

/* [<][>][^][v][top][bottom][index][help] */