//
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// span_unittests.cpp: Unit tests for the TSpan class.
//

#include "Common.h"

#include <gtest/gtest.h>

using namespace angle;

namespace
{

using Span                                      = sh::TSpan<const unsigned int>;
constexpr size_t kSpanDataSize                  = 16;
constexpr unsigned int kSpanData[kSpanDataSize] = {0, 1, 2,  3,  4,  5,  6,  7,
                                                   8, 9, 10, 11, 12, 13, 14, 15};

// Test that comparing spans work
TEST(SpanTest, Comparison)
{
    // Duplicate data to make sure comparison is being done on values (and not addresses).
    constexpr unsigned int kSpanDataDup[kSpanDataSize] = {0, 1, 2,  3,  4,  5,  6,  7,
                                                          8, 9, 10, 11, 12, 13, 14, 15};

    // Don't use ASSERT_EQ at first because the == is more hidden
    ASSERT_TRUE(Span() == Span(kSpanData, 0));
    ASSERT_TRUE(Span(kSpanData + 3, 4) != Span(kSpanDataDup + 5, 4));

    // Check ASSERT_EQ and ASSERT_NE work correctly
    ASSERT_EQ(Span(kSpanData, kSpanDataSize), Span(kSpanDataDup, kSpanDataSize));
    ASSERT_NE(Span(kSpanData, kSpanDataSize - 1), Span(kSpanDataDup + 1, kSpanDataSize - 1));
    ASSERT_NE(Span(kSpanData, kSpanDataSize), Span(kSpanDataDup, kSpanDataSize - 1));
    ASSERT_NE(Span(kSpanData, kSpanDataSize - 1), Span(kSpanDataDup, kSpanDataSize));
    ASSERT_NE(Span(kSpanData, 0), Span(kSpanDataDup, 1));
    ASSERT_NE(Span(kSpanData, 1), Span(kSpanDataDup, 0));
}

// Test indexing
TEST(SpanTest, Indexing)
{
    constexpr Span sp(kSpanData, kSpanDataSize);

    for (size_t i = 0; i < kSpanDataSize; ++i)
    {
        ASSERT_EQ(sp[i], i);
    }

    unsigned int storage[kSpanDataSize] = {};
    sh::TSpan<unsigned int> writableSpan(storage, kSpanDataSize);

    for (size_t i = 0; i < kSpanDataSize; ++i)
    {
        writableSpan[i] = i;
    }
    for (size_t i = 0; i < kSpanDataSize; ++i)
    {
        ASSERT_EQ(writableSpan[i], i);
    }
    for (size_t i = 0; i < kSpanDataSize; ++i)
    {
        ASSERT_EQ(storage[i], i);
    }
}

// Test for the various constructors
TEST(SpanTest, Constructors)
{
    // Default constructor
    {
        Span sp;
        ASSERT_TRUE(sp.size() == 0);
        ASSERT_TRUE(sp.empty());
    }

    // Constexpr construct from pointer
    {
        constexpr Span sp(kSpanData, kSpanDataSize);
        ASSERT_EQ(sp.data(), kSpanData);
        ASSERT_EQ(sp.size(), kSpanDataSize);
        ASSERT_FALSE(sp.empty());
    }

    // Copy constructor and copy assignment
    {
        Span sp(kSpanData, kSpanDataSize);
        Span sp2(sp);
        Span sp3;

        ASSERT_EQ(sp, sp2);
        ASSERT_EQ(sp2.data(), kSpanData);
        ASSERT_EQ(sp2.size(), kSpanDataSize);
        ASSERT_FALSE(sp2.empty());

        sp3 = sp;

        ASSERT_EQ(sp, sp3);
        ASSERT_EQ(sp3.data(), kSpanData);
        ASSERT_EQ(sp3.size(), kSpanDataSize);
        ASSERT_FALSE(sp3.empty());
    }
}

// Test accessing the data directly
TEST(SpanTest, DataAccess)
{
    constexpr Span sp(kSpanData, kSpanDataSize);
    const unsigned int *data = sp.data();

    for (size_t i = 0; i < kSpanDataSize; ++i)
    {
        ASSERT_EQ(data[i], i);
    }
}

// Test front and back
TEST(SpanTest, FrontAndBack)
{
    constexpr Span sp(kSpanData, kSpanDataSize);
    ASSERT_TRUE(sp.front() == 0);
    ASSERT_EQ(sp.back(), kSpanDataSize - 1);
}

// Test begin and end
TEST(SpanTest, BeginAndEnd)
{
    constexpr Span sp(kSpanData, kSpanDataSize);

    size_t currentIndex = 0;
    for (unsigned int value : sp)
    {
        ASSERT_EQ(value, currentIndex);
        ++currentIndex;
    }
}

// Test reverse begin and end
TEST(SpanTest, RbeginAndRend)
{
    constexpr Span sp(kSpanData, kSpanDataSize);

    size_t currentIndex = 0;
    for (auto iter = sp.rbegin(); iter != sp.rend(); ++iter)
    {
        ASSERT_EQ(*iter, kSpanDataSize - 1 - currentIndex);
        ++currentIndex;
    }
}

// Test first and last
TEST(SpanTest, FirstAndLast)
{
    constexpr Span sp(kSpanData, kSpanDataSize);
    constexpr size_t kSplitSize = kSpanDataSize / 4;
    constexpr Span first        = sp.first(kSplitSize);
    constexpr Span last         = sp.last(kSplitSize);

    ASSERT_EQ(first, Span(kSpanData, kSplitSize));
    ASSERT_EQ(first.data(), kSpanData);
    ASSERT_EQ(first.size(), kSplitSize);

    ASSERT_EQ(last, Span(kSpanData + kSpanDataSize - kSplitSize, kSplitSize));
    ASSERT_EQ(last.data(), kSpanData + kSpanDataSize - kSplitSize);
    ASSERT_EQ(last.size(), kSplitSize);
}

// Test subspan
TEST(SpanTest, Subspan)
{
    constexpr Span sp(kSpanData, kSpanDataSize);
    constexpr size_t kSplitOffset = kSpanDataSize / 4;
    constexpr size_t kSplitSize   = kSpanDataSize / 2;
    constexpr Span subspan        = sp.subspan(kSplitOffset, kSplitSize);

    ASSERT_EQ(subspan, Span(kSpanData + kSplitOffset, kSplitSize));
    ASSERT_EQ(subspan.data(), kSpanData + kSplitOffset);
    ASSERT_EQ(subspan.size(), kSplitSize);
}

}  // anonymous namespace
