//===-- sanitizer_list_test.cc --------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
//
//===----------------------------------------------------------------------===//
#include "sanitizer_common/sanitizer_list.h"
#include "gtest/gtest.h"

namespace __sanitizer {

struct ListItem {
  ListItem *next;
};

typedef IntrusiveList<ListItem> List;

static List static_list;

static void SetList(List *l, ListItem *x = 0,
                    ListItem *y = 0, ListItem *z = 0) {
  l->clear();
  if (x) l->push_back(x);
  if (y) l->push_back(y);
  if (z) l->push_back(z);
}

static void CheckList(List *l, ListItem *i1, ListItem *i2 = 0, ListItem *i3 = 0,
                      ListItem *i4 = 0, ListItem *i5 = 0, ListItem *i6 = 0) {
  if (i1) {
    CHECK_EQ(l->front(), i1);
    l->pop_front();
  }
  if (i2) {
    CHECK_EQ(l->front(), i2);
    l->pop_front();
  }
  if (i3) {
    CHECK_EQ(l->front(), i3);
    l->pop_front();
  }
  if (i4) {
    CHECK_EQ(l->front(), i4);
    l->pop_front();
  }
  if (i5) {
    CHECK_EQ(l->front(), i5);
    l->pop_front();
  }
  if (i6) {
    CHECK_EQ(l->front(), i6);
    l->pop_front();
  }
  CHECK(l->empty());
}

TEST(SanitizerCommon, IntrusiveList) {
  ListItem items[6];
  CHECK_EQ(static_list.size(), 0);

  List l;
  l.clear();

  ListItem *x = &items[0];
  ListItem *y = &items[1];
  ListItem *z = &items[2];
  ListItem *a = &items[3];
  ListItem *b = &items[4];
  ListItem *c = &items[5];

  CHECK_EQ(l.size(), 0);
  l.push_back(x);
  CHECK_EQ(l.size(), 1);
  CHECK_EQ(l.back(), x);
  CHECK_EQ(l.front(), x);
  l.pop_front();
  CHECK(l.empty());
  l.CheckConsistency();

  l.push_front(x);
  CHECK_EQ(l.size(), 1);
  CHECK_EQ(l.back(), x);
  CHECK_EQ(l.front(), x);
  l.pop_front();
  CHECK(l.empty());
  l.CheckConsistency();

  l.push_front(x);
  l.push_front(y);
  l.push_front(z);
  CHECK_EQ(l.size(), 3);
  CHECK_EQ(l.front(), z);
  CHECK_EQ(l.back(), x);
  l.CheckConsistency();

  l.pop_front();
  CHECK_EQ(l.size(), 2);
  CHECK_EQ(l.front(), y);
  CHECK_EQ(l.back(), x);
  l.pop_front();
  l.pop_front();
  CHECK(l.empty());
  l.CheckConsistency();

  l.push_back(x);
  l.push_back(y);
  l.push_back(z);
  CHECK_EQ(l.size(), 3);
  CHECK_EQ(l.front(), x);
  CHECK_EQ(l.back(), z);
  l.CheckConsistency();

  l.pop_front();
  CHECK_EQ(l.size(), 2);
  CHECK_EQ(l.front(), y);
  CHECK_EQ(l.back(), z);
  l.pop_front();
  l.pop_front();
  CHECK(l.empty());
  l.CheckConsistency();

  l.push_back(x);
  l.push_back(y);
  l.push_back(z);
  l.extract(x, y);
  CHECK_EQ(l.size(), 2);
  CHECK_EQ(l.front(), x);
  CHECK_EQ(l.back(), z);
  l.CheckConsistency();
  l.extract(x, z);
  CHECK_EQ(l.size(), 1);
  CHECK_EQ(l.front(), x);
  CHECK_EQ(l.back(), x);
  l.CheckConsistency();
  l.pop_front();
  CHECK(l.empty());

  List l1, l2;
  l1.clear();
  l2.clear();

  l1.append_front(&l2);
  CHECK(l1.empty());
  CHECK(l2.empty());

  l1.append_back(&l2);
  CHECK(l1.empty());
  CHECK(l2.empty());

  SetList(&l1, x);
  CheckList(&l1, x);

  SetList(&l1, x, y, z);
  SetList(&l2, a, b, c);
  l1.append_back(&l2);
  CheckList(&l1, x, y, z, a, b, c);
  CHECK(l2.empty());

  SetList(&l1, x, y);
  SetList(&l2);
  l1.append_front(&l2);
  CheckList(&l1, x, y);
  CHECK(l2.empty());
}

TEST(SanitizerCommon, IntrusiveListAppendEmpty) {
  ListItem i;
  List l;
  l.clear();
  l.push_back(&i);
  List l2;
  l2.clear();
  l.append_back(&l2);
  CHECK_EQ(l.back(), &i);
  CHECK_EQ(l.front(), &i);
  CHECK_EQ(l.size(), 1);
  l.append_front(&l2);
  CHECK_EQ(l.back(), &i);
  CHECK_EQ(l.front(), &i);
  CHECK_EQ(l.size(), 1);
}

}  // namespace __sanitizer
