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 
38 namespace android {
39 namespace uirenderer {
40 
41 #if HWUI_NEW_OPS
42 typedef RecordingCanvas TestCanvas;
43 #else
44 typedef 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 
77 /**
78  * Sets a property value temporarily, generally for the duration of a test, restoring the previous
79  * value when going out of scope.
80  *
81  * Can be used e.g. to test behavior only active while Properties::debugOverdraw is enabled.
82  */
83 template <typename T>
84 class ScopedProperty {
85 public:
ScopedProperty(T & property,T newValue)86     ScopedProperty(T& property, T newValue)
87         : mPropertyPtr(&property)
88         , mOldValue(property) {
89         property = newValue;
90     }
~ScopedProperty()91     ~ScopedProperty() {
92         *mPropertyPtr = mOldValue;
93     }
94 private:
95     T* mPropertyPtr;
96     T mOldValue;
97 };
98 
99 class TestUtils {
100 public:
101     class SignalingDtor {
102     public:
SignalingDtor()103         SignalingDtor()
104                 : mSignal(nullptr) {}
SignalingDtor(int * signal)105         SignalingDtor(int* signal)
106                 : mSignal(signal) {}
setSignal(int * signal)107         void setSignal(int* signal) {
108             mSignal = signal;
109         }
~SignalingDtor()110         ~SignalingDtor() {
111             if (mSignal) {
112                 (*mSignal)++;
113             }
114         }
115     private:
116         int* mSignal;
117     };
118 
matricesAreApproxEqual(const Matrix4 & a,const Matrix4 & b)119     static bool matricesAreApproxEqual(const Matrix4& a, const Matrix4& b) {
120         for (int i = 0; i < 16; i++) {
121             if (!MathUtils::areEqual(a[i], b[i])) {
122                 return false;
123             }
124         }
125         return true;
126     }
127 
makeSnapshot(const Matrix4 & transform,const Rect & clip)128     static std::unique_ptr<Snapshot> makeSnapshot(const Matrix4& transform, const Rect& clip) {
129         std::unique_ptr<Snapshot> snapshot(new Snapshot());
130         snapshot->clip(clip, SkRegion::kReplace_Op); // store clip first, so it isn't transformed
131         *(snapshot->transform) = transform;
132         return snapshot;
133     }
134 
135     static SkBitmap createSkBitmap(int width, int height,
136             SkColorType colorType = kN32_SkColorType) {
137         SkBitmap bitmap;
138         SkImageInfo info = SkImageInfo::Make(width, height,
139                 colorType, kPremul_SkAlphaType);
140         bitmap.setInfo(info);
141         bitmap.allocPixels(info);
142         return bitmap;
143     }
144 
145     static sp<DeferredLayerUpdater> createTextureLayerUpdater(
146             renderthread::RenderThread& renderThread, uint32_t width, uint32_t height,
147             const SkMatrix& transform);
148 
149     template<class CanvasType>
createDisplayList(int width,int height,std::function<void (CanvasType & canvas)> canvasCallback)150     static std::unique_ptr<DisplayList> createDisplayList(int width, int height,
151             std::function<void(CanvasType& canvas)> canvasCallback) {
152         CanvasType canvas(width, height);
153         canvasCallback(canvas);
154         return std::unique_ptr<DisplayList>(canvas.finishRecording());
155     }
156 
createNode(int left,int top,int right,int bottom,std::function<void (RenderProperties & props,TestCanvas & canvas)> setup)157     static sp<RenderNode> createNode(int left, int top, int right, int bottom,
158             std::function<void(RenderProperties& props, TestCanvas& canvas)> setup) {
159 #if HWUI_NULL_GPU
160         // if RenderNodes are being sync'd/used, device info will be needed, since
161         // DeviceInfo::maxTextureSize() affects layer property
162         DeviceInfo::initialize();
163 #endif
164 
165         sp<RenderNode> node = new RenderNode();
166         RenderProperties& props = node->mutateStagingProperties();
167         props.setLeftTopRightBottom(left, top, right, bottom);
168         if (setup) {
169             TestCanvas canvas(props.getWidth(), props.getHeight());
170             setup(props, canvas);
171             node->setStagingDisplayList(canvas.finishRecording(), nullptr);
172         }
173         node->setPropertyFieldsDirty(0xFFFFFFFF);
174         return node;
175     }
176 
recordNode(RenderNode & node,std::function<void (TestCanvas &)> contentCallback)177     static void recordNode(RenderNode& node,
178             std::function<void(TestCanvas&)> contentCallback) {
179        TestCanvas canvas(node.stagingProperties().getWidth(),
180                node.stagingProperties().getHeight());
181        contentCallback(canvas);
182        node.setStagingDisplayList(canvas.finishRecording(), nullptr);
183     }
184 
185     /**
186      * Forces a sync of a tree of RenderNode, such that every descendant will have its staging
187      * properties and DisplayList moved to the render copies.
188      *
189      * Note: does not check dirtiness bits, so any non-staging DisplayLists will be discarded.
190      * For this reason, this should generally only be called once on a tree.
191      */
syncHierarchyPropertiesAndDisplayList(sp<RenderNode> & node)192     static void syncHierarchyPropertiesAndDisplayList(sp<RenderNode>& node) {
193         syncHierarchyPropertiesAndDisplayListImpl(node.get());
194     }
195 
getSyncedNode(sp<RenderNode> & node)196     static sp<RenderNode>& getSyncedNode(sp<RenderNode>& node) {
197         syncHierarchyPropertiesAndDisplayList(node);
198         return node;
199     }
200 
201     typedef std::function<void(renderthread::RenderThread& thread)> RtCallback;
202 
203     class TestTask : public renderthread::RenderTask {
204     public:
TestTask(RtCallback rtCallback)205         TestTask(RtCallback rtCallback)
206                 : rtCallback(rtCallback) {}
~TestTask()207         virtual ~TestTask() {}
208         virtual void run() override;
209         RtCallback rtCallback;
210     };
211 
212     /**
213      * NOTE: requires surfaceflinger to run, otherwise this method will wait indefinitely.
214      */
runOnRenderThread(RtCallback rtCallback)215     static void runOnRenderThread(RtCallback rtCallback) {
216         TestTask task(rtCallback);
217         renderthread::RenderThread::getInstance().queueAndWait(&task);
218     }
219 
isRenderThreadRunning()220     static bool isRenderThreadRunning() {
221         return renderthread::RenderThread::hasInstance();
222     }
223 
224     static SkColor interpolateColor(float fraction, SkColor start, SkColor end);
225 
226     static void layoutTextUnscaled(const SkPaint& paint, const char* text,
227             std::vector<glyph_t>* outGlyphs, std::vector<float>* outPositions,
228             float* outTotalAdvance, Rect* outBounds);
229 
230     static void drawUtf8ToCanvas(Canvas* canvas, const char* text,
231             const SkPaint& paint, float x, float y);
232 
233     static void drawUtf8ToCanvas(Canvas* canvas, const char* text,
234             const SkPaint& paint, const SkPath& path);
235 
236     static std::unique_ptr<uint16_t[]> asciiToUtf16(const char* str);
237 
238 private:
syncHierarchyPropertiesAndDisplayListImpl(RenderNode * node)239     static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) {
240         node->syncProperties();
241         node->syncDisplayList(nullptr);
242         auto displayList = node->getDisplayList();
243         if (displayList) {
244             for (auto&& childOp : displayList->getChildren()) {
245                 syncHierarchyPropertiesAndDisplayListImpl(childOp->renderNode);
246             }
247         }
248     }
249 
250 }; // class TestUtils
251 
252 } /* namespace uirenderer */
253 } /* namespace android */
254 
255 #endif /* TEST_UTILS_H */
256