// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "bsdiff/suffix_array_index.h"

#include <stdlib.h>

#include <algorithm>
#include <string>
#include <utility>
#include <vector>

#include <gtest/gtest.h>

namespace bsdiff {

class SuffixArrayIndexTest : public ::testing::Test {
 protected:
  void InitSA(const std::string& text) {
    text_.resize(text.size());
    std::copy(text.begin(), text.end(), text_.data());
    sai_ = CreateSuffixArrayIndex(text_.data(), text_.size());
  }

  void SearchPrefix(const std::string& pattern,
                    size_t* out_length,
                    uint64_t* out_pos) {
    sai_->SearchPrefix(reinterpret_cast<const uint8_t*>(pattern.data()),
                       pattern.size(), out_length, out_pos);
    // Check that the returned values are indeed a valid prefix.
    EXPECT_LE(*out_length, pattern.size());
    ASSERT_LT(*out_pos, text_.size());
    ASSERT_LE(*out_pos + *out_length, text_.size());
    EXPECT_EQ(0, memcmp(text_.data() + *out_pos, pattern.data(), *out_length));
  }

  std::vector<uint8_t> text_;
  std::unique_ptr<SuffixArrayIndexInterface> sai_;
};

// Test that the suffix array index can be initialized with an example text.
TEST_F(SuffixArrayIndexTest, MississippiTest) {
  InitSA("mississippi");
}

TEST_F(SuffixArrayIndexTest, EmptySuffixArrayTest) {
  InitSA("");
}

// Test various corner cases while searching for prefixes.
TEST_F(SuffixArrayIndexTest, SearchPrefixTest) {
  InitSA("abc1_abc2_abc3_ab_abcd");

  uint64_t pos;
  size_t length;
  // The string is not found at all.
  SearchPrefix("zzz", &length, &pos);  // lexicographically highest.
  EXPECT_EQ(0U, length);

  SearchPrefix("   ", &length, &pos);  // lexicographically lowest.
  EXPECT_EQ(0U, length);

  // The exact pattern is found multiple times.
  SearchPrefix("abc", &length, &pos);
  EXPECT_EQ(3U, length);

  // The exact pattern is found only one time, at the end of the string.
  SearchPrefix("abcd", &length, &pos);
  EXPECT_EQ(4U, length);
  EXPECT_EQ(18U, pos);

  // The string is not found, but a prefix is found.
  SearchPrefix("abcW", &length, &pos);
  EXPECT_EQ(3U, length);
}

}  // namespace bsdiff