| /* |
| * Copyright 2017 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. |
| */ |
| |
| #define LOG_NDEBUG 0 |
| #undef LOG_TAG |
| #define LOG_TAG "FakeHwcUtil" |
| #include <log/log.h> |
| |
| #include "FakeComposerUtils.h" |
| #include "RenderState.h" |
| |
| #include "SurfaceFlinger.h" // Get the name of the service... |
| |
| #include <binder/IServiceManager.h> |
| |
| #include <cutils/properties.h> |
| |
| #include <iomanip> |
| #include <thread> |
| |
| using android::String16; |
| using android::sp; |
| using namespace std::chrono_literals; |
| using namespace sftest; |
| using std::setw; |
| |
| namespace sftest { |
| |
| // clang-format off |
| inline void printSourceRectAligned(::std::ostream& os, const hwc_frect_t& sourceRect, int align) { |
| os << std::fixed << std::setprecision(1) << "(" |
| << setw(align) << sourceRect.left << setw(0) << "," |
| << setw(align) << sourceRect.top << setw(0) << "," |
| << setw(align) << sourceRect.right << setw(0) << "," |
| << setw(align) << sourceRect.bottom << setw(0) << ")"; |
| } |
| |
| inline void printDisplayRectAligned(::std::ostream& os, const hwc_rect_t& displayRect, int align) { |
| os << "(" |
| << setw(align) << displayRect.left << setw(0) << "," |
| << setw(align) << displayRect.top << setw(0) << "," |
| << setw(align) << displayRect.right << setw(0) << "," |
| << setw(align) << displayRect.bottom << setw(0) << ")"; |
| } |
| // clang-format on |
| |
| inline ::std::ostream& operator<<(::std::ostream& os, const sftest::RenderState& state) { |
| printSourceRectAligned(os, state.mSourceCrop, 7); |
| os << "->"; |
| printDisplayRectAligned(os, state.mDisplayFrame, 5); |
| return os << " Swaps:" << state.mSwapCount << " Alpha:" << std::setprecision(3) |
| << state.mPlaneAlpha << " Xform:" << state.mTransform; |
| } |
| |
| // Helper for verifying the parts of the RenderState |
| template <typename T> |
| bool valuesMatch(::testing::AssertionResult& message, const T& ref, const T& val, |
| const char* name) { |
| if (ref != val) { |
| message = message << "Expected " << name << ":" << ref << ", got:" << val << "."; |
| return false; |
| } |
| return true; |
| } |
| |
| ::testing::AssertionResult rectsAreSame(const RenderState& ref, const RenderState& val) { |
| // TODO: Message could start as success and be assigned as failure. |
| // Only problem is that utility assumes it to be failure and just adds stuff. Would |
| // need still special case the initial failure in the utility? |
| // TODO: ... or would it be possible to break this back to gtest primitives? |
| ::testing::AssertionResult message = ::testing::AssertionFailure(); |
| bool passes = true; |
| |
| // The work here is mostly about providing good log strings for differences |
| passes &= valuesMatch(message, ref.mDisplayFrame, val.mDisplayFrame, "display frame"); |
| passes &= valuesMatch(message, ref.mPlaneAlpha, val.mPlaneAlpha, "alpha"); |
| passes &= valuesMatch(message, ref.mSwapCount, val.mSwapCount, "swap count"); |
| passes &= valuesMatch(message, ref.mSourceCrop, val.mSourceCrop, "source crop"); |
| // ... add more |
| if (passes) { |
| return ::testing::AssertionSuccess(); |
| } |
| return message; |
| } |
| |
| ::testing::AssertionResult framesAreSame(const std::vector<RenderState>& ref, |
| const std::vector<RenderState>& val) { |
| ::testing::AssertionResult message = ::testing::AssertionFailure(); |
| bool passed = true; |
| if (ref.size() != val.size()) { |
| message << "Expected " << ref.size() << " rects, got " << val.size() << "."; |
| passed = false; |
| } |
| for (size_t rectIndex = 0; rectIndex < std::min(ref.size(), val.size()); rectIndex++) { |
| ::testing::AssertionResult rectResult = rectsAreSame(ref[rectIndex], val[rectIndex]); |
| if (rectResult == false) { |
| message << "First different rect at " << rectIndex << ": " << rectResult.message(); |
| passed = false; |
| break; |
| } |
| } |
| |
| if (passed) { |
| return ::testing::AssertionSuccess(); |
| } else { |
| message << "\nReference:"; |
| for (auto state = ref.begin(); state != ref.end(); ++state) { |
| message << "\n" << *state; |
| } |
| message << "\nActual:"; |
| for (auto state = val.begin(); state != val.end(); ++state) { |
| message << "\n" << *state; |
| } |
| } |
| return message; |
| } |
| |
| void startSurfaceFlinger() { |
| ALOGI("Start SurfaceFlinger"); |
| system("start surfaceflinger"); |
| |
| sp<android::IServiceManager> sm(android::defaultServiceManager()); |
| sp<android::IBinder> sf; |
| while (sf == nullptr) { |
| std::this_thread::sleep_for(10ms); |
| sf = sm->checkService(String16(android::SurfaceFlinger::getServiceName())); |
| } |
| ALOGV("SurfaceFlinger running"); |
| } |
| |
| void stopSurfaceFlinger() { |
| ALOGI("Stop SurfaceFlinger"); |
| system("stop surfaceflinger"); |
| sp<android::IServiceManager> sm(android::defaultServiceManager()); |
| sp<android::IBinder> sf; |
| while (sf != nullptr) { |
| std::this_thread::sleep_for(10ms); |
| sf = sm->checkService(String16(android::SurfaceFlinger::getServiceName())); |
| } |
| ALOGV("SurfaceFlinger stopped"); |
| } |
| |
| //////////////////////////////////////////////// |
| |
| void FakeHwcEnvironment::SetUp() { |
| ALOGI("Test env setup"); |
| system("setenforce 0"); |
| system("stop"); |
| property_set("debug.sf.nobootanimation", "1"); |
| { |
| char value[PROPERTY_VALUE_MAX]; |
| property_get("debug.sf.nobootanimation", value, "0"); |
| LOG_FATAL_IF(atoi(value) != 1, "boot skip not set"); |
| } |
| // TODO: Try registering the mock as the default service instead. |
| property_set("debug.sf.hwc_service_name", "mock"); |
| // This allows the SurfaceFlinger to load a HIDL service not listed in manifest files. |
| property_set("debug.sf.treble_testing_override", "true"); |
| } |
| |
| void FakeHwcEnvironment::TearDown() { |
| ALOGI("Test env tear down"); |
| system("stop"); |
| // Wait for mock call signaling teardown? |
| property_set("debug.sf.nobootanimation", "0"); |
| property_set("debug.sf.hwc_service_name", "default"); |
| ALOGI("Test env tear down - done"); |
| } |
| |
| } // namespace sftest |