/* * 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 #include #include #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>> createTokenMap(size_t maxSize) { auto result = std::make_unique>>(); for (size_t i = 0; i < maxSize; ++i) { result->emplace(i, sp::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 sApplyToken = sp::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(); for (int i = 0; i < 5; i++) { callbacks->emplace_back(sp::make(), std::unordered_set{}); } 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