<commit_before>#include "xchainer/testing/routines.h"

#include <functional>
#include <iostream>
#include <sstream>
#include <vector>

#include <gsl/gsl>

#include "xchainer/array.h"
#include "xchainer/context.h"
#include "xchainer/numeric.h"
#include "xchainer/testing/threading.h"

namespace xchainer {
namespace testing {
namespace {

void CheckArraysEqual(const std::vector<Array>& expected, const std::vector<Array>& actual, double atol, double rtol) {
    // Number of outputs
    if (expected.size() != actual.size()) {
        throw RoutinesCheckError{"Number of arrays does not match."};
    }

    // Output array properties
    for (size_t i = 0; i < expected.size(); ++i) {
        const Array& e = expected[i];
        const Array& a = actual[i];
        if (e.shape() != a.shape()) {
            throw RoutinesCheckError{"Shape of output array ",
                                     i,
                                     " of ",
                                     expected.size(),
                                     " is incorrect. Actual ",
                                     a.shape(),
                                     " != Expected ",
                                     e.shape(),
                                     "."};
        }
        if (e.dtype() != a.dtype()) {
            throw RoutinesCheckError{"Dtype of output array ",
                                     i,
                                     " of ",
                                     expected.size(),
                                     " is incorrect. Actual ",
                                     GetDtypeName(a.dtype()),
                                     " != Expected ",
                                     GetDtypeName(e.dtype()),
                                     "."};
        }
        if (&e.device() != &a.device()) {
            throw RoutinesCheckError{"Device of output array ",
                                     i,
                                     " of ",
                                     expected.size(),
                                     " is incorrect. Actual ",
                                     a.device().name(),
                                     " != Expected ",
                                     e.device().name(),
                                     "."};
        }
    }

    // Numerical check
    std::vector<size_t> failed_input_indices;
    for (size_t i = 0; i < expected.size(); ++i) {
        const Array& e = expected[i];
        const Array& a = actual[i];
        if (!AllClose(e, a, atol, rtol)) {
            failed_input_indices.emplace_back(i);
        }
    }
    if (!failed_input_indices.empty()) {
        std::ostringstream os;
        os << "Numerical error in forward outputs (out of " << expected.size() << "): ";
        for (size_t i : failed_input_indices) {
            if (i != 0) {
                os << ", ";
            }
            os << i;
        }
        os << std::endl;
        os << "Atol: " << atol << "  Rtol: " << rtol << std::endl;
        for (size_t i : failed_input_indices) {
            const Array& e = expected[i];
            const Array& a = actual[i];
            os << "Error[" << i << "]:" << std::endl
               << e - a << std::endl  // TODO(niboshi): Use abs
               << "Actual output[" << i << "]:" << std::endl
               << a << std::endl
               << "Expected output[" << i << "]:" << std ::endl
               << e << std::endl;
        }
        throw RoutinesCheckError{os.str()};
    }
}

}  // namespace

// TODO(niboshi): Check array nodes of output arrays to ensure the implementation takes backprop mode into account
void CheckForward(
        const std::function<std::vector<Array>(const std::vector<Array>&)>& func,
        const std::vector<Array>& inputs,
        const std::vector<Array>& expected_outputs,
        size_t concurrent_check_repeat_count,
        size_t concurrent_check_thread_count,
        double atol,
        double rtol) {
    // Run single-shot test
    std::vector<Array> outputs = func(inputs);
    CheckArraysEqual(expected_outputs, outputs, atol, rtol);

    // Run thread safety check
    if (concurrent_check_repeat_count > 0) {
        Context& context = xchainer::GetDefaultContext();

        CheckThreadSafety(
                concurrent_check_repeat_count,
                concurrent_check_thread_count,
                [](size_t /*repeat_index*/) { return nullptr; },
                [&func, &inputs, &expected_outputs, &atol, &rtol, &context](size_t /*thread_index*/, std::nullptr_t) {
                    xchainer::SetDefaultContext(&context);
                    std::vector<Array> outputs = func(inputs);
                    CheckArraysEqual(expected_outputs, outputs, atol, rtol);
                    return nullptr;
                },
                [](const std::vector<std::nullptr_t>&) {});
    }
}

}  // namespace testing
}  // namespace xchainer
<commit_msg>CheckArraysEqual -> CheckOutputArraysEqual<commit_after>#include "xchainer/testing/routines.h"

#include <functional>
#include <iostream>
#include <sstream>
#include <vector>

#include <gsl/gsl>

#include "xchainer/array.h"
#include "xchainer/context.h"
#include "xchainer/numeric.h"
#include "xchainer/testing/threading.h"

namespace xchainer {
namespace testing {
namespace {

void CheckOutputArraysEqual(const std::vector<Array>& expected, const std::vector<Array>& actual, double atol, double rtol) {
    // Number of outputs
    if (expected.size() != actual.size()) {
        throw RoutinesCheckError{"Number of output arrays does not match."};
    }

    // Output array properties
    for (size_t i = 0; i < expected.size(); ++i) {
        const Array& e = expected[i];
        const Array& a = actual[i];
        if (e.shape() != a.shape()) {
            throw RoutinesCheckError{"Shape of output array ",
                                     i,
                                     " of ",
                                     expected.size(),
                                     " is incorrect. Actual ",
                                     a.shape(),
                                     " != Expected ",
                                     e.shape(),
                                     "."};
        }
        if (e.dtype() != a.dtype()) {
            throw RoutinesCheckError{"Dtype of output array ",
                                     i,
                                     " of ",
                                     expected.size(),
                                     " is incorrect. Actual ",
                                     GetDtypeName(a.dtype()),
                                     " != Expected ",
                                     GetDtypeName(e.dtype()),
                                     "."};
        }
        if (&e.device() != &a.device()) {
            throw RoutinesCheckError{"Device of output array ",
                                     i,
                                     " of ",
                                     expected.size(),
                                     " is incorrect. Actual ",
                                     a.device().name(),
                                     " != Expected ",
                                     e.device().name(),
                                     "."};
        }
    }

    // Numerical check
    std::vector<size_t> failed_input_indices;
    for (size_t i = 0; i < expected.size(); ++i) {
        const Array& e = expected[i];
        const Array& a = actual[i];
        if (!AllClose(e, a, atol, rtol)) {
            failed_input_indices.emplace_back(i);
        }
    }
    if (!failed_input_indices.empty()) {
        std::ostringstream os;
        os << "Numerical error in forward outputs (out of " << expected.size() << "): ";
        for (size_t i : failed_input_indices) {
            if (i != 0) {
                os << ", ";
            }
            os << i;
        }
        os << std::endl;
        os << "Atol: " << atol << "  Rtol: " << rtol << std::endl;
        for (size_t i : failed_input_indices) {
            const Array& e = expected[i];
            const Array& a = actual[i];
            os << "Error[" << i << "]:" << std::endl
               << e - a << std::endl  // TODO(niboshi): Use abs
               << "Actual output[" << i << "]:" << std::endl
               << a << std::endl
               << "Expected output[" << i << "]:" << std ::endl
               << e << std::endl;
        }
        throw RoutinesCheckError{os.str()};
    }
}

}  // namespace

// TODO(niboshi): Check array nodes of output arrays to ensure the implementation takes backprop mode into account
void CheckForward(
        const std::function<std::vector<Array>(const std::vector<Array>&)>& func,
        const std::vector<Array>& inputs,
        const std::vector<Array>& expected_outputs,
        size_t concurrent_check_repeat_count,
        size_t concurrent_check_thread_count,
        double atol,
        double rtol) {
    // Run single-shot test
    std::vector<Array> outputs = func(inputs);
    CheckOutputArraysEqual(expected_outputs, outputs, atol, rtol);

    // Run thread safety check
    if (concurrent_check_repeat_count > 0) {
        Context& context = xchainer::GetDefaultContext();

        CheckThreadSafety(
                concurrent_check_repeat_count,
                concurrent_check_thread_count,
                [](size_t /*repeat_index*/) { return nullptr; },
                [&func, &inputs, &expected_outputs, &atol, &rtol, &context](size_t /*thread_index*/, std::nullptr_t) {
                    xchainer::SetDefaultContext(&context);
                    std::vector<Array> outputs = func(inputs);
                    CheckOutputArraysEqual(expected_outputs, outputs, atol, rtol);
                    return nullptr;
                },
                [](const std::vector<std::nullptr_t>&) {});
    }
}

}  // namespace testing
}  // namespace xchainer
<|endoftext|>


<commit_before>// Copyright (c) 2020 by Apex.AI Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "test.hpp"

#include "iceoryx_utils/concurrent/resizeable_lockfree_queue.hpp"
using namespace ::testing;

//*********************************************************
// Test the added functionaility of ResizeableLockFreeQueue
// to change the capacity (setCapacity).
// The remaining functionality is identical to LockFreeQueue
// and tested in test_lockfree_queue.cpp (as a typed test).
//*********************************************************

namespace
{
// use a non-POD type for testing (just a boxed version of int)
struct Integer
{
    Integer(int value = 0)
        : value(value)
    {
    }

    int value{0};

    // so that it behaves like an int for comparison purposes
    operator int() const
    {
        return value;
    }
};

template <typename T>
class ResizeableLockFreeQueueTest : public ::testing::Test
{
  protected:
    ResizeableLockFreeQueueTest()
    {
    }

    ~ResizeableLockFreeQueueTest()
    {
    }

    void SetUp()
    {
    }

    void TearDown()
    {
    }

    void fillQueue(int start = 0)
    {
        int element{start};
        for (uint64_t i = 0; i < queue.capacity(); ++i)
        {
            queue.tryPush(element);
            element++;
        }
    }

    using Queue = T;
    Queue queue;
};

template <size_t Capacity>
using IntegerQueue = iox::concurrent::ResizeableLockFreeQueue<Integer, Capacity>;

template <size_t Capacity>
using IntQueue = iox::concurrent::ResizeableLockFreeQueue<int, Capacity>;

typedef ::testing::Types<IntegerQueue<1>, IntegerQueue<10>, IntQueue<10>> TestQueues;

/// we require TYPED_TEST since we support gtest 1.8 for our safety targets
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
TYPED_TEST_CASE(ResizeableLockFreeQueueTest, TestQueues);
#pragma GCC diagnostic pop

TEST(ResizeableLockFreeQueueTest, maxCapacityIsConsistent)
{
    using Queue = IntegerQueue<37>;
    EXPECT_EQ(Queue::maxCapacity(), 37);
}

TYPED_TEST(ResizeableLockFreeQueueTest, initialCapacityIsMaximalbyDefault)
{
    using Queue = typename TestFixture::Queue;
    auto& q = this->queue;
    EXPECT_EQ(q.capacity(), q.maxCapacity());
    EXPECT_EQ(q.capacity(), Queue::maxCapacity());
}

TYPED_TEST(ResizeableLockFreeQueueTest, constructWithMaxCapacity)
{
    constexpr auto MAX_CAP = TestFixture::Queue::MAX_CAPACITY;
    typename TestFixture::Queue q(MAX_CAP);
    EXPECT_EQ(q.capacity(), q.maxCapacity());
}

TYPED_TEST(ResizeableLockFreeQueueTest, constructWithMoreThanMaxCapacitySaturatesAtMaxCapacity)
{
    constexpr auto MAX_CAP = TestFixture::Queue::MAX_CAPACITY;
    typename TestFixture::Queue q(MAX_CAP + 1);
    EXPECT_EQ(q.capacity(), q.maxCapacity());
}

TYPED_TEST(ResizeableLockFreeQueueTest, constructWithNoCapacity)
{
    typename TestFixture::Queue q(0);
    EXPECT_EQ(q.capacity(), 0);
}

TYPED_TEST(ResizeableLockFreeQueueTest, constructWithHalfOfMaxCapacity)
{
    constexpr auto cap = TestFixture::Queue::MAX_CAPACITY / 2;
    typename TestFixture::Queue q(cap);
    EXPECT_EQ(q.capacity(), cap);
}

TYPED_TEST(ResizeableLockFreeQueueTest, decreaseCapacityToZeroOneByOne)
{
    auto& q = this->queue;
    constexpr auto MAX_CAP = TestFixture::Queue::MAX_CAPACITY;

    auto i = MAX_CAP;
    while (i > 0)
    {
        EXPECT_TRUE(q.setCapacity(--i));
        ASSERT_EQ(q.capacity(), i);
    }
}

TYPED_TEST(ResizeableLockFreeQueueTest, increaseToMaxCapacity)
{
    typename TestFixture::Queue q(0);
    constexpr auto MAX_CAP = TestFixture::Queue::MAX_CAPACITY;
    EXPECT_EQ(q.capacity(), 0);
    EXPECT_TRUE(q.setCapacity(MAX_CAP));
    EXPECT_EQ(q.capacity(), MAX_CAP);
}

TYPED_TEST(ResizeableLockFreeQueueTest, increaseToMaxCapacityOneByOne)
{
    typename TestFixture::Queue q(0);
    constexpr auto MAX_CAP = TestFixture::Queue::MAX_CAPACITY;
    EXPECT_EQ(q.capacity(), 0);

    for (uint64_t i = 0; i < MAX_CAP;)
    {
        EXPECT_TRUE(q.setCapacity(++i));
        ASSERT_EQ(q.capacity(), i);
    }
}

TYPED_TEST(ResizeableLockFreeQueueTest, setCapacityToZero)
{
    auto& q = this->queue;
    EXPECT_TRUE(q.setCapacity(0));
    EXPECT_EQ(q.capacity(), 0);
}

TYPED_TEST(ResizeableLockFreeQueueTest, setCapacityToOne)
{
    auto& q = this->queue;
    EXPECT_TRUE(q.setCapacity(1));
    EXPECT_EQ(q.capacity(), 1);
}

TYPED_TEST(ResizeableLockFreeQueueTest, setCapacityToMaxCapacity)
{
    typename TestFixture::Queue q(0);
    constexpr auto MAX_CAP = TestFixture::Queue::MAX_CAPACITY;
    EXPECT_TRUE(q.setCapacity(MAX_CAP));
    EXPECT_EQ(q.capacity(), MAX_CAP);
}

TYPED_TEST(ResizeableLockFreeQueueTest, setCapacityToHalfOfMaxCapacityAndFillIt)
{
    auto& q = this->queue;
    constexpr auto MAX_CAP = TestFixture::Queue::MAX_CAPACITY;
    uint64_t newCap = MAX_CAP / 2;
    EXPECT_EQ(q.setCapacity(newCap), true);
    EXPECT_EQ(q.capacity(), newCap);

    uint element = 0;
    while (q.tryPush(element++))
        ;

    EXPECT_EQ(q.capacity(), newCap);
    EXPECT_EQ(q.size(), newCap);
    EXPECT_EQ(element, newCap + 1);
}

TYPED_TEST(ResizeableLockFreeQueueTest, setCapacityFromHalfOfMaxCapacityToMaxCapacity)
{
    auto& q = this->queue;
    constexpr auto MAX_CAP = TestFixture::Queue::MAX_CAPACITY;
    uint64_t cap = MAX_CAP / 2;
    EXPECT_EQ(q.setCapacity(cap), true);
    EXPECT_EQ(q.capacity(), cap);

    uint64_t element = 0;
    while (q.tryPush(element++))
        ;

    EXPECT_EQ(q.capacity(), cap);
    EXPECT_EQ(q.size(), cap);
    EXPECT_EQ(element, cap + 1);

    EXPECT_EQ(q.setCapacity(MAX_CAP), true);
    EXPECT_EQ(q.capacity(), MAX_CAP);
    EXPECT_EQ(q.size(), cap);

    --element; // compensate the last failed tryPush
    while (q.tryPush(element++))
        ;

    // we want to find all elements we pushed
    for (element = 0; element < MAX_CAP; ++element)
    {
        auto result = q.pop();
        ASSERT_TRUE(result.has_value());
        EXPECT_EQ(result.value(), element);
    }
}

TYPED_TEST(ResizeableLockFreeQueueTest, setCapacityOfFullQueueToHalfOfMaxCapacity)
{
    auto& q = this->queue;
    constexpr auto MAX_CAP = TestFixture::Queue::MAX_CAPACITY;
    uint64_t cap = MAX_CAP / 2;

    uint64_t element = 0;
    while (q.tryPush(element++))
        ;
    EXPECT_EQ(q.capacity(), MAX_CAP);
    EXPECT_EQ(q.size(), MAX_CAP);

    EXPECT_TRUE(q.setCapacity(cap));
    EXPECT_EQ(q.capacity(), cap);
    EXPECT_EQ(q.size(), cap);

    if (cap == 0)
    {
        return;
    }
    // the least recent values are removed due to the capacity being decreased
    for (element = cap; element < MAX_CAP; ++element)
    {
        auto result = q.pop();
        ASSERT_TRUE(result.has_value());
        EXPECT_EQ(result.value(), element);
    }
}

// note this is one of the most general cases and necessary to test:
// decreasing the capacity starting with a partially filled queue and checking whether the last values
// remain (and the others are removed)
TYPED_TEST(ResizeableLockFreeQueueTest, DecreaseCapacityOfAPartiallyFilledQueue)
{
    auto& q = this->queue;
    constexpr auto MAX_CAP = TestFixture::Queue::MAX_CAPACITY;
    iox::cxx::vector<int, MAX_CAP> removedElements;

    uint64_t cap = MAX_CAP / 2;

    EXPECT_TRUE(q.setCapacity(cap));
    EXPECT_EQ(q.capacity(), cap);

    uint64_t element = 0;
    while (q.tryPush(element++))
        ;

    EXPECT_EQ(q.capacity(), cap);
    EXPECT_EQ(q.size(), cap);

    auto cap2 = cap + MAX_CAP / 4; // roughly 3 quarters of max (integer division)
    EXPECT_TRUE(q.setCapacity(cap2));

    EXPECT_EQ(q.capacity(), cap2);
    EXPECT_EQ(q.size(), cap);

    auto cap3 = cap2 - cap; // roughly a quarter of max

    EXPECT_TRUE(q.setCapacity(cap3, removedElements));
    EXPECT_EQ(q.capacity(), cap3);
    EXPECT_EQ(q.size(), cap3);

    // cap3 elements remain, the first cap - cap3 elements are removed

    // were the least recent elements removed?
    EXPECT_EQ(removedElements.size(), cap - cap3);
    element = 0;
    for (auto& removedElement : removedElements)
    {
        EXPECT_EQ(removedElement, element++);
    }

    // are the remaining elements correct? (i.e. we did not remove too many elements)
    for (element = cap - cap3; element < cap; ++element)
    {
        auto result = q.pop();
        ASSERT_TRUE(result.has_value());
        EXPECT_EQ(result.value(), element);
    }

    // refill to verify the capacity can really be used

    element = 0;
    while (q.tryPush(element++))
        ;

    for (element = 0; element < cap3; ++element)
    {
        auto result = q.pop();
        ASSERT_TRUE(result.has_value());
        EXPECT_EQ(result.value(), element);
    }
}

TYPED_TEST(ResizeableLockFreeQueueTest, DecreaseCapacityOfAPartiallyFilledQueueWithRemoveHandler)
{
    using element_t = typename TestFixture::Queue::element_t;
    auto& q = this->queue;
    constexpr auto MAX_CAP = TestFixture::Queue::MAX_CAPACITY;

    iox::cxx::vector<int, MAX_CAP> removedElements;
    auto removeHandler = [&](const element_t& value) { removedElements.push_back(std::move(value)); };

    uint64_t cap = MAX_CAP / 2;

    EXPECT_TRUE(q.setCapacity(cap));
    EXPECT_EQ(q.capacity(), cap);

    uint64_t element = 0;
    while (q.tryPush(element++))
        ;

    EXPECT_EQ(q.capacity(), cap);
    EXPECT_EQ(q.size(), cap);

    auto cap2 = cap + MAX_CAP / 4; // roughly 3 quarters of max (integer division)
    EXPECT_TRUE(q.setCapacity(cap2));

    EXPECT_EQ(q.capacity(), cap2);
    EXPECT_EQ(q.size(), cap);

    auto cap3 = cap2 - cap; // roughly a quarter of max

    EXPECT_TRUE(q.setCapacity(cap3, removeHandler));
    EXPECT_EQ(q.capacity(), cap3);
    EXPECT_EQ(q.size(), cap3);

    // cap3 elements remain, the first cap - cap3 elements are removed

    // were the least recent elements removed?
    EXPECT_EQ(removedElements.size(), cap - cap3);
    element = 0;
    for (auto& removedElement : removedElements)
    {
        EXPECT_EQ(removedElement, element++);
    }

    // are the remaining elements correct? (i.e. we did not remove too many elements)
    for (element = cap - cap3; element < cap; ++element)
    {
        auto result = q.pop();
        ASSERT_TRUE(result.has_value());
        EXPECT_EQ(result.value(), element);
    }

    // refill to verify the capacity can really be used

    element = 0;
    while (q.tryPush(element++))
        ;

    for (element = 0; element < cap3; ++element)
    {
        auto result = q.pop();
        ASSERT_TRUE(result.has_value());
        EXPECT_EQ(result.value(), element);
    }
}

} // namespace<commit_msg>iox-#216 fix type conversion in test<commit_after>// Copyright (c) 2020 by Apex.AI Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "test.hpp"

#include "iceoryx_utils/concurrent/resizeable_lockfree_queue.hpp"
using namespace ::testing;

//*********************************************************
// Test the added functionaility of ResizeableLockFreeQueue
// to change the capacity (setCapacity).
// The remaining functionality is identical to LockFreeQueue
// and tested in test_lockfree_queue.cpp (as a typed test).
//*********************************************************

namespace
{
// use a non-POD type for testing (just a boxed version of int)
struct Integer
{
    Integer(int value = 0)
        : value(value)
    {
    }

    int value{0};

    // so that it behaves like an int for comparison purposes
    operator int() const
    {
        return value;
    }
};

template <typename T>
class ResizeableLockFreeQueueTest : public ::testing::Test
{
  protected:
    ResizeableLockFreeQueueTest()
    {
    }

    ~ResizeableLockFreeQueueTest()
    {
    }

    void SetUp()
    {
    }

    void TearDown()
    {
    }

    void fillQueue(int start = 0)
    {
        int element{start};
        for (uint64_t i = 0; i < queue.capacity(); ++i)
        {
            queue.tryPush(element);
            element++;
        }
    }

    using Queue = T;
    Queue queue;
};

template <size_t Capacity>
using IntegerQueue = iox::concurrent::ResizeableLockFreeQueue<Integer, Capacity>;

template <size_t Capacity>
using IntQueue = iox::concurrent::ResizeableLockFreeQueue<int, Capacity>;

typedef ::testing::Types<IntegerQueue<1>, IntegerQueue<10>, IntQueue<10>> TestQueues;

/// we require TYPED_TEST since we support gtest 1.8 for our safety targets
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
TYPED_TEST_CASE(ResizeableLockFreeQueueTest, TestQueues);
#pragma GCC diagnostic pop

TEST(ResizeableLockFreeQueueTest, maxCapacityIsConsistent)
{
    using Queue = IntegerQueue<37>;
    EXPECT_EQ(Queue::maxCapacity(), 37);
}

TYPED_TEST(ResizeableLockFreeQueueTest, initialCapacityIsMaximalbyDefault)
{
    using Queue = typename TestFixture::Queue;
    auto& q = this->queue;
    EXPECT_EQ(q.capacity(), q.maxCapacity());
    EXPECT_EQ(q.capacity(), Queue::maxCapacity());
}

TYPED_TEST(ResizeableLockFreeQueueTest, constructWithMaxCapacity)
{
    constexpr auto MAX_CAP = TestFixture::Queue::MAX_CAPACITY;
    typename TestFixture::Queue q(MAX_CAP);
    EXPECT_EQ(q.capacity(), q.maxCapacity());
}

TYPED_TEST(ResizeableLockFreeQueueTest, constructWithMoreThanMaxCapacitySaturatesAtMaxCapacity)
{
    constexpr auto MAX_CAP = TestFixture::Queue::MAX_CAPACITY;
    typename TestFixture::Queue q(MAX_CAP + 1);
    EXPECT_EQ(q.capacity(), q.maxCapacity());
}

TYPED_TEST(ResizeableLockFreeQueueTest, constructWithNoCapacity)
{
    typename TestFixture::Queue q(0);
    EXPECT_EQ(q.capacity(), 0);
}

TYPED_TEST(ResizeableLockFreeQueueTest, constructWithHalfOfMaxCapacity)
{
    constexpr auto cap = TestFixture::Queue::MAX_CAPACITY / 2;
    typename TestFixture::Queue q(cap);
    EXPECT_EQ(q.capacity(), cap);
}

TYPED_TEST(ResizeableLockFreeQueueTest, decreaseCapacityToZeroOneByOne)
{
    auto& q = this->queue;
    constexpr auto MAX_CAP = TestFixture::Queue::MAX_CAPACITY;

    auto i = MAX_CAP;
    while (i > 0)
    {
        EXPECT_TRUE(q.setCapacity(--i));
        ASSERT_EQ(q.capacity(), i);
    }
}

TYPED_TEST(ResizeableLockFreeQueueTest, increaseToMaxCapacity)
{
    typename TestFixture::Queue q(0);
    constexpr auto MAX_CAP = TestFixture::Queue::MAX_CAPACITY;
    EXPECT_EQ(q.capacity(), 0);
    EXPECT_TRUE(q.setCapacity(MAX_CAP));
    EXPECT_EQ(q.capacity(), MAX_CAP);
}

TYPED_TEST(ResizeableLockFreeQueueTest, increaseToMaxCapacityOneByOne)
{
    typename TestFixture::Queue q(0);
    constexpr auto MAX_CAP = TestFixture::Queue::MAX_CAPACITY;
    EXPECT_EQ(q.capacity(), 0);

    for (uint64_t i = 0; i < MAX_CAP;)
    {
        EXPECT_TRUE(q.setCapacity(++i));
        ASSERT_EQ(q.capacity(), i);
    }
}

TYPED_TEST(ResizeableLockFreeQueueTest, setCapacityToZero)
{
    auto& q = this->queue;
    EXPECT_TRUE(q.setCapacity(0));
    EXPECT_EQ(q.capacity(), 0);
}

TYPED_TEST(ResizeableLockFreeQueueTest, setCapacityToOne)
{
    auto& q = this->queue;
    EXPECT_TRUE(q.setCapacity(1));
    EXPECT_EQ(q.capacity(), 1);
}

TYPED_TEST(ResizeableLockFreeQueueTest, setCapacityToMaxCapacity)
{
    typename TestFixture::Queue q(0);
    constexpr auto MAX_CAP = TestFixture::Queue::MAX_CAPACITY;
    EXPECT_TRUE(q.setCapacity(MAX_CAP));
    EXPECT_EQ(q.capacity(), MAX_CAP);
}

TYPED_TEST(ResizeableLockFreeQueueTest, setCapacityToHalfOfMaxCapacityAndFillIt)
{
    auto& q = this->queue;
    constexpr auto MAX_CAP = TestFixture::Queue::MAX_CAPACITY;
    uint64_t newCap = MAX_CAP / 2;
    EXPECT_EQ(q.setCapacity(newCap), true);
    EXPECT_EQ(q.capacity(), newCap);

    int element = 0;
    while (q.tryPush(element++))
        ;

    EXPECT_EQ(q.capacity(), newCap);
    EXPECT_EQ(q.size(), newCap);
    EXPECT_EQ(element, newCap + 1);
}

TYPED_TEST(ResizeableLockFreeQueueTest, setCapacityFromHalfOfMaxCapacityToMaxCapacity)
{
    auto& q = this->queue;
    constexpr auto MAX_CAP = TestFixture::Queue::MAX_CAPACITY;
    uint64_t cap = MAX_CAP / 2;
    EXPECT_EQ(q.setCapacity(cap), true);
    EXPECT_EQ(q.capacity(), cap);

    int element = 0;
    while (q.tryPush(element++))
        ;

    EXPECT_EQ(q.capacity(), cap);
    EXPECT_EQ(q.size(), cap);
    EXPECT_EQ(element, cap + 1);

    EXPECT_EQ(q.setCapacity(MAX_CAP), true);
    EXPECT_EQ(q.capacity(), MAX_CAP);
    EXPECT_EQ(q.size(), cap);

    --element; // compensate the last failed tryPush
    while (q.tryPush(element++))
        ;

    // we want to find all elements we pushed
    for (element = 0; element < MAX_CAP; ++element)
    {
        auto result = q.pop();
        ASSERT_TRUE(result.has_value());
        EXPECT_EQ(result.value(), element);
    }
}

TYPED_TEST(ResizeableLockFreeQueueTest, setCapacityOfFullQueueToHalfOfMaxCapacity)
{
    auto& q = this->queue;
    constexpr auto MAX_CAP = TestFixture::Queue::MAX_CAPACITY;
    uint64_t cap = MAX_CAP / 2;

    int element = 0;
    while (q.tryPush(element++))
        ;
    EXPECT_EQ(q.capacity(), MAX_CAP);
    EXPECT_EQ(q.size(), MAX_CAP);

    EXPECT_TRUE(q.setCapacity(cap));
    EXPECT_EQ(q.capacity(), cap);
    EXPECT_EQ(q.size(), cap);

    if (cap == 0)
    {
        return;
    }
    // the least recent values are removed due to the capacity being decreased
    for (element = cap; element < MAX_CAP; ++element)
    {
        auto result = q.pop();
        ASSERT_TRUE(result.has_value());
        EXPECT_EQ(result.value(), element);
    }
}

// note this is one of the most general cases and necessary to test:
// decreasing the capacity starting with a partially filled queue and checking whether the last values
// remain (and the others are removed)
TYPED_TEST(ResizeableLockFreeQueueTest, DecreaseCapacityOfAPartiallyFilledQueue)
{
    auto& q = this->queue;
    constexpr auto MAX_CAP = TestFixture::Queue::MAX_CAPACITY;
    iox::cxx::vector<int, MAX_CAP> removedElements;

    uint64_t cap = MAX_CAP / 2;

    EXPECT_TRUE(q.setCapacity(cap));
    EXPECT_EQ(q.capacity(), cap);

    int element = 0;
    while (q.tryPush(element++))
        ;

    EXPECT_EQ(q.capacity(), cap);
    EXPECT_EQ(q.size(), cap);

    auto cap2 = cap + MAX_CAP / 4; // roughly 3 quarters of max (integer division)
    EXPECT_TRUE(q.setCapacity(cap2));

    EXPECT_EQ(q.capacity(), cap2);
    EXPECT_EQ(q.size(), cap);

    auto cap3 = cap2 - cap; // roughly a quarter of max

    EXPECT_TRUE(q.setCapacity(cap3, removedElements));
    EXPECT_EQ(q.capacity(), cap3);
    EXPECT_EQ(q.size(), cap3);

    // cap3 elements remain, the first cap - cap3 elements are removed

    // were the least recent elements removed?
    EXPECT_EQ(removedElements.size(), cap - cap3);
    element = 0;
    for (auto& removedElement : removedElements)
    {
        EXPECT_EQ(removedElement, element++);
    }

    // are the remaining elements correct? (i.e. we did not remove too many elements)
    for (element = cap - cap3; element < cap; ++element)
    {
        auto result = q.pop();
        ASSERT_TRUE(result.has_value());
        EXPECT_EQ(result.value(), element);
    }

    // refill to verify the capacity can really be used

    element = 0;
    while (q.tryPush(element++))
        ;

    for (element = 0; element < cap3; ++element)
    {
        auto result = q.pop();
        ASSERT_TRUE(result.has_value());
        EXPECT_EQ(result.value(), element);
    }
}

TYPED_TEST(ResizeableLockFreeQueueTest, DecreaseCapacityOfAPartiallyFilledQueueWithRemoveHandler)
{
    using element_t = typename TestFixture::Queue::element_t;
    auto& q = this->queue;
    constexpr auto MAX_CAP = TestFixture::Queue::MAX_CAPACITY;

    iox::cxx::vector<int, MAX_CAP> removedElements;
    auto removeHandler = [&](const element_t& value) { removedElements.push_back(std::move(value)); };

    uint64_t cap = MAX_CAP / 2;

    EXPECT_TRUE(q.setCapacity(cap));
    EXPECT_EQ(q.capacity(), cap);

    int element = 0;
    while (q.tryPush(element++))
        ;

    EXPECT_EQ(q.capacity(), cap);
    EXPECT_EQ(q.size(), cap);

    auto cap2 = cap + MAX_CAP / 4; // roughly 3 quarters of max (integer division)
    EXPECT_TRUE(q.setCapacity(cap2));

    EXPECT_EQ(q.capacity(), cap2);
    EXPECT_EQ(q.size(), cap);

    auto cap3 = cap2 - cap; // roughly a quarter of max

    EXPECT_TRUE(q.setCapacity(cap3, removeHandler));
    EXPECT_EQ(q.capacity(), cap3);
    EXPECT_EQ(q.size(), cap3);

    // cap3 elements remain, the first cap - cap3 elements are removed

    // were the least recent elements removed?
    EXPECT_EQ(removedElements.size(), cap - cap3);
    element = 0;
    for (auto& removedElement : removedElements)
    {
        EXPECT_EQ(removedElement, element++);
    }

    // are the remaining elements correct? (i.e. we did not remove too many elements)
    for (element = cap - cap3; element < cap; ++element)
    {
        auto result = q.pop();
        ASSERT_TRUE(result.has_value());
        EXPECT_EQ(result.value(), element);
    }

    // refill to verify the capacity can really be used

    element = 0;
    while (q.tryPush(element++))
        ;

    for (element = 0; element < cap3; ++element)
    {
        auto result = q.pop();
        ASSERT_TRUE(result.has_value());
        EXPECT_EQ(result.value(), element);
    }
}

} // namespace<|endoftext|>


<commit_before>/************************************************
 * Copyright (c) IBM Corp. 2014
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *************************************************/

/*
 * Contributors:
 *     arayshu, lschneid - initial implementation
 */

#ifndef __SKV_SERVER_RETRIEVE_N_KEYS_COMMAND_SM_HPP__
#define __SKV_SERVER_RETRIEVE_N_KEYS_COMMAND_SM_HPP__

#ifndef SKV_SERVER_RETRIEVE_N_KEYS_COMMAND_SM_LOG
#define SKV_SERVER_RETRIEVE_N_KEYS_COMMAND_SM_LOG  ( 0 | SKV_LOGGING_ALL )
#endif

#ifndef SKV_SERVER_RETRIEVE_N_KEYS_DATA_LOG
#define SKV_SERVER_RETRIEVE_N_KEYS_DATA_LOG ( 0 )
#endif

class skv_server_retrieve_n_keys_command_sm
{
  static inline
  skv_status_t create_multi_stage( skv_server_ep_state_t *aEPState,
                                   skv_local_kv_t *aLocalKV,
                                   skv_server_ccb_t *aCommand,
                                   int aCommandOrdinal )
  {
    skv_status_t status = SKV_SUCCESS;

    BegLogLine( SKV_SERVER_RETRIEVE_N_KEYS_COMMAND_SM_LOG )
      << "skv_server_retrieve_n_keys_command_sm:: "
      << " Command requires async operation (async storage or data transfer)..."
      << EndLogLine;

    // check if we're already multi-stage class command
    if ( aCommand->GetCommandClass() == SKV_COMMAND_CLASS_MULTI_STAGE )
      return status;

    // \todo check if we need locking when going multi-stage! (retreive-side locking isn't considered yet)
    // To be ready, we have the aLocalKV as a function arguments

    aEPState->ReplaceAndInitCommandBuffer( aCommand, aCommandOrdinal );

    return status;
  }

  static inline
  skv_status_t retrieve_n_start( skv_server_ep_state_t *aEPState,
                                 skv_local_kv_t *aLocalKV,
                                 skv_server_ccb_t *aCommand,
                                 int aCommandOrdinal,
                                 skv_cmd_retrieve_n_keys_req_t* aReq,
                                 int *aRetrievedKeysCount,
                                 skv_lmr_triplet_t *aRetrievedKeysSizesSegs,
                                 int *aRetrievedKeysSizesSegsCount )
  {
    // Check if the key is in the buffer
    if( ! aReq->mIsKeyInCtrlMsg )
      return SKV_ERRNO_KEY_TOO_LARGE;

    AssertLogLine( SKV_CLIENT_MAX_CURSOR_KEYS_TO_CACHE == aReq->mKeysDataListMaxCount )
      << "skv_server_retrieve_n_keys_command_sm:: ERROR: "
      << " aRemoteMemKeysMaxCount: " << aReq->mKeysDataListMaxCount
      << " SKV_CLIENT_MAX_CURSOR_KEYS_TO_CACHE: " << SKV_CLIENT_MAX_CURSOR_KEYS_TO_CACHE
      << EndLogLine;

    // Check if the key exists
    skv_local_kv_cookie_t *cookie = &aCommand->mLocalKVCookie;
    cookie->Set( aCommandOrdinal, aEPState );
    skv_status_t status = aLocalKV->RetrieveNKeys( aReq->mPDSId,
                                                   aReq->mStartingKeyData,
                                                   aReq->mStartingKeySize,
                                                   aRetrievedKeysSizesSegs,
                                                   aRetrievedKeysCount,
                                                   aRetrievedKeysSizesSegsCount,
                                                   aReq->mKeysDataListMaxCount,
                                                   aReq->mFlags,
                                                   cookie );

    BegLogLine( SKV_SERVER_RETRIEVE_N_KEYS_COMMAND_SM_LOG )
      << "skv_server_retrieve_n_keys_command_sm:: Results of local call to RetrieveNKeys: "
      << " RetrievedKeysCount: " << *aRetrievedKeysCount
      << " RetrievedKeysSizesSegsCount: " << aRetrievedKeysSizesSegsCount
      << " aRemoteMemKeysMaxCount: " << aReq->mKeysDataListMaxCount
      << " first Key: " << aRetrievedKeysSizesSegs[0].GetAddr()
      << " status: " << skv_status_to_string( status )
      << EndLogLine;

    return status;
  }

  static inline
  skv_status_t post_rdma_write( skv_server_ep_state_t *aEPState,
                                skv_lmr_triplet_t *aRetrievedKeysSizesSegs,
                                int aRetrievedKeysSizesSegsCount,
                                skv_cmd_retrieve_n_keys_req_t* aReq )
  {
    it_status_t itstatus = IT_SUCCESS;

    skv_server_rdma_write_cmpl_cookie_t Cookie;
    Cookie.Init( NULL, NULL );

    //        it_dto_flags_t dto_flags = (it_dto_flags_t) ( IT_COMPLETION_FLAG | IT_NOTIFY_FLAG );
    it_dto_flags_t dto_flags = (it_dto_flags_t) ( 0 );

    BegLogLine( SKV_SERVER_RETRIEVE_N_KEYS_DATA_LOG )
      << "skv_server_retrieve_n_keys_command_sm::post_rdma_write(): LMRs: "
      << " " << aRetrievedKeysSizesSegs[0]
      << " " << aRetrievedKeysSizesSegs[1]
      << " " << aRetrievedKeysSizesSegs[2]
      << " " << aRetrievedKeysSizesSegs[3]
      << EndLogLine;

    BegLogLine( SKV_SERVER_RETRIEVE_N_KEYS_DATA_LOG )
      << "skv_server_retrieve_n_keys_command_sm::post_rdma_write(): first keysize: " << *(int*)(aRetrievedKeysSizesSegs[0].GetAddr())
      << " writeTo: " << aReq->mKeysDataList
      << " KeyData: " << HexDump( (void*)aRetrievedKeysSizesSegs[0].GetAddr(), aRetrievedKeysSizesSegs[0].GetLen() )
      << " " << HexDump( (void*)aRetrievedKeysSizesSegs[1].GetAddr(), aRetrievedKeysSizesSegs[1].GetLen() )
      << " " << HexDump( (void*)aRetrievedKeysSizesSegs[2].GetAddr(), aRetrievedKeysSizesSegs[2].GetLen() )
      << " " << HexDump( (void*)aRetrievedKeysSizesSegs[3].GetAddr(), aRetrievedKeysSizesSegs[3].GetLen() )
      << EndLogLine;

    // rdma_write the value
    itstatus = it_post_rdma_write( aEPState->mEPHdl,
                                   (it_lmr_triplet_t *) aRetrievedKeysSizesSegs,
                                   aRetrievedKeysSizesSegsCount,
                                   Cookie.GetCookie(),
                                   dto_flags,
                                   (it_rdma_addr_t) aReq->mKeysDataList,
                                   aReq->mKeyDataCacheMemReg.mKeysDataCacheRMR );

    AssertLogLine( itstatus == IT_SUCCESS )
      << "skv_server_retrieve_n_keys_command_sm:: ERROR: "
      << " itstatus: " << itstatus
      << EndLogLine;

    return SKV_SUCCESS;
  }

  static inline
  skv_status_t command_completion( skv_status_t aRC,
                                   skv_server_ep_state_t *aEPState,
                                   skv_server_ccb_t *aCommand,
                                   int aRetrievedKeysCount,
                                   skv_lmr_triplet_t *aRetrievedKeysSizesSegs,
                                   skv_cmd_retrieve_n_keys_rdma_write_ack_t *aCmpl,
                                   int aCommandOrdinal,
                                   int *aSeqNo )
  {
    skv_status_t status;

    if( (aRC == SKV_SUCCESS) || (aRC == SKV_ERRNO_END_OF_RECORDS) )
    {
      aCmpl->mHdr.mEvent = SKV_CLIENT_EVENT_RDMA_WRITE_VALUE_ACK;
      aCmpl->mCachedKeysCount = aRetrievedKeysCount;
    }
    else
    {
      aCmpl->mHdr.mEvent = SKV_CLIENT_EVENT_ERROR;
      aCmpl->mCachedKeysCount = 0;
    }
    aCmpl->mStatus          = aRC;

    BegLogLine( SKV_SERVER_RETRIEVE_N_KEYS_COMMAND_SM_LOG )
      << "skv_server_retrieve_n_keys_command_sm:: "
      << " About to Dispatch(): "
      << " status: " << skv_status_to_string( aCmpl->mStatus )
      << " mCachedKeysCount: " << aCmpl->mCachedKeysCount
      << EndLogLine;

    status = aEPState->Dispatch( aCommand,
                                 aSeqNo,
                                 aCommandOrdinal );

    AssertLogLine( status == SKV_SUCCESS )
      << "skv_server_retrieve_n_keys_command_sm:: ERROR: "
      << " status: " << skv_status_to_string( status )
      << EndLogLine;

    delete aRetrievedKeysSizesSegs;

    return status;
  }

public:
  static
  skv_status_t
  Execute( skv_local_kv_t *aLocalKV,
           skv_server_ep_state_t *aEPState,
           int aCommandOrdinal,
           skv_server_event_t *aEvent,
           int *aSeqNo,
           it_pz_handle_t aPZHdl )
  {
    skv_status_t status = SKV_SUCCESS;
    skv_server_ccb_t* Command = aEPState->GetCommandForOrdinal( aCommandOrdinal );
    skv_server_command_state_t State = Command->mState;
    skv_server_event_type_t EventType = aEvent->mCmdEventType;

    switch( State )
    {
      case SKV_SERVER_COMMAND_STATE_INIT:
      {
        switch( EventType )
        {
          case SKV_SERVER_EVENT_TYPE_IT_DTO_RETRIEVE_N_KEYS_CMD:
          {
            int RetrievedKeysCount = 0;
            int RetrievedKeysSizesSegsCount = 0;
            // NEED NEED NEED:: This will live on the stack for now. Think about how to allocate this memory based
            // on aRemoteMemKeysMaxCount. Need a better place to put this.
            //
#define SKV_CLIENT_MAX_CURSOR_KEYS_TO_CACHE_SEND_VEC ( 2 * SKV_CLIENT_MAX_CURSOR_KEYS_TO_CACHE )
//            skv_lmr_triplet_t RetrievedKeysSizesSegs[ SKV_CLIENT_MAX_CURSOR_KEYS_TO_CACHE_SEND_VEC ];
            skv_lmr_triplet_t *RetrievedKeysSizesSegs = (skv_lmr_triplet_t*)new char( SKV_CLIENT_MAX_CURSOR_KEYS_TO_CACHE_SEND_VEC * sizeof(skv_lmr_triplet_t) );

            status = retrieve_n_start( aEPState,
                                       aLocalKV,
                                       Command,
                                       aCommandOrdinal,
                                       (skv_cmd_retrieve_n_keys_req_t*) Command->GetSendBuff(),
                                       & RetrievedKeysCount,
                                       RetrievedKeysSizesSegs,
                                       & RetrievedKeysSizesSegsCount );

            switch( status )
            {
              case SKV_ERRNO_END_OF_RECORDS:
                // skip the rdma_write only if there was no key retrieved, otherwise rdma the remaining keys
                if( RetrievedKeysSizesSegsCount == 0 )
                  break;
              case SKV_SUCCESS:
              {
                post_rdma_write( aEPState,
                                 RetrievedKeysSizesSegs,
                                 RetrievedKeysSizesSegsCount,
                                 (skv_cmd_retrieve_n_keys_req_t*) Command->GetSendBuff()
                                 );
                break;
              }
              case SKV_ERRNO_LOCAL_KV_EVENT:
                create_multi_stage( aEPState, aLocalKV, Command, aCommandOrdinal );
                Command->Transit( SKV_SERVER_COMMAND_STATE_LOCAL_KV_DATA_OP );
                return SKV_SUCCESS;
              default:

                break;
            }

            status = command_completion( status,
                                         aEPState,
                                         Command,
                                         RetrievedKeysCount,
                                         RetrievedKeysSizesSegs,
                                         (skv_cmd_retrieve_n_keys_rdma_write_ack_t*)Command->GetSendBuff(),
                                         aCommandOrdinal,
                                         aSeqNo );
            Command->Transit( SKV_SERVER_COMMAND_STATE_INIT );
            break;
          }
          default:
          {
            StrongAssertLogLine( 0 )
              << "skv_server_retrieve_n_keys_command_sm:: Execute():: ERROR: State not recognized"
              << " State: " << State
              << " EventType: " << EventType
              << EndLogLine;

            break;
          }
        }
        break;
      }
      case SKV_SERVER_COMMAND_STATE_LOCAL_KV_DATA_OP:
      {
        switch( EventType )
        {
          case SKV_SERVER_EVENT_TYPE_LOCAL_KV_CMPL:
          {
            status = Command->mLocalKVrc;
            switch( status )
            {
              case SKV_ERRNO_END_OF_RECORDS:
                // skip the rdma_write only if there was no key retrieved
                if( Command->mLocalKVData.mRetrieveNKeys.mKeysCount == 0 )
                  break;
                // no break on purpose: EOR might be signaled even if there were a few keys available?
              case SKV_SUCCESS:
                post_rdma_write( aEPState,
                                 Command->mLocalKVData.mRetrieveNKeys.mKeysSizesSegs,
                                 Command->mLocalKVData.mRetrieveNKeys.mKeysSizesSegsCount,
                                 (skv_cmd_retrieve_n_keys_req_t*) Command->GetSendBuff() );
                break;
            }
            status = command_completion( status,
                                         aEPState,
                                         Command,
                                         Command->mLocalKVData.mRetrieveNKeys.mKeysCount,
                                         Command->mLocalKVData.mRetrieveNKeys.mKeysSizesSegs,
                                         (skv_cmd_retrieve_n_keys_rdma_write_ack_t*)Command->GetSendBuff(),
                                         aCommandOrdinal,
                                         aSeqNo );
            Command->Transit( SKV_SERVER_COMMAND_STATE_INIT );
            break;
          }
          default:
            status = SKV_ERRNO_STATE_MACHINE_ERROR;
        }
        break;
      }
      default:
      {
        StrongAssertLogLine( 0 )
          << "skv_server_retrieve_n_keys_command_sm:: Execute():: ERROR: State not recognized"
          << " State: " << State
          << EndLogLine;

        break;
      }
    }

    return status;
  }
};
#endif
<commit_msg>fix memory allocation issue<commit_after>/************************************************
 * Copyright (c) IBM Corp. 2014
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *************************************************/

/*
 * Contributors:
 *     arayshu, lschneid - initial implementation
 */

#ifndef __SKV_SERVER_RETRIEVE_N_KEYS_COMMAND_SM_HPP__
#define __SKV_SERVER_RETRIEVE_N_KEYS_COMMAND_SM_HPP__

#ifndef SKV_SERVER_RETRIEVE_N_KEYS_COMMAND_SM_LOG
#define SKV_SERVER_RETRIEVE_N_KEYS_COMMAND_SM_LOG  ( 0 | SKV_LOGGING_ALL )
#endif

#ifndef SKV_SERVER_RETRIEVE_N_KEYS_DATA_LOG
#define SKV_SERVER_RETRIEVE_N_KEYS_DATA_LOG ( 0 )
#endif

class skv_server_retrieve_n_keys_command_sm
{
  static inline
  skv_status_t create_multi_stage( skv_server_ep_state_t *aEPState,
                                   skv_local_kv_t *aLocalKV,
                                   skv_server_ccb_t *aCommand,
                                   int aCommandOrdinal )
  {
    skv_status_t status = SKV_SUCCESS;

    BegLogLine( SKV_SERVER_RETRIEVE_N_KEYS_COMMAND_SM_LOG )
      << "skv_server_retrieve_n_keys_command_sm:: "
      << " Command requires async operation (async storage or data transfer)..."
      << EndLogLine;

    // check if we're already multi-stage class command
    if ( aCommand->GetCommandClass() == SKV_COMMAND_CLASS_MULTI_STAGE )
      return status;

    // \todo check if we need locking when going multi-stage! (retreive-side locking isn't considered yet)
    // To be ready, we have the aLocalKV as a function arguments

    aEPState->ReplaceAndInitCommandBuffer( aCommand, aCommandOrdinal );

    return status;
  }

  static inline
  skv_status_t retrieve_n_start( skv_server_ep_state_t *aEPState,
                                 skv_local_kv_t *aLocalKV,
                                 skv_server_ccb_t *aCommand,
                                 int aCommandOrdinal,
                                 skv_cmd_retrieve_n_keys_req_t* aReq,
                                 int *aRetrievedKeysCount,
                                 skv_lmr_triplet_t *aRetrievedKeysSizesSegs,
                                 int *aRetrievedKeysSizesSegsCount )
  {
    // Check if the key is in the buffer
    if( ! aReq->mIsKeyInCtrlMsg )
      return SKV_ERRNO_KEY_TOO_LARGE;

    AssertLogLine( SKV_CLIENT_MAX_CURSOR_KEYS_TO_CACHE == aReq->mKeysDataListMaxCount )
      << "skv_server_retrieve_n_keys_command_sm:: ERROR: "
      << " aRemoteMemKeysMaxCount: " << aReq->mKeysDataListMaxCount
      << " SKV_CLIENT_MAX_CURSOR_KEYS_TO_CACHE: " << SKV_CLIENT_MAX_CURSOR_KEYS_TO_CACHE
      << EndLogLine;

    // Check if the key exists
    skv_local_kv_cookie_t *cookie = &aCommand->mLocalKVCookie;
    cookie->Set( aCommandOrdinal, aEPState );
    skv_status_t status = aLocalKV->RetrieveNKeys( aReq->mPDSId,
                                                   aReq->mStartingKeyData,
                                                   aReq->mStartingKeySize,
                                                   aRetrievedKeysSizesSegs,
                                                   aRetrievedKeysCount,
                                                   aRetrievedKeysSizesSegsCount,
                                                   aReq->mKeysDataListMaxCount,
                                                   aReq->mFlags,
                                                   cookie );

    BegLogLine( SKV_SERVER_RETRIEVE_N_KEYS_COMMAND_SM_LOG )
      << "skv_server_retrieve_n_keys_command_sm:: Results of local call to RetrieveNKeys: "
      << " RetrievedKeysCount: " << *aRetrievedKeysCount
      << " RetrievedKeysSizesSegsCount: " << aRetrievedKeysSizesSegsCount
      << " aRemoteMemKeysMaxCount: " << aReq->mKeysDataListMaxCount
      << " first Key: " << aRetrievedKeysSizesSegs[0].GetAddr()
      << " status: " << skv_status_to_string( status )
      << EndLogLine;

    return status;
  }

  static inline
  skv_status_t post_rdma_write( skv_server_ep_state_t *aEPState,
                                skv_lmr_triplet_t *aRetrievedKeysSizesSegs,
                                int aRetrievedKeysSizesSegsCount,
                                skv_cmd_retrieve_n_keys_req_t* aReq )
  {
    it_status_t itstatus = IT_SUCCESS;

    skv_server_rdma_write_cmpl_cookie_t Cookie;
    Cookie.Init( NULL, NULL );

    //        it_dto_flags_t dto_flags = (it_dto_flags_t) ( IT_COMPLETION_FLAG | IT_NOTIFY_FLAG );
    it_dto_flags_t dto_flags = (it_dto_flags_t) ( 0 );

    BegLogLine( SKV_SERVER_RETRIEVE_N_KEYS_DATA_LOG )
      << "skv_server_retrieve_n_keys_command_sm::post_rdma_write(): " << aRetrievedKeysSizesSegsCount << " LMRs: "
      << " " << aRetrievedKeysSizesSegs[0]
      << " " << aRetrievedKeysSizesSegs[1]
      << " " << aRetrievedKeysSizesSegs[2]
      << " " << aRetrievedKeysSizesSegs[3]
      << EndLogLine;

    BegLogLine( SKV_SERVER_RETRIEVE_N_KEYS_DATA_LOG )
      << "skv_server_retrieve_n_keys_command_sm::post_rdma_write(): first keysize: " << *(int*)(aRetrievedKeysSizesSegs[0].GetAddr())
      << " writeTo: " << aReq->mKeysDataList
      << " KeyData: " << HexDump( (void*)aRetrievedKeysSizesSegs[0].GetAddr(), aRetrievedKeysSizesSegs[0].GetLen() )
      << " " << HexDump( (void*)aRetrievedKeysSizesSegs[1].GetAddr(), aRetrievedKeysSizesSegs[1].GetLen() )
      << " " << HexDump( (void*)aRetrievedKeysSizesSegs[2].GetAddr(), aRetrievedKeysSizesSegs[2].GetLen() )
      << " " << HexDump( (void*)aRetrievedKeysSizesSegs[3].GetAddr(), aRetrievedKeysSizesSegs[3].GetLen() )
      << EndLogLine;

    // rdma_write the value
    itstatus = it_post_rdma_write( aEPState->mEPHdl,
                                   (it_lmr_triplet_t *) aRetrievedKeysSizesSegs,
                                   aRetrievedKeysSizesSegsCount,
                                   Cookie.GetCookie(),
                                   dto_flags,
                                   (it_rdma_addr_t) aReq->mKeysDataList,
                                   aReq->mKeyDataCacheMemReg.mKeysDataCacheRMR );

    AssertLogLine( itstatus == IT_SUCCESS )
      << "skv_server_retrieve_n_keys_command_sm:: ERROR: "
      << " itstatus: " << itstatus
      << EndLogLine;

    return SKV_SUCCESS;
  }

  static inline
  skv_status_t command_completion( skv_status_t aRC,
                                   skv_server_ep_state_t *aEPState,
                                   skv_server_ccb_t *aCommand,
                                   int aRetrievedKeysCount,
                                   skv_lmr_triplet_t *aRetrievedKeysSizesSegs,
                                   skv_cmd_retrieve_n_keys_rdma_write_ack_t *aCmpl,
                                   int aCommandOrdinal,
                                   int *aSeqNo )
  {
    skv_status_t status;

    if( (aRC == SKV_SUCCESS) || (aRC == SKV_ERRNO_END_OF_RECORDS) )
    {
      aCmpl->mHdr.mEvent = SKV_CLIENT_EVENT_RDMA_WRITE_VALUE_ACK;
      aCmpl->mCachedKeysCount = aRetrievedKeysCount;
    }
    else
    {
      aCmpl->mHdr.mEvent = SKV_CLIENT_EVENT_ERROR;
      aCmpl->mCachedKeysCount = 0;
    }
    aCmpl->mStatus          = aRC;

    BegLogLine( SKV_SERVER_RETRIEVE_N_KEYS_COMMAND_SM_LOG )
      << "skv_server_retrieve_n_keys_command_sm:: "
      << " About to Dispatch(): "
      << " status: " << skv_status_to_string( aCmpl->mStatus )
      << " mCachedKeysCount: " << aCmpl->mCachedKeysCount
      << EndLogLine;

    status = aEPState->Dispatch( aCommand,
                                 aSeqNo,
                                 aCommandOrdinal );

    AssertLogLine( status == SKV_SUCCESS )
      << "skv_server_retrieve_n_keys_command_sm:: ERROR: "
      << " status: " << skv_status_to_string( status )
      << EndLogLine;

    delete [] aRetrievedKeysSizesSegs;

    return status;
  }

public:
  static
  skv_status_t
  Execute( skv_local_kv_t *aLocalKV,
           skv_server_ep_state_t *aEPState,
           int aCommandOrdinal,
           skv_server_event_t *aEvent,
           int *aSeqNo,
           it_pz_handle_t aPZHdl )
  {
    skv_status_t status = SKV_SUCCESS;
    skv_server_ccb_t* Command = aEPState->GetCommandForOrdinal( aCommandOrdinal );
    skv_server_command_state_t State = Command->mState;
    skv_server_event_type_t EventType = aEvent->mCmdEventType;

    switch( State )
    {
      case SKV_SERVER_COMMAND_STATE_INIT:
      {
        switch( EventType )
        {
          case SKV_SERVER_EVENT_TYPE_IT_DTO_RETRIEVE_N_KEYS_CMD:
          {
            int RetrievedKeysCount = 0;
            int RetrievedKeysSizesSegsCount = 0;
#define SKV_CLIENT_MAX_CURSOR_KEYS_TO_CACHE_SEND_VEC ( 2 * SKV_CLIENT_MAX_CURSOR_KEYS_TO_CACHE )
            skv_lmr_triplet_t *RetrievedKeysSizesSegs = new skv_lmr_triplet_t[ SKV_CLIENT_MAX_CURSOR_KEYS_TO_CACHE_SEND_VEC ];

            BegLogLine( SKV_SERVER_RETRIEVE_N_KEYS_COMMAND_SM_LOG )
              << "skv_server_retrieve_n_keys_command_sm: allocated " << SKV_CLIENT_MAX_CURSOR_KEYS_TO_CACHE_SEND_VEC
              << " KeysSegs@" << (void*)RetrievedKeysSizesSegs
              << EndLogLine;

            status = retrieve_n_start( aEPState,
                                       aLocalKV,
                                       Command,
                                       aCommandOrdinal,
                                       (skv_cmd_retrieve_n_keys_req_t*) Command->GetSendBuff(),
                                       & RetrievedKeysCount,
                                       RetrievedKeysSizesSegs,
                                       & RetrievedKeysSizesSegsCount );

            switch( status )
            {
              case SKV_ERRNO_END_OF_RECORDS:
                // skip the rdma_write only if there was no key retrieved, otherwise rdma the remaining keys
                if( RetrievedKeysSizesSegsCount == 0 )
                  break;
              case SKV_SUCCESS:
              {
                post_rdma_write( aEPState,
                                 RetrievedKeysSizesSegs,
                                 RetrievedKeysSizesSegsCount,
                                 (skv_cmd_retrieve_n_keys_req_t*) Command->GetSendBuff()
                                 );
                break;
              }
              case SKV_ERRNO_LOCAL_KV_EVENT:
                create_multi_stage( aEPState, aLocalKV, Command, aCommandOrdinal );
                Command->Transit( SKV_SERVER_COMMAND_STATE_LOCAL_KV_DATA_OP );
                return SKV_SUCCESS;
              default:

                break;
            }

            status = command_completion( status,
                                         aEPState,
                                         Command,
                                         RetrievedKeysCount,
                                         RetrievedKeysSizesSegs,
                                         (skv_cmd_retrieve_n_keys_rdma_write_ack_t*)Command->GetSendBuff(),
                                         aCommandOrdinal,
                                         aSeqNo );
            Command->Transit( SKV_SERVER_COMMAND_STATE_INIT );
            break;
          }
          default:
          {
            StrongAssertLogLine( 0 )
              << "skv_server_retrieve_n_keys_command_sm:: Execute():: ERROR: State not recognized"
              << " State: " << State
              << " EventType: " << EventType
              << EndLogLine;

            break;
          }
        }
        break;
      }
      case SKV_SERVER_COMMAND_STATE_LOCAL_KV_DATA_OP:
      {
        switch( EventType )
        {
          case SKV_SERVER_EVENT_TYPE_LOCAL_KV_CMPL:
          {
            status = Command->mLocalKVrc;
            switch( status )
            {
              case SKV_ERRNO_END_OF_RECORDS:
                // skip the rdma_write only if there was no key retrieved
                if( Command->mLocalKVData.mRetrieveNKeys.mKeysCount == 0 )
                  break;
                // no break on purpose: EOR might be signaled even if there were a few keys available?
              case SKV_SUCCESS:
                post_rdma_write( aEPState,
                                 Command->mLocalKVData.mRetrieveNKeys.mKeysSizesSegs,
                                 Command->mLocalKVData.mRetrieveNKeys.mKeysSizesSegsCount,
                                 (skv_cmd_retrieve_n_keys_req_t*) Command->GetSendBuff() );
                break;
            }
            status = command_completion( status,
                                         aEPState,
                                         Command,
                                         Command->mLocalKVData.mRetrieveNKeys.mKeysCount,
                                         Command->mLocalKVData.mRetrieveNKeys.mKeysSizesSegs,
                                         (skv_cmd_retrieve_n_keys_rdma_write_ack_t*)Command->GetSendBuff(),
                                         aCommandOrdinal,
                                         aSeqNo );
            Command->Transit( SKV_SERVER_COMMAND_STATE_INIT );
            break;
          }
          default:
            status = SKV_ERRNO_STATE_MACHINE_ERROR;
        }
        break;
      }
      default:
      {
        StrongAssertLogLine( 0 )
          << "skv_server_retrieve_n_keys_command_sm:: Execute():: ERROR: State not recognized"
          << " State: " << State
          << EndLogLine;

        break;
      }
    }

    return status;
  }
};
#endif
<|endoftext|>


<commit_before>//******************************************************************
//
// Copyright 2016 Samsung Electronics All Rights Reserved.
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

#include <gtest/gtest.h>
#include <HippoMocks/hippomocks.h>
#include <atomic>
#include <functional>
#include <condition_variable>
#include <mutex>
#include <chrono>

#include "ESEnrolleeSimulator.h"
#include "escommon.h"
#include "ESRichCommon.h"
#include "EasySetup.hpp"
#include "RemoteEnrollee.h"

#include "ESEnrolleeCommon.h"
#include "easysetup.h"

#include "ocrandom.h"
#include "cainterface.h"
#include "OCPlatform.h"

using namespace OC;
using namespace OIC::Service;

namespace
{
    std::atomic_bool g_isStartedStack(false);

    std::chrono::milliseconds g_waitForResponse(1000);

    std::condition_variable responseCon;
    std::mutex mutexForCondition;

    ESEnrolleeSimulator g_enrolleeSimul;
    std::shared_ptr<RemoteEnrollee> g_remoteEnrollee;
}

class TestWithMock: public testing::Test
{
public:
    MockRepository mocks;

protected:
    virtual ~TestWithMock() noexcept(noexcept(std::declval<Test>().~Test()))
    {
    }

    virtual void TearDown()
    {
        try
        {
            mocks.VerifyAll();
        }
        catch (...)
        {
            mocks.reset();
            throw;
        }
    }
};

class EasysetupMediatorTest : public TestWithMock
{
public:
    std::shared_ptr<OC::OCResource> m_enrolleeResource;

public:
    EasysetupMediatorTest() = default;
    ~EasysetupMediatorTest() = default;

    std::shared_ptr<OC::OCResource> CreateNotEasySetupResource()
    {
        OCConnectivityType connectivityType = CT_DEFAULT;
        std::vector<std::string> types = {"oic.r.noteasysetup"};
        std::vector<std::string> ifaces = {DEFAULT_INTERFACE};

        return OCPlatform::constructResourceObject("coap://192.168.1.2:5000",
                                                   "/NotEasySetupResURI",
                                                   connectivityType,
                                                   false,
                                                   types,
                                                   ifaces);
    }

    void discoverRemoteEnrollee()
    {
        std::string uri = std::string("/oic/res?rt=") + OC_RSRVD_ES_RES_TYPE_EASYSETUP;
        OC::OCPlatform::findResource("", uri,
                OCConnectivityType::CT_DEFAULT,
                std::bind(&EasysetupMediatorTest::discoverRemoteEnrolleeCb,
                            this, std::placeholders::_1));

        std::unique_lock< std::mutex > lock{ mutexForCondition };
        responseCon.wait_for(lock, g_waitForResponse);
    }

protected:
    void SetUp()
    {
        TestWithMock::SetUp();
        if (g_isStartedStack == false)
        {
            if (OCInit(NULL, 0, OC_CLIENT_SERVER) != OC_STACK_OK)
            {
                printf("OCStack init error!!\n");
                return;
            }
            g_enrolleeSimul.initEnrollee();
            g_isStartedStack = true;
        }
    }

    void TearDown()
    {
        TestWithMock::TearDown();
    }

private:
    bool isValidResourceToTest(std::shared_ptr<OC::OCResource> resource)
    {
        if((resource->connectivityType() & CT_ADAPTER_TCP) == CT_ADAPTER_TCP)
        {
            return false;
        }

        CAEndpoint_t *tempInfo = NULL;
        size_t tempSize = 0;

        CAResult_t res = CAGetNetworkInformation(&tempInfo, &tempSize);
        if (CA_STATUS_OK != res || NULL == tempInfo || 0 == tempSize)
        {
            free(tempInfo);
            return false;
        }

        for (size_t index = 0; index  < tempSize; index++)
        {
            if (CA_ADAPTER_IP == tempInfo[index].adapter)
            {
                if(resource->host().find(tempInfo[index].addr) != std::string::npos &&
                    resource->host().find(std::to_string(tempInfo[index].port).c_str()) != std::string::npos)
                {
                    return true;
                }
            }
        }

        return false;
    }

    void discoverRemoteEnrolleeCb(std::shared_ptr<OC::OCResource> resource)
    {
        if(!isValidResourceToTest(resource))
        {
            return ;
        }

        if(!resource->getResourceTypes().at(0).compare(OC_RSRVD_ES_RES_TYPE_EASYSETUP))
        {
            m_enrolleeResource = resource;
        }
    }
};

TEST_F(EasysetupMediatorTest, createremoteenrolleeFailedWithNotEasySetupResource)
{
    auto remoteEnrollee = EasySetup::getInstance()->createRemoteEnrollee(CreateNotEasySetupResource());

    EXPECT_EQ(nullptr, remoteEnrollee);
}

TEST_F(EasysetupMediatorTest, createremoteenrolleeSucceedWithEasySetupResource)
{
    discoverRemoteEnrollee();
    g_remoteEnrollee = EasySetup::getInstance()->createRemoteEnrollee(m_enrolleeResource);

    ASSERT_NE(nullptr, g_remoteEnrollee);
}

class GetConfigurationTest : public EasysetupMediatorTest
{
public:
    GetConfigurationTest() = default;
    ~GetConfigurationTest() = default;

    static void onGetConfigurationCb(shared_ptr< GetConfigurationStatus > /*status*/)
    {
    }

protected:
    void SetUp()
    {
        TestWithMock::SetUp();
    }

    void TearDown()
    {
        TestWithMock::TearDown();
    }
};

TEST_F(GetConfigurationTest, ThrowExceptionWhenGetConfigurationFailedByCallbackIsNull)
{
    EXPECT_ANY_THROW(g_remoteEnrollee->getConfiguration(nullptr));
}

TEST_F(GetConfigurationTest, GetConfigurationSucceed)
{
    bool isWellConstructed = false;

    g_enrolleeSimul.setDeviceProperty();

    mocks.ExpectCallFunc(onGetConfigurationCb).Do(
        [&isWellConstructed](std::shared_ptr< GetConfigurationStatus > status)
        {
            if(status->getESResult() == ES_OK)
            {
                EnrolleeConf conf = status->getEnrolleeConf();
                if(!conf.getWiFiModes().empty())
                {
                    if(conf.getWiFiModes().at(0) == WIFI_11G &&
                        conf.getWiFiFreq() == WIFI_5G &&
                        !strcmp(conf.getDeviceName().c_str(), "Test Device") &&
                        !strcmp(conf.getModelNumber().c_str(), "Test Model Number"))
                    {
                        isWellConstructed = true;
                    }
                    cout << "getDeviceName : " << conf.getDeviceName().c_str() << endl;
                    cout << "getModelNumber : " << conf.getModelNumber().c_str() << endl;
                }
            }
        });

    g_remoteEnrollee->getConfiguration(onGetConfigurationCb);

    std::unique_lock< std::mutex > lock{ mutexForCondition };
    responseCon.wait_for(lock, g_waitForResponse);

    EXPECT_TRUE(isWellConstructed);
}

class GetStatusTest : public EasysetupMediatorTest
{

public:
    GetStatusTest() = default;
    ~GetStatusTest() = default;

    static void onGetStatusCb(shared_ptr< GetEnrolleeStatus > /*status*/)
    {
    }

protected:
    void SetUp()
    {
        TestWithMock::SetUp();
    }

    void TearDown()
    {
        TestWithMock::TearDown();
    }
};

TEST_F(GetStatusTest, ThrowExceptionWhenGetStatusFailedByCallbackIsNull)
{
    EXPECT_ANY_THROW(g_remoteEnrollee->getStatus(nullptr));
}

TEST_F(GetStatusTest, GetStatusSucceed)
{
    g_enrolleeSimul.setESState();
    g_enrolleeSimul.setESErrorCode();

    bool isWellConstructed = false;
    mocks.ExpectCallFunc(onGetStatusCb).Do(
        [&isWellConstructed](std::shared_ptr< GetEnrolleeStatus > status)
        {
            if(status->getESResult() == ES_OK)
            {
                EnrolleeStatus enrolleeStatus = status->getEnrolleeStatus();

                if(enrolleeStatus.getProvStatus() == ES_STATE_CONNECTED_TO_ENROLLER &&
                   enrolleeStatus.getLastErrCode() == ES_ERRCODE_NO_INTERNETCONNECTION)
                {
                    isWellConstructed = true;
                }
            }
        });

    g_remoteEnrollee->getStatus(onGetStatusCb);

    std::unique_lock< std::mutex > lock{ mutexForCondition };
    responseCon.wait_for(lock, g_waitForResponse);

    EXPECT_TRUE(isWellConstructed);
}

class ProvisionDevicePropertiesTest : public EasysetupMediatorTest
{

public:
    ProvisionDevicePropertiesTest() = default;
    ~ProvisionDevicePropertiesTest() = default;

    static void deviceProvisioningStatusCb(
                                            shared_ptr< DevicePropProvisioningStatus > /*status*/)
    {
    }

protected:
    void SetUp()
    {
        TestWithMock::SetUp();
    }

    void TearDown()
    {
        TestWithMock::TearDown();
    }
};

TEST_F(ProvisionDevicePropertiesTest,
          ThrowExceptionWhenProvisionDeviceProperiesFailedByCallbackIsNull)
{
    DeviceProp devProp;
    devProp.setWiFiProp("Iotivity_SSID", "Iotivity_PWD", WPA2_PSK, TKIP_AES);
    devProp.setDevConfProp("korean", "Korea", "Location");

    EXPECT_ANY_THROW(g_remoteEnrollee->provisionDeviceProperties(devProp, nullptr));
}

TEST_F(ProvisionDevicePropertiesTest,
          ThrowExceptionWhenProvisionDeviceProperiesFailedWithoutSSID)
{
    DeviceProp devProp;
    devProp.setWiFiProp("", "Iotivity_PWD", WPA2_PSK, TKIP_AES);
    devProp.setDevConfProp("korean", "Korea", "Location");
    EXPECT_ANY_THROW(g_remoteEnrollee->provisionDeviceProperties(devProp,
                                                                 deviceProvisioningStatusCb));
}

TEST_F(ProvisionDevicePropertiesTest,
          ProvisionDeviceProperiesSucceed)
{
    DeviceProp devProp;
    devProp.setWiFiProp("Iotivity_SSID", "Iotivity_PWD", WPA2_PSK, TKIP_AES);
    devProp.setDevConfProp("korean", "Korea", "Location");

    int cntForReceivedCallbackWithSuccess = 0;

    mocks.OnCallFunc(deviceProvisioningStatusCb).Do(
        [&cntForReceivedCallbackWithSuccess]
        (std::shared_ptr< DevicePropProvisioningStatus > status)
        {
            if(status->getESResult() == ES_OK)
            {
               cntForReceivedCallbackWithSuccess++;
            }

        });

    g_remoteEnrollee->provisionDeviceProperties(devProp, deviceProvisioningStatusCb);

    std::unique_lock< std::mutex > lock{ mutexForCondition };
    responseCon.wait_for(lock, g_waitForResponse);

    EXPECT_EQ(cntForReceivedCallbackWithSuccess, 1);
}

class ProvisionCloudPropertiesTest : public EasysetupMediatorTest
{

public:
    ProvisionCloudPropertiesTest() = default;
    ~ProvisionCloudPropertiesTest() = default;

    static void cloudPropProvStatusCb(shared_ptr< CloudPropProvisioningStatus > /*status*/)
    {
    }

protected:
    void SetUp()
    {
        TestWithMock::SetUp();
    }

    void TearDown()
    {
        TestWithMock::TearDown();
    }
};

TEST_F(ProvisionCloudPropertiesTest,
          ThrowExceptionWhenProvisionCloudPropertiesFailedByCallbackIsNull)
{
    CloudProp cloudProp;
    cloudProp.setCloudProp("authCode", "authProvider", "ciServer");
    cloudProp.setCloudID("f002ae8b-c42c-40d3-8b8d-1927c17bd1b3");

    EXPECT_ANY_THROW(g_remoteEnrollee->provisionCloudProperties(cloudProp, nullptr));
}

TEST_F(ProvisionCloudPropertiesTest,
          ThrowExceptionWhenProvisionCloudPropertiesFailedWithoutAuthCode)
{
    CloudProp cloudProp;
    cloudProp.setCloudProp("", "authProvider", "ciServer");
    cloudProp.setCloudID("f002ae8b-c42c-40d3-8b8d-1927c17bd1b3");
    EXPECT_ANY_THROW(g_remoteEnrollee->provisionCloudProperties(cloudProp,
                                                                cloudPropProvStatusCb));
}

TEST_F(ProvisionCloudPropertiesTest,
          ThrowExceptionWhenProvisionCloudPropertiesFailedWithoutAuthProvider)
{
    CloudProp cloudProp;
    cloudProp.setCloudProp("authCode", "", "ciServer");
    cloudProp.setCloudID("f002ae8b-c42c-40d3-8b8d-1927c17bd1b3");
    EXPECT_ANY_THROW(g_remoteEnrollee->provisionCloudProperties(cloudProp,
                                                                cloudPropProvStatusCb));
}

TEST_F(ProvisionCloudPropertiesTest,
          ThrowExceptionWhenProvisionCloudPropertiesFailedWithoutCIServer)
{
    CloudProp cloudProp;
    cloudProp.setCloudProp("authCode", "authProvider", "");
    cloudProp.setCloudID("f002ae8b-c42c-40d3-8b8d-1927c17bd1b3");
    EXPECT_ANY_THROW(g_remoteEnrollee->provisionCloudProperties(cloudProp,
                                                                cloudPropProvStatusCb));
}

TEST_F(ProvisionCloudPropertiesTest, ProvisionCloudPropertiesSucceed)
{
    CloudProp cloudProp;
    cloudProp.setCloudProp("authCode", "authProvider", "ciServer");
    cloudProp.setCloudID("f002ae8b-c42c-40d3-8b8d-1927c17bd1b3");

    int cntForReceivedCallbackWithSuccess = 0;

    mocks.OnCallFunc(cloudPropProvStatusCb).Do(
        [& cntForReceivedCallbackWithSuccess](std::shared_ptr< CloudPropProvisioningStatus > status)
        {
            if(status->getESResult() == ES_OK)
            {
               cntForReceivedCallbackWithSuccess++;
            }
        });

    g_remoteEnrollee->provisionCloudProperties(cloudProp, cloudPropProvStatusCb);

    std::unique_lock< std::mutex > lock{ mutexForCondition };
    responseCon.wait_for(lock, g_waitForResponse);

    EXPECT_EQ(cntForReceivedCallbackWithSuccess, 1);

    ESTerminateEnrollee();
}
<commit_msg>Removed easy-setup TC associated with  https://gerrit.iotivity.org/gerrit/#/c/17995/<commit_after>//******************************************************************
//
// Copyright 2016 Samsung Electronics All Rights Reserved.
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

#include <gtest/gtest.h>
#include <HippoMocks/hippomocks.h>
#include <atomic>
#include <functional>
#include <condition_variable>
#include <mutex>
#include <chrono>

#include "ESEnrolleeSimulator.h"
#include "escommon.h"
#include "ESRichCommon.h"
#include "EasySetup.hpp"
#include "RemoteEnrollee.h"

#include "ESEnrolleeCommon.h"
#include "easysetup.h"

#include "ocrandom.h"
#include "cainterface.h"
#include "OCPlatform.h"

using namespace OC;
using namespace OIC::Service;

namespace
{
    std::atomic_bool g_isStartedStack(false);

    std::chrono::milliseconds g_waitForResponse(1000);

    std::condition_variable responseCon;
    std::mutex mutexForCondition;

    ESEnrolleeSimulator g_enrolleeSimul;
    std::shared_ptr<RemoteEnrollee> g_remoteEnrollee;
}

class TestWithMock: public testing::Test
{
public:
    MockRepository mocks;

protected:
    virtual ~TestWithMock() noexcept(noexcept(std::declval<Test>().~Test()))
    {
    }

    virtual void TearDown()
    {
        try
        {
            mocks.VerifyAll();
        }
        catch (...)
        {
            mocks.reset();
            throw;
        }
    }
};

class EasysetupMediatorTest : public TestWithMock
{
public:
    std::shared_ptr<OC::OCResource> m_enrolleeResource;

public:
    EasysetupMediatorTest() = default;
    ~EasysetupMediatorTest() = default;

    std::shared_ptr<OC::OCResource> CreateNotEasySetupResource()
    {
        OCConnectivityType connectivityType = CT_DEFAULT;
        std::vector<std::string> types = {"oic.r.noteasysetup"};
        std::vector<std::string> ifaces = {DEFAULT_INTERFACE};

        return OCPlatform::constructResourceObject("coap://192.168.1.2:5000",
                                                   "/NotEasySetupResURI",
                                                   connectivityType,
                                                   false,
                                                   types,
                                                   ifaces);
    }

    void discoverRemoteEnrollee()
    {
        std::string uri = std::string("/oic/res?rt=") + OC_RSRVD_ES_RES_TYPE_EASYSETUP;
        OC::OCPlatform::findResource("", uri,
                OCConnectivityType::CT_DEFAULT,
                std::bind(&EasysetupMediatorTest::discoverRemoteEnrolleeCb,
                            this, std::placeholders::_1));

        std::unique_lock< std::mutex > lock{ mutexForCondition };
        responseCon.wait_for(lock, g_waitForResponse);
    }

protected:
    void SetUp()
    {
        TestWithMock::SetUp();
        if (g_isStartedStack == false)
        {
            if (OCInit(NULL, 0, OC_CLIENT_SERVER) != OC_STACK_OK)
            {
                printf("OCStack init error!!\n");
                return;
            }
            g_enrolleeSimul.initEnrollee();
            g_isStartedStack = true;
        }
    }

    void TearDown()
    {
        TestWithMock::TearDown();
    }

private:
    bool isValidResourceToTest(std::shared_ptr<OC::OCResource> resource)
    {
        if((resource->connectivityType() & CT_ADAPTER_TCP) == CT_ADAPTER_TCP)
        {
            return false;
        }

        CAEndpoint_t *tempInfo = NULL;
        size_t tempSize = 0;

        CAResult_t res = CAGetNetworkInformation(&tempInfo, &tempSize);
        if (CA_STATUS_OK != res || NULL == tempInfo || 0 == tempSize)
        {
            free(tempInfo);
            return false;
        }

        for (size_t index = 0; index  < tempSize; index++)
        {
            if (CA_ADAPTER_IP == tempInfo[index].adapter)
            {
                if(resource->host().find(tempInfo[index].addr) != std::string::npos &&
                    resource->host().find(std::to_string(tempInfo[index].port).c_str()) != std::string::npos)
                {
                    return true;
                }
            }
        }

        return false;
    }

    void discoverRemoteEnrolleeCb(std::shared_ptr<OC::OCResource> resource)
    {
        if(!isValidResourceToTest(resource))
        {
            return ;
        }

        if(!resource->getResourceTypes().at(0).compare(OC_RSRVD_ES_RES_TYPE_EASYSETUP))
        {
            m_enrolleeResource = resource;
        }
    }
};

TEST_F(EasysetupMediatorTest, createremoteenrolleeFailedWithNotEasySetupResource)
{
    auto remoteEnrollee = EasySetup::getInstance()->createRemoteEnrollee(CreateNotEasySetupResource());

    EXPECT_EQ(nullptr, remoteEnrollee);
}

TEST_F(EasysetupMediatorTest, createremoteenrolleeSucceedWithEasySetupResource)
{
    discoverRemoteEnrollee();
    g_remoteEnrollee = EasySetup::getInstance()->createRemoteEnrollee(m_enrolleeResource);

    ASSERT_NE(nullptr, g_remoteEnrollee);
}

class GetConfigurationTest : public EasysetupMediatorTest
{
public:
    GetConfigurationTest() = default;
    ~GetConfigurationTest() = default;

    static void onGetConfigurationCb(shared_ptr< GetConfigurationStatus > /*status*/)
    {
    }

protected:
    void SetUp()
    {
        TestWithMock::SetUp();
    }

    void TearDown()
    {
        TestWithMock::TearDown();
    }
};

TEST_F(GetConfigurationTest, ThrowExceptionWhenGetConfigurationFailedByCallbackIsNull)
{
    EXPECT_ANY_THROW(g_remoteEnrollee->getConfiguration(nullptr));
}

TEST_F(GetConfigurationTest, GetConfigurationSucceed)
{
    bool isWellConstructed = false;

    g_enrolleeSimul.setDeviceProperty();

    mocks.ExpectCallFunc(onGetConfigurationCb).Do(
        [&isWellConstructed](std::shared_ptr< GetConfigurationStatus > status)
        {
            if(status->getESResult() == ES_OK)
            {
                EnrolleeConf conf = status->getEnrolleeConf();
                if(!conf.getWiFiModes().empty())
                {
                    if(conf.getWiFiModes().at(0) == WIFI_11G &&
                        conf.getWiFiFreq() == WIFI_5G &&
                        !strcmp(conf.getDeviceName().c_str(), "Test Device") &&
                        !strcmp(conf.getModelNumber().c_str(), "Test Model Number"))
                    {
                        isWellConstructed = true;
                    }
                    cout << "getDeviceName : " << conf.getDeviceName().c_str() << endl;
                    cout << "getModelNumber : " << conf.getModelNumber().c_str() << endl;
                }
            }
        });

    g_remoteEnrollee->getConfiguration(onGetConfigurationCb);

    std::unique_lock< std::mutex > lock{ mutexForCondition };
    responseCon.wait_for(lock, g_waitForResponse);

    EXPECT_TRUE(isWellConstructed);
}

class GetStatusTest : public EasysetupMediatorTest
{

public:
    GetStatusTest() = default;
    ~GetStatusTest() = default;

    static void onGetStatusCb(shared_ptr< GetEnrolleeStatus > /*status*/)
    {
    }

protected:
    void SetUp()
    {
        TestWithMock::SetUp();
    }

    void TearDown()
    {
        TestWithMock::TearDown();
    }
};

TEST_F(GetStatusTest, ThrowExceptionWhenGetStatusFailedByCallbackIsNull)
{
    EXPECT_ANY_THROW(g_remoteEnrollee->getStatus(nullptr));
}

TEST_F(GetStatusTest, GetStatusSucceed)
{
    g_enrolleeSimul.setESState();
    g_enrolleeSimul.setESErrorCode();

    bool isWellConstructed = false;
    mocks.ExpectCallFunc(onGetStatusCb).Do(
        [&isWellConstructed](std::shared_ptr< GetEnrolleeStatus > status)
        {
            if(status->getESResult() == ES_OK)
            {
                EnrolleeStatus enrolleeStatus = status->getEnrolleeStatus();

                if(enrolleeStatus.getProvStatus() == ES_STATE_CONNECTED_TO_ENROLLER &&
                   enrolleeStatus.getLastErrCode() == ES_ERRCODE_NO_INTERNETCONNECTION)
                {
                    isWellConstructed = true;
                }
            }
        });

    g_remoteEnrollee->getStatus(onGetStatusCb);

    std::unique_lock< std::mutex > lock{ mutexForCondition };
    responseCon.wait_for(lock, g_waitForResponse);

    EXPECT_TRUE(isWellConstructed);
}

class ProvisionDevicePropertiesTest : public EasysetupMediatorTest
{

public:
    ProvisionDevicePropertiesTest() = default;
    ~ProvisionDevicePropertiesTest() = default;

    static void deviceProvisioningStatusCb(
                                            shared_ptr< DevicePropProvisioningStatus > /*status*/)
    {
    }

protected:
    void SetUp()
    {
        TestWithMock::SetUp();
    }

    void TearDown()
    {
        TestWithMock::TearDown();
    }
};

TEST_F(ProvisionDevicePropertiesTest,
          ThrowExceptionWhenProvisionDeviceProperiesFailedByCallbackIsNull)
{
    DeviceProp devProp;
    devProp.setWiFiProp("Iotivity_SSID", "Iotivity_PWD", WPA2_PSK, TKIP_AES);
    devProp.setDevConfProp("korean", "Korea", "Location");

    EXPECT_ANY_THROW(g_remoteEnrollee->provisionDeviceProperties(devProp, nullptr));
}

TEST_F(ProvisionDevicePropertiesTest,
          ProvisionDeviceProperiesSucceed)
{
    DeviceProp devProp;
    devProp.setWiFiProp("Iotivity_SSID", "Iotivity_PWD", WPA2_PSK, TKIP_AES);
    devProp.setDevConfProp("korean", "Korea", "Location");

    int cntForReceivedCallbackWithSuccess = 0;

    mocks.OnCallFunc(deviceProvisioningStatusCb).Do(
        [&cntForReceivedCallbackWithSuccess]
        (std::shared_ptr< DevicePropProvisioningStatus > status)
        {
            if(status->getESResult() == ES_OK)
            {
               cntForReceivedCallbackWithSuccess++;
            }

        });

    g_remoteEnrollee->provisionDeviceProperties(devProp, deviceProvisioningStatusCb);

    std::unique_lock< std::mutex > lock{ mutexForCondition };
    responseCon.wait_for(lock, g_waitForResponse);

    EXPECT_EQ(cntForReceivedCallbackWithSuccess, 1);
}

class ProvisionCloudPropertiesTest : public EasysetupMediatorTest
{

public:
    ProvisionCloudPropertiesTest() = default;
    ~ProvisionCloudPropertiesTest() = default;

    static void cloudPropProvStatusCb(shared_ptr< CloudPropProvisioningStatus > /*status*/)
    {
    }

protected:
    void SetUp()
    {
        TestWithMock::SetUp();
    }

    void TearDown()
    {
        TestWithMock::TearDown();
    }
};

TEST_F(ProvisionCloudPropertiesTest,
          ThrowExceptionWhenProvisionCloudPropertiesFailedByCallbackIsNull)
{
    CloudProp cloudProp;
    cloudProp.setCloudProp("authCode", "authProvider", "ciServer");
    cloudProp.setCloudID("f002ae8b-c42c-40d3-8b8d-1927c17bd1b3");

    EXPECT_ANY_THROW(g_remoteEnrollee->provisionCloudProperties(cloudProp, nullptr));
}

TEST_F(ProvisionCloudPropertiesTest,
          ThrowExceptionWhenProvisionCloudPropertiesFailedWithoutAuthCode)
{
    CloudProp cloudProp;
    cloudProp.setCloudProp("", "authProvider", "ciServer");
    cloudProp.setCloudID("f002ae8b-c42c-40d3-8b8d-1927c17bd1b3");
    EXPECT_ANY_THROW(g_remoteEnrollee->provisionCloudProperties(cloudProp,
                                                                cloudPropProvStatusCb));
}

TEST_F(ProvisionCloudPropertiesTest,
          ThrowExceptionWhenProvisionCloudPropertiesFailedWithoutAuthProvider)
{
    CloudProp cloudProp;
    cloudProp.setCloudProp("authCode", "", "ciServer");
    cloudProp.setCloudID("f002ae8b-c42c-40d3-8b8d-1927c17bd1b3");
    EXPECT_ANY_THROW(g_remoteEnrollee->provisionCloudProperties(cloudProp,
                                                                cloudPropProvStatusCb));
}

TEST_F(ProvisionCloudPropertiesTest,
          ThrowExceptionWhenProvisionCloudPropertiesFailedWithoutCIServer)
{
    CloudProp cloudProp;
    cloudProp.setCloudProp("authCode", "authProvider", "");
    cloudProp.setCloudID("f002ae8b-c42c-40d3-8b8d-1927c17bd1b3");
    EXPECT_ANY_THROW(g_remoteEnrollee->provisionCloudProperties(cloudProp,
                                                                cloudPropProvStatusCb));
}

TEST_F(ProvisionCloudPropertiesTest, ProvisionCloudPropertiesSucceed)
{
    CloudProp cloudProp;
    cloudProp.setCloudProp("authCode", "authProvider", "ciServer");
    cloudProp.setCloudID("f002ae8b-c42c-40d3-8b8d-1927c17bd1b3");

    int cntForReceivedCallbackWithSuccess = 0;

    mocks.OnCallFunc(cloudPropProvStatusCb).Do(
        [& cntForReceivedCallbackWithSuccess](std::shared_ptr< CloudPropProvisioningStatus > status)
        {
            if(status->getESResult() == ES_OK)
            {
               cntForReceivedCallbackWithSuccess++;
            }
        });

    g_remoteEnrollee->provisionCloudProperties(cloudProp, cloudPropProvStatusCb);

    std::unique_lock< std::mutex > lock{ mutexForCondition };
    responseCon.wait_for(lock, g_waitForResponse);

    EXPECT_EQ(cntForReceivedCallbackWithSuccess, 1);

    ESTerminateEnrollee();
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// CLASS HEADERS
#include <dali/internal/render/renderers/render-renderer-property-buffer.h>

// INTERNAL INCLUDES
#include <dali/internal/render/gl-resources/gpu-buffer.h>
#include <dali/internal/render/shaders/program.h>

namespace Dali
{
namespace Internal
{
namespace SceneGraph
{

RenderPropertyBuffer::RenderPropertyBuffer( const PropertyBufferDataProvider& propertyBufferDataProvider,
                                            GpuBuffer::Target gpuBufferTarget,
                                            GpuBuffer::Usage gpuBufferUsage )
: mDataProvider( propertyBufferDataProvider ),
  mGpuBuffer( NULL ),
  mGpuBufferTarget( gpuBufferTarget ),
  mGpuBufferUsage( gpuBufferUsage )
{
}

RenderPropertyBuffer::~RenderPropertyBuffer()
{
}

void RenderPropertyBuffer::Upload( Context& context, BufferIndex bufferIndex )
{
  bool hasGpuBuffer = NULL != mGpuBuffer;

  // Check if we have a gpu-buffer
  unsigned int gpuBufferId = 0; // TODO: MESH_REWORK FIX THIS  mDataProvider.GetGpuBufferId( bufferIndex );
  if ( ! hasGpuBuffer )
  {
    // TODO: MESH_REWORK
//    mGpuBuffer /*= gpuBufferCache.GetGpuBuffer( gpuBufferId ) */;
    (void )gpuBufferId;

    mGpuBuffer = new GpuBuffer( context, mGpuBufferTarget, mGpuBufferUsage );
  }

  // Update the GpuBuffer
  if ( ! hasGpuBuffer || mDataProvider.HasDataChanged( bufferIndex ) )
  {
    std::size_t dataSize = mDataProvider.GetDataSize( bufferIndex );

    const void *data = &(mDataProvider.GetData( bufferIndex )[0]);

    // Index buffer needs to be unsigned short which is not supported by the property system
    if( mGpuBufferTarget == GpuBuffer::ELEMENT_ARRAY_BUFFER )
    {
      Vector<unsigned short> ushortData;
      ushortData.Resize( dataSize );
      const unsigned int* unsignedData = static_cast<const unsigned int*>(data);
      for( unsigned int i = 0; i < dataSize; ++i )
      {
        ushortData[i] = unsignedData[i];
      }
      data = &(ushortData[0]);
    }

    mGpuBuffer->UpdateDataBuffer( dataSize, data );
    mGpuBuffer->SetStride( mDataProvider.GetElementSize( bufferIndex ) );
  }
}

void RenderPropertyBuffer::BindBuffer( Context& context, Program& progam )
{
  mGpuBuffer->Bind();
}

void RenderPropertyBuffer::EnableVertexAttributes( Context& context, BufferIndex bufferIndex, Program& program )
{

  // Check if attribute locations are cached already
  if( mAttributesLocation.Size() == 0 )
  {
    UpdateAttributeLocations( context, bufferIndex, program );
  }

  unsigned int attributeCount = mDataProvider.GetAttributeCount( bufferIndex );
  DALI_ASSERT_DEBUG( attributeCount == mAttributesLocation.Size() && "Incorrect number of attributes!" );

  GLsizei elementSize = mDataProvider.GetElementSize( bufferIndex );

  for( unsigned int i = 0; i < attributeCount; ++i )
  {
    GLint attributeLocation = mAttributesLocation[i];
    if( attributeLocation != -1 )
    {
      context.EnableVertexAttributeArray( attributeLocation );

      GLint attributeSize = mDataProvider.GetAttributeSize( bufferIndex, i );
      size_t attributeOffset = mDataProvider.GetAttributeOffset( bufferIndex, i );

      // TODO: MESH_REWORK  Matrices need multiple calls to this function
      context.VertexAttribPointer( attributeLocation,
                                   attributeSize  / sizeof(float), // TODO: MESH_REWORK get the correct type
                                   GL_FLOAT, // TODO: MESH_REWORK get the correct type
                                   GL_FALSE,  // Not normalized
                                   elementSize,
                                   (void*)attributeOffset );
    }
  }
}

void RenderPropertyBuffer::DisableVertexAttributes( Context& context, BufferIndex bufferIndex, Program& program )
{
  unsigned int attributeCount = mDataProvider.GetAttributeCount( bufferIndex );
  for( unsigned int i = 0; i < attributeCount; ++i )
  {
    GLint attributeLocation = mAttributesLocation[i];
    if( attributeLocation != -1 )
    {
      context.DisableVertexAttributeArray( attributeLocation );
    }
  }
}

void RenderPropertyBuffer::UpdateAttributeLocations( Context& context, BufferIndex bufferIndex, Program& program )
{
  unsigned int attributeCount = mDataProvider.GetAttributeCount( bufferIndex );
  mAttributesLocation.Resize( attributeCount );

  for( unsigned int i = 0; i < attributeCount; ++i )
  {
    const std::string& attributeName = mDataProvider.GetAttributeName( bufferIndex, i );
    unsigned int index = program.RegisterCustomAttribute( attributeName );
    GLint attributeLocation = program.GetCustomAttributeLocation( index );

    if( -1 == attributeLocation )
    {
      DALI_LOG_WARNING( "Attribute not found in the shader: %s\n", attributeName.c_str() );
    }
    mAttributesLocation[i] = attributeLocation;
  }
}

} // namespace SceneGraph
} // namespace Internal
} // namespace Dali
<commit_msg>Fix buffer used in the wrong scope.<commit_after>/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// CLASS HEADERS
#include <dali/internal/render/renderers/render-renderer-property-buffer.h>

// INTERNAL INCLUDES
#include <dali/internal/render/gl-resources/gpu-buffer.h>
#include <dali/internal/render/shaders/program.h>

namespace Dali
{
namespace Internal
{
namespace SceneGraph
{

RenderPropertyBuffer::RenderPropertyBuffer( const PropertyBufferDataProvider& propertyBufferDataProvider,
                                            GpuBuffer::Target gpuBufferTarget,
                                            GpuBuffer::Usage gpuBufferUsage )
: mDataProvider( propertyBufferDataProvider ),
  mGpuBuffer( NULL ),
  mGpuBufferTarget( gpuBufferTarget ),
  mGpuBufferUsage( gpuBufferUsage )
{
}

RenderPropertyBuffer::~RenderPropertyBuffer()
{
}

void RenderPropertyBuffer::Upload( Context& context, BufferIndex bufferIndex )
{
  bool hasGpuBuffer = NULL != mGpuBuffer;

  // Check if we have a gpu-buffer
  unsigned int gpuBufferId = 0; // TODO: MESH_REWORK FIX THIS  mDataProvider.GetGpuBufferId( bufferIndex );
  if ( ! hasGpuBuffer )
  {
    // TODO: MESH_REWORK
//    mGpuBuffer /*= gpuBufferCache.GetGpuBuffer( gpuBufferId ) */;
    (void )gpuBufferId;

    mGpuBuffer = new GpuBuffer( context, mGpuBufferTarget, mGpuBufferUsage );
  }

  // Update the GpuBuffer
  if ( ! hasGpuBuffer || mDataProvider.HasDataChanged( bufferIndex ) )
  {
    std::size_t dataSize = mDataProvider.GetDataSize( bufferIndex );

    const void *data = &(mDataProvider.GetData( bufferIndex )[0]);
    Vector<unsigned short> ushortData;

    // Index buffer needs to be unsigned short which is not supported by the property system
    if( mGpuBufferTarget == GpuBuffer::ELEMENT_ARRAY_BUFFER )
    {
      ushortData.Resize( dataSize );
      const unsigned int* unsignedData = static_cast<const unsigned int*>(data);
      for( unsigned int i = 0; i < dataSize; ++i )
      {
        ushortData[i] = unsignedData[i];
      }
      data = &(ushortData[0]);
    }

    mGpuBuffer->UpdateDataBuffer( dataSize, data );
    mGpuBuffer->SetStride( mDataProvider.GetElementSize( bufferIndex ) );
  }
}

void RenderPropertyBuffer::BindBuffer( Context& context, Program& progam )
{
  mGpuBuffer->Bind();
}

void RenderPropertyBuffer::EnableVertexAttributes( Context& context, BufferIndex bufferIndex, Program& program )
{

  // Check if attribute locations are cached already
  if( mAttributesLocation.Size() == 0 )
  {
    UpdateAttributeLocations( context, bufferIndex, program );
  }

  unsigned int attributeCount = mDataProvider.GetAttributeCount( bufferIndex );
  DALI_ASSERT_DEBUG( attributeCount == mAttributesLocation.Size() && "Incorrect number of attributes!" );

  GLsizei elementSize = mDataProvider.GetElementSize( bufferIndex );

  for( unsigned int i = 0; i < attributeCount; ++i )
  {
    GLint attributeLocation = mAttributesLocation[i];
    if( attributeLocation != -1 )
    {
      context.EnableVertexAttributeArray( attributeLocation );

      GLint attributeSize = mDataProvider.GetAttributeSize( bufferIndex, i );
      size_t attributeOffset = mDataProvider.GetAttributeOffset( bufferIndex, i );

      // TODO: MESH_REWORK  Matrices need multiple calls to this function
      context.VertexAttribPointer( attributeLocation,
                                   attributeSize  / sizeof(float), // TODO: MESH_REWORK get the correct type
                                   GL_FLOAT, // TODO: MESH_REWORK get the correct type
                                   GL_FALSE,  // Not normalized
                                   elementSize,
                                   (void*)attributeOffset );
    }
  }
}

void RenderPropertyBuffer::DisableVertexAttributes( Context& context, BufferIndex bufferIndex, Program& program )
{
  unsigned int attributeCount = mDataProvider.GetAttributeCount( bufferIndex );
  for( unsigned int i = 0; i < attributeCount; ++i )
  {
    GLint attributeLocation = mAttributesLocation[i];
    if( attributeLocation != -1 )
    {
      context.DisableVertexAttributeArray( attributeLocation );
    }
  }
}

void RenderPropertyBuffer::UpdateAttributeLocations( Context& context, BufferIndex bufferIndex, Program& program )
{
  unsigned int attributeCount = mDataProvider.GetAttributeCount( bufferIndex );
  mAttributesLocation.Resize( attributeCount );

  for( unsigned int i = 0; i < attributeCount; ++i )
  {
    const std::string& attributeName = mDataProvider.GetAttributeName( bufferIndex, i );
    unsigned int index = program.RegisterCustomAttribute( attributeName );
    GLint attributeLocation = program.GetCustomAttributeLocation( index );

    if( -1 == attributeLocation )
    {
      DALI_LOG_WARNING( "Attribute not found in the shader: %s\n", attributeName.c_str() );
    }
    mAttributesLocation[i] = attributeLocation;
  }
}

} // namespace SceneGraph
} // namespace Internal
} // namespace Dali
<|endoftext|>