TestUtils.h revision 9cd1bbe5c9e14472e631d8cc10005613925f34af
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 const SkMatrix& transform); 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(), nullptr); 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(), nullptr); 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 sp<RenderNode>& getSyncedNode(sp<RenderNode>& node) { 175 syncHierarchyPropertiesAndDisplayList(node); 176 return node; 177 } 178 179 typedef std::function<void(renderthread::RenderThread& thread)> RtCallback; 180 181 class TestTask : public renderthread::RenderTask { 182 public: 183 TestTask(RtCallback rtCallback) 184 : rtCallback(rtCallback) {} 185 virtual ~TestTask() {} 186 virtual void run() override; 187 RtCallback rtCallback; 188 }; 189 190 /** 191 * NOTE: requires surfaceflinger to run, otherwise this method will wait indefinitely. 192 */ 193 static void runOnRenderThread(RtCallback rtCallback) { 194 TestTask task(rtCallback); 195 renderthread::RenderThread::getInstance().queueAndWait(&task); 196 } 197 198 static bool isRenderThreadRunning() { 199 return renderthread::RenderThread::hasInstance(); 200 } 201 202 static SkColor interpolateColor(float fraction, SkColor start, SkColor end); 203 204 static void layoutTextUnscaled(const SkPaint& paint, const char* text, 205 std::vector<glyph_t>* outGlyphs, std::vector<float>* outPositions, 206 float* outTotalAdvance, Rect* outBounds); 207 208 static void drawUtf8ToCanvas(Canvas* canvas, const char* text, 209 const SkPaint& paint, float x, float y); 210 211 static void drawUtf8ToCanvas(Canvas* canvas, const char* text, 212 const SkPaint& paint, const SkPath& path); 213 214 static std::unique_ptr<uint16_t[]> asciiToUtf16(const char* str); 215 216private: 217 static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) { 218 node->syncProperties(); 219 node->syncDisplayList(nullptr); 220 auto displayList = node->getDisplayList(); 221 if (displayList) { 222 for (auto&& childOp : displayList->getChildren()) { 223 syncHierarchyPropertiesAndDisplayListImpl(childOp->renderNode); 224 } 225 } 226 } 227 228}; // class TestUtils 229 230} /* namespace uirenderer */ 231} /* namespace android */ 232 233#endif /* TEST_UTILS_H */ 234