//===-- Unittests for queue -----------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#include "src/__support/CPP/string.h"
#include "src/__support/char_vector.h"
#include "src/__support/macros/config.h"
#include "test/UnitTest/Test.h"

#include "include/llvm-libc-macros/sys-queue-macros.h"

using LIBC_NAMESPACE::CharVector;
using LIBC_NAMESPACE::cpp::string;

namespace LIBC_NAMESPACE_DECL {

TEST(LlvmLibcQueueTest, SList) {
  struct Entry {
    char c;
    SLIST_ENTRY(Entry) entries;
  };

  SLIST_HEAD(Head, Entry);

  Head head = SLIST_HEAD_INITIALIZER(head);

  struct Contains : public testing::Matcher<Head> {
    string s;
    Contains(string s) : s(s) {}
    bool match(Head head) {
      Entry *e;
      CharVector v;
      SLIST_FOREACH(e, &head, entries) { v.append(e->c); }
      return s == v.c_str();
    }
  };

  Entry e1 = {'a', {NULL}};
  SLIST_INSERT_HEAD(&head, &e1, entries);

  ASSERT_THAT(head, Contains("a"));

  Entry e2 = {'b', {NULL}};
  SLIST_INSERT_AFTER(&e1, &e2, entries);

  ASSERT_THAT(head, Contains("ab"));

  Head head2 = SLIST_HEAD_INITIALIZER(head);

  Entry e3 = {'c', {NULL}};
  SLIST_INSERT_HEAD(&head2, &e3, entries);

  ASSERT_THAT(head2, Contains("c"));

  SLIST_SWAP(&head, &head2, Entry);

  ASSERT_THAT(head2, Contains("ab"));

  SLIST_CONCAT(&head2, &head, Entry, entries);

  ASSERT_THAT(head2, Contains("abc"));

  SLIST_CONCAT(&head, &head2, Entry, entries);

  ASSERT_THAT(head, Contains("abc"));

  Entry *e = NULL, *tmp = NULL;
  SLIST_FOREACH_SAFE(e, &head, entries, tmp) {
    if (e == &e2) {
      SLIST_REMOVE(&head, e, Entry, entries);
    }
  }

  ASSERT_THAT(head, Contains("ac"));

  while (!SLIST_EMPTY(&head)) {
    e = SLIST_FIRST(&head);
    SLIST_REMOVE_HEAD(&head, entries);
  }

  ASSERT_TRUE(SLIST_EMPTY(&head));
}

TEST(LlvmLibcQueueTest, STailQ) {
  struct Entry {
    char c;
    STAILQ_ENTRY(Entry) entries;
  };

  STAILQ_HEAD(Head, Entry);

  Head head = STAILQ_HEAD_INITIALIZER(head);

  struct Contains : public testing::Matcher<Head> {
    string s;
    Contains(string s) : s(s) {}
    bool match(Head head) {
      Entry *e;
      CharVector v;
      STAILQ_FOREACH(e, &head, entries) { v.append(e->c); }
      return s == v.c_str();
    }
  };

  STAILQ_INIT(&head);
  ASSERT_TRUE(STAILQ_EMPTY(&head));

  Entry e1 = {'a', {NULL}};
  STAILQ_INSERT_HEAD(&head, &e1, entries);

  ASSERT_THAT(head, Contains("a"));

  Entry e2 = {'b', {NULL}};
  STAILQ_INSERT_TAIL(&head, &e2, entries);

  ASSERT_THAT(head, Contains("ab"));

  Entry e3 = {'c', {NULL}};
  STAILQ_INSERT_AFTER(&head, &e2, &e3, entries);

  ASSERT_THAT(head, Contains("abc"));

  Head head2 = STAILQ_HEAD_INITIALIZER(head);

  Entry e4 = {'d', {NULL}};
  STAILQ_INSERT_HEAD(&head2, &e4, entries);

  ASSERT_THAT(head2, Contains("d"));

  STAILQ_SWAP(&head, &head2, Entry);

  ASSERT_THAT(head2, Contains("abc"));

  STAILQ_CONCAT(&head2, &head, Entry, entries);

  ASSERT_EQ(STAILQ_FIRST(&head2), &e1);
  ASSERT_EQ(STAILQ_LAST(&head2, Entry, entries), &e4);

  ASSERT_THAT(head2, Contains("abcd"));

  STAILQ_CONCAT(&head, &head2, Entry, entries);

  ASSERT_EQ(STAILQ_FIRST(&head), &e1);
  ASSERT_EQ(STAILQ_LAST(&head, Entry, entries), &e4);

  ASSERT_THAT(head, Contains("abcd"));

  Entry *e = NULL, *tmp = NULL;
  STAILQ_FOREACH_SAFE(e, &head, entries, tmp) {
    if (e == &e2) {
      STAILQ_REMOVE(&head, e, Entry, entries);
    }
  }

  ASSERT_THAT(head, Contains("acd"));

  while (!STAILQ_EMPTY(&head)) {
    e = STAILQ_FIRST(&head);
    STAILQ_REMOVE_HEAD(&head, entries);
  }

  ASSERT_TRUE(STAILQ_EMPTY(&head));
}

} // namespace LIBC_NAMESPACE_DECL
