TestUtils.h revision dccca44ffda4836b56a21da95a046c9708ffd49c
1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16#ifndef TEST_UTILS_H 17#define TEST_UTILS_H 18 19#include <DeviceInfo.h> 20#include <DisplayList.h> 21#include <Matrix.h> 22#include <Rect.h> 23#include <RenderNode.h> 24#include <renderstate/RenderState.h> 25#include <renderthread/RenderThread.h> 26#include <Snapshot.h> 27 28#if HWUI_NEW_OPS 29#include <RecordedOp.h> 30#include <RecordingCanvas.h> 31#else 32#include <DisplayListOp.h> 33#include <DisplayListCanvas.h> 34#endif 35 36#include <memory> 37 38namespace android { 39namespace uirenderer { 40 41#if HWUI_NEW_OPS 42typedef RecordingCanvas TestCanvas; 43#else 44typedef DisplayListCanvas TestCanvas; 45#endif 46 47#define EXPECT_MATRIX_APPROX_EQ(a, b) \ 48 EXPECT_TRUE(TestUtils::matricesAreApproxEqual(a, b)) 49 50#define EXPECT_RECT_APPROX_EQ(a, b) \ 51 EXPECT_TRUE(MathUtils::areEqual(a.left, b.left) \ 52 && MathUtils::areEqual(a.top, b.top) \ 53 && MathUtils::areEqual(a.right, b.right) \ 54 && MathUtils::areEqual(a.bottom, b.bottom)); 55 56#define EXPECT_CLIP_RECT(expRect, clipStatePtr) \ 57 EXPECT_NE(nullptr, (clipStatePtr)) << "Op is unclipped"; \ 58 if ((clipStatePtr)->mode == ClipMode::Rectangle) { \ 59 EXPECT_EQ((expRect), reinterpret_cast<const ClipRect*>(clipStatePtr)->rect); \ 60 } else { \ 61 ADD_FAILURE() << "ClipState not a rect"; \ 62 } 63/** 64 * Like gtest's TEST, but runs on the RenderThread, and 'renderThread' is passed, in top level scope 65 * (for e.g. accessing its RenderState) 66 */ 67#define RENDERTHREAD_TEST(test_case_name, test_name) \ 68 class test_case_name##_##test_name##_RenderThreadTest { \ 69 public: \ 70 static void doTheThing(renderthread::RenderThread& renderThread); \ 71 }; \ 72 TEST(test_case_name, test_name) { \ 73 TestUtils::runOnRenderThread(test_case_name##_##test_name##_RenderThreadTest::doTheThing); \ 74 }; \ 75 void test_case_name##_##test_name##_RenderThreadTest::doTheThing(renderthread::RenderThread& renderThread) 76 77class TestUtils { 78public: 79 class SignalingDtor { 80 public: 81 SignalingDtor() 82 : mSignal(nullptr) {} 83 SignalingDtor(int* signal) 84 : mSignal(signal) {} 85 void setSignal(int* signal) { 86 mSignal = signal; 87 } 88 ~SignalingDtor() { 89 if (mSignal) { 90 (*mSignal)++; 91 } 92 } 93 private: 94 int* mSignal; 95 }; 96 97 static bool matricesAreApproxEqual(const Matrix4& a, const Matrix4& b) { 98 for (int i = 0; i < 16; i++) { 99 if (!MathUtils::areEqual(a[i], b[i])) { 100 return false; 101 } 102 } 103 return true; 104 } 105 106 static std::unique_ptr<Snapshot> makeSnapshot(const Matrix4& transform, const Rect& clip) { 107 std::unique_ptr<Snapshot> snapshot(new Snapshot()); 108 snapshot->clip(clip, SkRegion::kReplace_Op); // store clip first, so it isn't transformed 109 *(snapshot->transform) = transform; 110 return snapshot; 111 } 112 113 static SkBitmap createSkBitmap(int width, int height, 114 SkColorType colorType = kN32_SkColorType) { 115 SkBitmap bitmap; 116 SkImageInfo info = SkImageInfo::Make(width, height, 117 colorType, kPremul_SkAlphaType); 118 bitmap.setInfo(info); 119 bitmap.allocPixels(info); 120 return bitmap; 121 } 122 123 static sp<DeferredLayerUpdater> createTextureLayerUpdater( 124 renderthread::RenderThread& renderThread, uint32_t width, uint32_t height, 125 std::function<void(Matrix4*)> transformSetupCallback); 126 127 template<class CanvasType> 128 static std::unique_ptr<DisplayList> createDisplayList(int width, int height, 129 std::function<void(CanvasType& canvas)> canvasCallback) { 130 CanvasType canvas(width, height); 131 canvasCallback(canvas); 132 return std::unique_ptr<DisplayList>(canvas.finishRecording()); 133 } 134 135 static sp<RenderNode> createNode(int left, int top, int right, int bottom, 136 std::function<void(RenderProperties& props, TestCanvas& canvas)> setup) { 137#if HWUI_NULL_GPU 138 // if RenderNodes are being sync'd/used, device info will be needed, since 139 // DeviceInfo::maxTextureSize() affects layer property 140 DeviceInfo::initialize(); 141#endif 142 143 sp<RenderNode> node = new RenderNode(); 144 RenderProperties& props = node->mutateStagingProperties(); 145 props.setLeftTopRightBottom(left, top, right, bottom); 146 if (setup) { 147 TestCanvas canvas(props.getWidth(), props.getHeight()); 148 setup(props, canvas); 149 node->setStagingDisplayList(canvas.finishRecording()); 150 } 151 node->setPropertyFieldsDirty(0xFFFFFFFF); 152 return node; 153 } 154 155 static void recordNode(RenderNode& node, 156 std::function<void(TestCanvas&)> contentCallback) { 157 TestCanvas canvas(node.stagingProperties().getWidth(), 158 node.stagingProperties().getHeight()); 159 contentCallback(canvas); 160 node.setStagingDisplayList(canvas.finishRecording()); 161 } 162 163 /** 164 * Forces a sync of a tree of RenderNode, such that every descendant will have its staging 165 * properties and DisplayList moved to the render copies. 166 * 167 * Note: does not check dirtiness bits, so any non-staging DisplayLists will be discarded. 168 * For this reason, this should generally only be called once on a tree. 169 */ 170 static void syncHierarchyPropertiesAndDisplayList(sp<RenderNode>& node) { 171 syncHierarchyPropertiesAndDisplayListImpl(node.get()); 172 } 173 174 static std::vector<sp<RenderNode>> createSyncedNodeList(sp<RenderNode>& node) { 175 TestUtils::syncHierarchyPropertiesAndDisplayList(node); 176 std::vector<sp<RenderNode>> vec; 177 vec.emplace_back(node); 178 return vec; 179 } 180 181 typedef std::function<void(renderthread::RenderThread& thread)> RtCallback; 182 183 class TestTask : public renderthread::RenderTask { 184 public: 185 TestTask(RtCallback rtCallback) 186 : rtCallback(rtCallback) {} 187 virtual ~TestTask() {} 188 virtual void run() override; 189 RtCallback rtCallback; 190 }; 191 192 /** 193 * NOTE: requires surfaceflinger to run, otherwise this method will wait indefinitely. 194 */ 195 static void runOnRenderThread(RtCallback rtCallback) { 196 TestTask task(rtCallback); 197 renderthread::RenderThread::getInstance().queueAndWait(&task); 198 } 199 200 static bool isRenderThreadRunning() { 201 return renderthread::RenderThread::hasInstance(); 202 } 203 204 static SkColor interpolateColor(float fraction, SkColor start, SkColor end); 205 206 static void layoutTextUnscaled(const SkPaint& paint, const char* text, 207 std::vector<glyph_t>* outGlyphs, std::vector<float>* outPositions, 208 float* outTotalAdvance, Rect* outBounds); 209 210 static void drawUtf8ToCanvas(TestCanvas* canvas, const char* text, 211 const SkPaint& paint, float x, float y); 212 213 static void drawUtf8ToCanvas(TestCanvas* canvas, const char* text, 214 const SkPaint& paint, const SkPath& path); 215 216 static std::unique_ptr<uint16_t[]> utf8ToUtf16(const char* str); 217 218private: 219 static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) { 220 node->syncProperties(); 221 node->syncDisplayList(); 222 auto displayList = node->getDisplayList(); 223 if (displayList) { 224 for (auto&& childOp : displayList->getChildren()) { 225 syncHierarchyPropertiesAndDisplayListImpl(childOp->renderNode); 226 } 227 } 228 } 229 230}; // class TestUtils 231 232} /* namespace uirenderer */ 233} /* namespace android */ 234 235#endif /* TEST_UTILS_H */ 236