| /* | 
 |  * 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 |