Home | History | Annotate | Download | only in common
      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:
     86     ScopedProperty(T& property, T newValue)
     87         : mPropertyPtr(&property)
     88         , mOldValue(property) {
     89         property = newValue;
     90     }
     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:
    103         SignalingDtor()
    104                 : mSignal(nullptr) {}
    105         SignalingDtor(int* signal)
    106                 : mSignal(signal) {}
    107         void setSignal(int* signal) {
    108             mSignal = signal;
    109         }
    110         ~SignalingDtor() {
    111             if (mSignal) {
    112                 (*mSignal)++;
    113             }
    114         }
    115     private:
    116         int* mSignal;
    117     };
    118 
    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 
    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>
    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 
    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 
    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      */
    192     static void syncHierarchyPropertiesAndDisplayList(sp<RenderNode>& node) {
    193         syncHierarchyPropertiesAndDisplayListImpl(node.get());
    194     }
    195 
    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:
    205         TestTask(RtCallback rtCallback)
    206                 : rtCallback(rtCallback) {}
    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      */
    215     static void runOnRenderThread(RtCallback rtCallback) {
    216         TestTask task(rtCallback);
    217         renderthread::RenderThread::getInstance().queueAndWait(&task);
    218     }
    219 
    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:
    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