blob: 179b264dbf7ec0b930fa2542b88cba61f5642359 [file] [log] [blame]
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <unordered_map>
#include "android/gui/FocusRequest.h"
#include "binder/Binder.h"
#include "binder/Parcel.h"
#include "gtest/gtest.h"
#include "gui/LayerState.h"
#include "gui/WindowInfo.h"
#include "gui/TransactionState.h"
namespace android {
void sprintf(std::string& out, const char* format, ...) {
va_list arg_list;
va_start(arg_list, format);
int len = vsnprintf(nullptr, 0, format, arg_list);
if (len < 0) {
va_end(arg_list);
}
std::string line(len, '\0');
int written = vsnprintf(line.data(), len + 1, format, arg_list);
if (written != len) {
va_end(arg_list);
}
line.pop_back();
out += line;
va_end(arg_list);
}
constexpr std::string dump_struct(auto& x) {
std::string s;
#if __has_builtin(__builtin_dump_struct)
__builtin_dump_struct(&x, sprintf, s);
#else
(void)x;
#endif
return s;
}
void PrintTo(const TransactionState& state, ::std::ostream* os) {
*os << dump_struct(state);
*os << state.mFrameTimelineInfo.toString();
for (auto mergedId : state.mMergedTransactionIds) {
*os << mergedId << ",";
}
}
void PrintTo(const ComposerState& state, ::std::ostream* os) {
*os << dump_struct(state.state);
*os << state.state.getWindowInfo();
}
// In case EXPECT_EQ fails, this function is useful to pinpoint exactly which
// field did not compare ==.
void Compare(const TransactionState& s1, const TransactionState& s2) {
EXPECT_EQ(s1.mId, s2.mId);
EXPECT_EQ(s1.mMergedTransactionIds, s2.mMergedTransactionIds);
EXPECT_EQ(s1.mFlags, s2.mFlags);
EXPECT_EQ(s1.mFrameTimelineInfo, s2.mFrameTimelineInfo);
EXPECT_EQ(s1.mDesiredPresentTime, s2.mDesiredPresentTime);
EXPECT_EQ(s1.mIsAutoTimestamp, s2.mIsAutoTimestamp);
EXPECT_EQ(s1.mApplyToken, s2.mApplyToken);
EXPECT_EQ(s1.mMayContainBuffer, s2.mMayContainBuffer);
EXPECT_EQ(s1.mLogCallPoints, s2.mLogCallPoints);
EXPECT_EQ(s1.mDisplayStates.size(), s2.mDisplayStates.size());
EXPECT_THAT(s1.mDisplayStates, ::testing::ContainerEq(s2.mDisplayStates));
EXPECT_EQ(s1.mComposerStates.size(), s2.mComposerStates.size());
EXPECT_EQ(s1.mComposerStates, s2.mComposerStates);
EXPECT_EQ(s1.mInputWindowCommands, s2.mInputWindowCommands);
EXPECT_EQ(s1.mUncacheBuffers, s2.mUncacheBuffers);
EXPECT_EQ(s1.mHasListenerCallbacks, s2.mHasListenerCallbacks);
EXPECT_EQ(s1.mListenerCallbacks.size(), s2.mListenerCallbacks.size());
EXPECT_EQ(s1.mListenerCallbacks, s2.mListenerCallbacks);
}
std::unique_ptr<std::unordered_map<int, sp<BBinder>>> createTokenMap(size_t maxSize) {
auto result = std::make_unique<std::unordered_map<int, sp<BBinder>>>();
for (size_t i = 0; i < maxSize; ++i) {
result->emplace(i, sp<BBinder>::make());
}
return result;
}
constexpr size_t kMaxComposerStates = 2;
ComposerState createComposerStateForTest(size_t i) {
static const auto* const sLayerHandle = createTokenMap(kMaxComposerStates).release();
ComposerState state;
state.state.what = layer_state_t::eFlagsChanged;
state.state.surface = sLayerHandle->at(i);
state.state.layerId = i;
state.state.flags = 20 * i;
return state;
}
constexpr size_t kMaxDisplayStates = 5;
DisplayState createDisplayStateForTest(size_t i) {
static const auto* const sDisplayTokens = createTokenMap(kMaxDisplayStates).release();
DisplayState displayState;
displayState.what = DisplayState::eFlagsChanged;
displayState.token = sDisplayTokens->at(i);
displayState.flags = 20 * i;
return displayState;
}
TransactionState createTransactionStateForTest() {
static sp<BBinder> sApplyToken = sp<BBinder>::make();
TransactionState state;
state.mId = 123;
state.mMergedTransactionIds.push_back(15);
state.mMergedTransactionIds.push_back(0);
state.mFrameTimelineInfo.vsyncId = 14;
state.mDesiredPresentTime = 11;
state.mIsAutoTimestamp = true;
state.mApplyToken = sApplyToken;
for (size_t i = 0; i < kMaxDisplayStates; i++) {
state.mDisplayStates.push_back(createDisplayStateForTest(i));
}
for (size_t i = 0; i < kMaxComposerStates; i++) {
state.mComposerStates.push_back(createComposerStateForTest(i));
}
static const auto* const sFocusRequestTokens = createTokenMap(5).release();
for (int i = 0; i < 5; i++) {
gui::FocusRequest request;
request.token = sFocusRequestTokens->at(i);
request.timestamp = i;
state.mInputWindowCommands.addFocusRequest(request);
}
static const auto* const sCacheToken = createTokenMap(5).release();
for (int i = 0; i < 5; i++) {
client_cache_t cache;
cache.token = sCacheToken->at(i);
cache.id = i;
state.mUncacheBuffers.emplace_back(std::move(cache));
}
static const auto* const sListenerCallbacks = []() {
auto* callbacks = new std::vector<ListenerCallbacks>();
for (int i = 0; i < 5; i++) {
callbacks->emplace_back(sp<BBinder>::make(),
std::unordered_set<CallbackId, CallbackIdHash>{});
}
return callbacks;
}();
state.mHasListenerCallbacks = true;
state.mListenerCallbacks = *sListenerCallbacks;
return state;
}
TransactionState createEmptyTransaction(uint64_t id) {
TransactionState state;
state.mId = id;
return state;
}
TEST(TransactionStateTest, parcel) {
TransactionState state = createTransactionStateForTest();
Parcel p;
state.writeToParcel(&p);
p.setDataPosition(0);
TransactionState parcelledState;
parcelledState.readFromParcel(&p);
EXPECT_EQ(state, parcelledState);
};
TEST(TransactionStateTest, parcelDisplayState) {
DisplayState state = createDisplayStateForTest(0);
Parcel p;
state.write(p);
p.setDataPosition(0);
DisplayState parcelledState;
parcelledState.read(p);
EXPECT_EQ(state, parcelledState);
};
TEST(TransactionStateTest, parcelLayerState) {
ComposerState state = createComposerStateForTest(0);
Parcel p;
state.write(p);
p.setDataPosition(0);
ComposerState parcelledState;
parcelledState.read(p);
EXPECT_EQ(state, parcelledState);
};
TEST(TransactionStateTest, parcelEmptyState) {
TransactionState state;
Parcel p;
state.writeToParcel(&p);
p.setDataPosition(0);
TransactionState parcelledState;
state.readFromParcel(&p);
EXPECT_EQ(state, parcelledState);
};
TEST(TransactionStateTest, mergeLayerState) {
ComposerState composerState = createComposerStateForTest(0);
ComposerState update;
update.state.surface = composerState.state.surface;
update.state.layerId = 0;
update.state.what = layer_state_t::eAlphaChanged;
update.state.color.a = .42;
composerState.state.merge(update.state);
ComposerState expectedMergedState = createComposerStateForTest(0);
expectedMergedState.state.what |= layer_state_t::eAlphaChanged;
expectedMergedState.state.color.a = .42;
EXPECT_EQ(composerState, expectedMergedState);
};
TEST(TransactionStateTest, merge) {
// Setup.
static constexpr uint64_t kUpdateTransactionId = 200;
TransactionState state = createTransactionStateForTest();
TransactionState update;
update.mId = kUpdateTransactionId;
{
ComposerState composerState;
composerState.state.surface = state.mComposerStates[0].state.surface;
composerState.state.what = layer_state_t::eAlphaChanged;
composerState.state.color.a = .42;
update.mComposerStates.push_back(composerState);
}
{
ComposerState composerState;
composerState.state.surface = state.mComposerStates[1].state.surface;
composerState.state.what = layer_state_t::eBufferChanged;
update.mComposerStates.push_back(composerState);
}
int32_t overrwiteLayerId = -1;
// Mutation.
state.merge(std::move(update),
[&overrwiteLayerId](layer_state_t ls) { overrwiteLayerId = ls.layerId; });
// Assertions.
EXPECT_EQ(1, overrwiteLayerId);
EXPECT_EQ(update, createEmptyTransaction(update.getId()));
TransactionState expectedMergedState = createTransactionStateForTest();
expectedMergedState.mMergedTransactionIds
.insert(expectedMergedState.mMergedTransactionIds.begin(), kUpdateTransactionId);
expectedMergedState.mComposerStates.at(0).state.what |= layer_state_t::eAlphaChanged;
expectedMergedState.mComposerStates.at(0).state.color.a = .42;
expectedMergedState.mComposerStates.at(1).state.what |= layer_state_t::eBufferChanged;
auto inputCommands = expectedMergedState.mInputWindowCommands;
// desired present time is not merged.
expectedMergedState.mDesiredPresentTime = state.mDesiredPresentTime;
EXPECT_EQ(state.mComposerStates[0], expectedMergedState.mComposerStates[0]);
EXPECT_EQ(state.mInputWindowCommands, expectedMergedState.mInputWindowCommands);
EXPECT_EQ(state, expectedMergedState);
};
TEST(TransactionStateTest, clear) {
TransactionState state = createTransactionStateForTest();
state.clear();
TransactionState emptyState = createEmptyTransaction(state.getId());
EXPECT_EQ(state, emptyState);
};
} // namespace android