| Index: tests/GrTRecorderTest.cpp
 | 
| diff --git a/tests/GrTRecorderTest.cpp b/tests/GrTRecorderTest.cpp
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..a5aedf687c1ef780a074f3f3290a236c84f6330d
 | 
| --- /dev/null
 | 
| +++ b/tests/GrTRecorderTest.cpp
 | 
| @@ -0,0 +1,245 @@
 | 
| +/*
 | 
| + * Copyright 2014 Google Inc.
 | 
| + *
 | 
| + * Use of this source code is governed by a BSD-style license that can be
 | 
| + * found in the LICENSE file.
 | 
| + */
 | 
| +
 | 
| +#if SK_SUPPORT_GPU
 | 
| +
 | 
| +#include "SkMatrix.h"
 | 
| +#include "SkString.h"
 | 
| +#include "GrTRecorder.h"
 | 
| +#include "Test.h"
 | 
| +
 | 
| +////////////////////////////////////////////////////////////////////////////////
 | 
| +
 | 
| +static int activeRecorderItems = 0;
 | 
| +
 | 
| +class IntWrapper {
 | 
| +public:
 | 
| +    IntWrapper() {}
 | 
| +    IntWrapper(int value) : fValue(value) {}
 | 
| +    operator int() { return fValue; }
 | 
| +private:
 | 
| +    int fValue;
 | 
| +};
 | 
| +
 | 
| +static void test_empty_back(skiatest::Reporter* reporter) {
 | 
| +    GrTRecorder<IntWrapper, int> recorder(0);
 | 
| +
 | 
| +    REPORTER_ASSERT(reporter, recorder.empty());
 | 
| +
 | 
| +    for (int i = 0; i < 100; ++i) {
 | 
| +        REPORTER_ASSERT(reporter, i == *GrNEW_APPEND_TO_RECORDER(recorder, IntWrapper, (i)));
 | 
| +        REPORTER_ASSERT(reporter, !recorder.empty());
 | 
| +        REPORTER_ASSERT(reporter, i == recorder.back());
 | 
| +    }
 | 
| +
 | 
| +    REPORTER_ASSERT(reporter, !recorder.empty());
 | 
| +
 | 
| +    recorder.reset();
 | 
| +
 | 
| +    REPORTER_ASSERT(reporter, recorder.empty());
 | 
| +}
 | 
| +
 | 
| +struct ExtraData {
 | 
| +    typedef GrTRecorder<ExtraData, int> Recorder;
 | 
| +
 | 
| +    ExtraData(int i) : fData(i) {
 | 
| +        int* extraData = this->extraData();
 | 
| +        for (int j = 0; j < i; j++) {
 | 
| +            extraData[j] = i;
 | 
| +        }
 | 
| +        ++activeRecorderItems;
 | 
| +    }
 | 
| +    ~ExtraData() {
 | 
| +        --activeRecorderItems;
 | 
| +    }
 | 
| +    int* extraData() {
 | 
| +        return reinterpret_cast<int*>(Recorder::GetDataForItem(this));
 | 
| +    }
 | 
| +    int fData;
 | 
| +};
 | 
| +
 | 
| +static void test_extra_data(skiatest::Reporter* reporter) {
 | 
| +    ExtraData::Recorder recorder(0);
 | 
| +    for (int i = 0; i < 100; ++i) {
 | 
| +        GrNEW_APPEND_WITH_DATA_TO_RECORDER(recorder, ExtraData, (i), i * sizeof(int));
 | 
| +    }
 | 
| +    REPORTER_ASSERT(reporter, 100 == activeRecorderItems);
 | 
| +
 | 
| +    ExtraData::Recorder::Iter iter(recorder);
 | 
| +    for (int i = 0; i < 100; ++i) {
 | 
| +        REPORTER_ASSERT(reporter, iter.next());
 | 
| +        REPORTER_ASSERT(reporter, i == iter->fData);
 | 
| +        for (int j = 0; j < i; j++) {
 | 
| +            REPORTER_ASSERT(reporter, i == iter->extraData()[j]);
 | 
| +        }
 | 
| +    }
 | 
| +    REPORTER_ASSERT(reporter, !iter.next());
 | 
| +
 | 
| +    recorder.reset();
 | 
| +    REPORTER_ASSERT(reporter, 0 == activeRecorderItems);
 | 
| +}
 | 
| +
 | 
| +enum ClassType {
 | 
| +    kBase_ClassType,
 | 
| +    kSubclass_ClassType,
 | 
| +    kSubSubclass_ClassType,
 | 
| +    kSubclassExtraData_ClassType,
 | 
| +    kSubclassEmpty_ClassType,
 | 
| +
 | 
| +    kNumClassTypes
 | 
| +};
 | 
| +
 | 
| +class Base {
 | 
| +public:
 | 
| +    typedef GrTRecorder<Base, void*> Recorder;
 | 
| +
 | 
| +    Base() {
 | 
| +        fMatrix.reset();
 | 
| +        ++activeRecorderItems;
 | 
| +    }
 | 
| +
 | 
| +    virtual ~Base() { --activeRecorderItems; }
 | 
| +
 | 
| +    virtual ClassType getType() { return kBase_ClassType; }
 | 
| +
 | 
| +    virtual void validate(skiatest::Reporter* reporter) const {
 | 
| +        REPORTER_ASSERT(reporter, fMatrix.isIdentity());
 | 
| +    }
 | 
| +
 | 
| +private:
 | 
| +    SkMatrix fMatrix;
 | 
| +};
 | 
| +
 | 
| +class Subclass : public Base {
 | 
| +public:
 | 
| +    Subclass() : fString("Lorem ipsum dolor sit amet") {}
 | 
| +
 | 
| +    virtual ClassType getType() { return kSubclass_ClassType; }
 | 
| +
 | 
| +    virtual void validate(skiatest::Reporter* reporter) const {
 | 
| +        Base::validate(reporter);
 | 
| +        REPORTER_ASSERT(reporter, !strcmp("Lorem ipsum dolor sit amet", fString.c_str()));
 | 
| +    }
 | 
| +
 | 
| +private:
 | 
| +    SkString fString;
 | 
| +};
 | 
| +
 | 
| +class SubSubclass : public Subclass {
 | 
| +public:
 | 
| +    SubSubclass() : fInt(1234), fFloat(1.234f) {}
 | 
| +
 | 
| +    virtual ClassType getType() { return kSubSubclass_ClassType; }
 | 
| +
 | 
| +    virtual void validate(skiatest::Reporter* reporter) const {
 | 
| +        Subclass::validate(reporter);
 | 
| +        REPORTER_ASSERT(reporter, 1234 == fInt);
 | 
| +        REPORTER_ASSERT(reporter, 1.234f == fFloat);
 | 
| +    }
 | 
| +
 | 
| +private:
 | 
| +    int fInt;
 | 
| +    float fFloat;
 | 
| +};
 | 
| +
 | 
| +class SubclassExtraData : public Base {
 | 
| +public:
 | 
| +    SubclassExtraData(int length) : fLength(length) {
 | 
| +        int* data = reinterpret_cast<int*>(Recorder::GetDataForItem(this));
 | 
| +        for (int i = 0; i < fLength; ++i) {
 | 
| +            data[i] = ValueAt(i);
 | 
| +        }
 | 
| +    }
 | 
| +
 | 
| +    virtual ClassType getType() { return kSubclassExtraData_ClassType; }
 | 
| +
 | 
| +    virtual void validate(skiatest::Reporter* reporter) const {
 | 
| +        Base::validate(reporter);
 | 
| +        const int* data = reinterpret_cast<const int*>(Recorder::GetDataForItem(this));
 | 
| +        for (int i = 0; i < fLength; ++i) {
 | 
| +            REPORTER_ASSERT(reporter, ValueAt(i) == data[i]);
 | 
| +        }
 | 
| +    }
 | 
| +
 | 
| +private:
 | 
| +    static int ValueAt(uint64_t i) { return static_cast<int>(123456789 + 987654321 * i); }
 | 
| +    int fLength;
 | 
| +};
 | 
| +
 | 
| +class SubclassEmpty : public Base {
 | 
| +public:
 | 
| +    virtual ClassType getType() { return kSubclassEmpty_ClassType; }
 | 
| +};
 | 
| +
 | 
| +static void test_subclasses(skiatest::Reporter* reporter) {
 | 
| +    class Order {
 | 
| +    public:
 | 
| +        Order() { this->reset(); }
 | 
| +        void reset() { fCurrent = 0; }
 | 
| +        ClassType next() {
 | 
| +            fCurrent = 1664525 * fCurrent + 1013904223;
 | 
| +            return static_cast<ClassType>(fCurrent % kNumClassTypes);
 | 
| +        }
 | 
| +    private:
 | 
| +        uint32_t fCurrent;
 | 
| +    };
 | 
| +
 | 
| +    Base::Recorder recorder(1024);
 | 
| +
 | 
| +    Order order;
 | 
| +    for (int i = 0; i < 1000; i++) {
 | 
| +        switch (order.next()) {
 | 
| +            case kBase_ClassType:
 | 
| +                GrNEW_APPEND_TO_RECORDER(recorder, Base, ());
 | 
| +                break;
 | 
| +
 | 
| +            case kSubclass_ClassType:
 | 
| +                GrNEW_APPEND_TO_RECORDER(recorder, Subclass, ());
 | 
| +                break;
 | 
| +
 | 
| +            case kSubSubclass_ClassType:
 | 
| +                GrNEW_APPEND_TO_RECORDER(recorder, SubSubclass, ());
 | 
| +                break;
 | 
| +
 | 
| +            case kSubclassExtraData_ClassType:
 | 
| +                GrNEW_APPEND_WITH_DATA_TO_RECORDER(recorder, SubclassExtraData, (i), sizeof(int) * i);
 | 
| +                break;
 | 
| +
 | 
| +            case kSubclassEmpty_ClassType:
 | 
| +                GrNEW_APPEND_TO_RECORDER(recorder, SubclassEmpty, ());
 | 
| +                break;
 | 
| +
 | 
| +            default:
 | 
| +                reporter->reportFailed(SkString("Invalid class type"));
 | 
| +                break;
 | 
| +        }
 | 
| +    }
 | 
| +    REPORTER_ASSERT(reporter, 1000 == activeRecorderItems);
 | 
| +
 | 
| +    order.reset();
 | 
| +    Base::Recorder::Iter iter(recorder);
 | 
| +    for (int i = 0; i < 1000; ++i) {
 | 
| +        REPORTER_ASSERT(reporter, iter.next());
 | 
| +        REPORTER_ASSERT(reporter, order.next() == iter->getType());
 | 
| +        iter->validate(reporter);
 | 
| +    }
 | 
| +    REPORTER_ASSERT(reporter, !iter.next());
 | 
| +
 | 
| +    // Don't reset the recorder. It should automatically destruct all its items.
 | 
| +}
 | 
| +
 | 
| +DEF_GPUTEST(GrTRecorder, reporter, factory) {
 | 
| +    test_empty_back(reporter);
 | 
| +
 | 
| +    test_extra_data(reporter);
 | 
| +    REPORTER_ASSERT(reporter, 0 == activeRecorderItems); // test_extra_data should call reset().
 | 
| +
 | 
| +    test_subclasses(reporter);
 | 
| +    REPORTER_ASSERT(reporter, 0 == activeRecorderItems); // Ensure ~GrTRecorder invokes dtors.
 | 
| +}
 | 
| +
 | 
| +#endif
 | 
| 
 |