blob: ffc39d6d5e4bb439ac3cc9ea8498972c5cf43a80 [file] [log] [blame]
// Copyright 2020 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 "ui/gfx/x/property_cache.h"
#include <memory>
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/x/x11_atom_cache.h"
#include "ui/gfx/x/xproto.h"
#include "ui/gfx/x/xproto_util.h"
namespace x11 {
class PropertyCacheTest : public testing::Test {
public:
~PropertyCacheTest() override = default;
protected:
base::flat_map<Atom, PropertyCache::PropertyValue>& GetProperties(
PropertyCache* property_cache) {
return property_cache->properties_;
}
Connection* connection() { return connection_; }
Window window() { return window_; }
private:
void SetUp() override {
connection_ = Connection::Get();
window_ = CreateDummyWindow("");
}
void TearDown() override {
connection_->DestroyWindow({window_}).Sync();
window_ = Window::None;
connection_ = nullptr;
}
Connection* connection_ = nullptr;
Window window_ = Window::None;
};
TEST_F(PropertyCacheTest, GetSync) {
auto atom = x11::GetAtom("DUMMY ATOM");
SetProperty(window(), atom, Atom::CARDINAL, 1234);
PropertyCache cache(connection(), window(), {atom});
// The cache should Sync() on getting the value.
EXPECT_FALSE(GetProperties(&cache)[atom].response.has_value());
auto* value = cache.GetAs<uint32_t>(atom);
EXPECT_TRUE(GetProperties(&cache)[atom].response.has_value());
ASSERT_TRUE(value);
EXPECT_EQ(*value, 1234u);
}
TEST_F(PropertyCacheTest, GetAsync) {
auto atom = x11::GetAtom("DUMMY ATOM");
SetProperty(window(), atom, Atom::CARDINAL, 1234);
PropertyCache cache(connection(), window(), {atom});
// The cache should not Sync() unnecessarily.
EXPECT_FALSE(GetProperties(&cache)[atom].response.has_value());
connection()->Sync();
connection()->DispatchAll();
EXPECT_TRUE(GetProperties(&cache)[atom].response.has_value());
auto* value = cache.GetAs<uint32_t>(atom);
ASSERT_TRUE(value);
EXPECT_EQ(*value, 1234u);
}
TEST_F(PropertyCacheTest, Event) {
auto atom = x11::GetAtom("DUMMY ATOM");
SetProperty(window(), atom, Atom::CARDINAL, 1234);
PropertyCache cache(connection(), window(), {atom});
auto* value = cache.GetAs<uint32_t>(atom);
ASSERT_TRUE(value);
EXPECT_EQ(*value, 1234u);
// Change the property and sync to ensure the PropertyNotify event is ready to
// be dispatched.
SetProperty(window(), atom, Atom::CARDINAL, 5678);
connection()->Sync();
connection()->ReadResponses();
// Dispatch the PropertyNotify event, which will cause the PropertyCache to
// send another GetPropertyRequest. Calling DispatchAll() would introduce a
// race condition where we could get the GetPropertyResponse early if the 2
// round trips are completed fast enough. To avoid this, we use Dispatch(),
// which doesn't read or write on the socket.
while (connection()->Dispatch()) {
}
// We don't have the new GetPropertyResponse yet, so the old value should
// still be there.
value = cache.GetAs<uint32_t>(atom);
ASSERT_TRUE(value);
EXPECT_EQ(*value, 1234u);
// Complete the second round trip to acquire the new property value.
connection()->Sync();
connection()->DispatchAll();
// Now the cache should have the new value.
value = cache.GetAs<uint32_t>(atom);
ASSERT_TRUE(value);
EXPECT_EQ(*value, 5678u);
}
TEST_F(PropertyCacheTest, GetAs) {
auto atom = x11::GetAtom("DUMMY ATOM");
SetProperty(window(), atom, Atom::CARDINAL, 1234);
PropertyCache cache(connection(), window(), {atom});
// Get() should return the correct property value.
auto& response = cache.Get(atom);
ASSERT_TRUE(response);
EXPECT_EQ(response->bytes_after, 0u);
EXPECT_EQ(response->format, 32);
EXPECT_EQ(response->type, Atom::CARDINAL);
EXPECT_EQ(*response->value->front_as<uint32_t>(), 1234u);
EXPECT_EQ(response->value_len, 1u);
// GetAs() should do the same thing as Get().
size_t size = 0;
auto* value = cache.GetAs<uint32_t>(atom, &size);
EXPECT_EQ(size, 1u);
ASSERT_TRUE(value);
EXPECT_EQ(*value, 1234u);
// GetAs() should allow a nullptr size.
value = cache.GetAs<uint32_t>(atom /* size is defaulted to nullptr */);
ASSERT_TRUE(value);
EXPECT_EQ(*value, 1234u);
// The below checks make requests and requires event dispatching, so
// synchronize all requests to avoid verbosity from Sync()ing everywhere.
connection()->SynchronizeForTest(true);
// GetAs() should return nullptr if the type's size is mismatched.
SetProperty(window(), atom, Atom::CARDINAL, static_cast<uint8_t>(123));
connection()->DispatchAll();
value = cache.GetAs<uint32_t>(atom, &size);
EXPECT_EQ(size, 0u);
EXPECT_FALSE(value);
// GetAs() should return nullptr if the property has no elements.
SetArrayProperty(window(), atom, Atom::CARDINAL, std::vector<uint32_t>());
connection()->DispatchAll();
value = cache.GetAs<uint32_t>(atom, &size);
EXPECT_EQ(size, 0u);
EXPECT_FALSE(value);
// GetAs() should return nullptr if the property is deleted.
DeleteProperty(window(), atom);
connection()->DispatchAll();
value = cache.GetAs<uint32_t>(atom, &size);
EXPECT_EQ(size, 0u);
EXPECT_FALSE(value);
connection()->SynchronizeForTest(true);
}
} // namespace x11