// Unit-Test für Bit-Array-Klasse mittels GoogleTest
//
// Klaus Kusche, 2012

#include "bitarray.h"
#include <gtest/gtest.h>

// Testumgebung: Legt ein paar Testobjekte an
class BitArrayTest : public ::testing::Test
{
  protected:
      
    BitArray b0a, b0b;       // zwei BitArrays mit 0 Elementen
    BitArray b1_0;           // ein BitArrays mit 1 Element Wert 0
    BitArray b1_1;           // ein BitArrays mit 1 Element Wert 1
    BitArray b13_0;          // ein BitArrays mit 13 Elementen Wert 0
    BitArray b64_1;          // ein BitArrays mit 64 Elementen Wert 1
    BitArray b129_0;         // ein BitArrays mit 129 Elementen Wert 0
    BitArray b129_1;         // ein BitArrays mit 129 Elementen Wert 1

    BitArrayTest()
      : b0a(0), b0b(0, true), b1_0(1), b1_1(1, true),
        b13_0(13), b64_1(64, true), b129_0(129, false), b129_1(129, true) {}
};

// testet, ob die Testdaten korrekt angelegt worden sind
// zugleich vollständiger Test für den normalen Konstruktor und getSize
// (haben beide keine Fehler-Fälle, Memory-Überlauf tritt bei mir nicht auf)
// sowie die Gut-Fälle von test
TEST_F(BitArrayTest, InitDataOk)
{
  unsigned int i;
  
  ASSERT_EQ(0, b0a.getSize());
  ASSERT_EQ(0, b0b.getSize());
  ASSERT_EQ(1, b1_0.getSize());
  ASSERT_EQ(1, b1_1.getSize());
  ASSERT_EQ(13, b13_0.getSize());
  ASSERT_EQ(64, b64_1.getSize());
  ASSERT_EQ(129, b129_0.getSize());
  ASSERT_EQ(129, b129_1.getSize());

  EXPECT_FALSE(b1_0.test(0));
  EXPECT_TRUE(b1_1.test(0));

  for (i = 0; i < 13; ++i) EXPECT_FALSE(b13_0.test(i)) << "where i = " << i;
  for (i = 0; i < 64; ++i) EXPECT_TRUE(b64_1.test(i)) << "where i = " << i;
  for (i = 0; i < 129; ++i) EXPECT_FALSE(b129_0.test(i)) << "where i = " << i;
  for (i = 0; i < 129; ++i) EXPECT_TRUE(b129_1.test(i)) << "where i = " << i;
}

// der Destruktor ist nicht sinnvoll testbar

// testet den Fehler-Fall von test,
// die Gut-Fälle sind schon durch InitData getestet
TEST_F(BitArrayTest, TestBad)
{
  EXPECT_THROW(b0a.test(0), const char *);
  EXPECT_THROW(b1_1.test(1), const char *);
  EXPECT_THROW(b64_1.test(64), const char *);
  EXPECT_THROW(b129_0.test(129), const char *);

  EXPECT_THROW(b13_0.test(-1U), const char *);
}

// testet die Gut-Fälle von set und clear
TEST_F(BitArrayTest, SetClearGood)
{
  unsigned int i;
  
  b1_0.set(0);
  ASSERT_EQ(1, b1_0.getSize());
  EXPECT_TRUE(b1_0.test(0));

  for (i = 1; i < 12; ++i) b13_0.set(i, true);
  EXPECT_FALSE(b13_0.test(0));
  EXPECT_FALSE(b13_0.test(12));
  for (i = 1; i < 12; ++i) EXPECT_TRUE(b13_0.test(i)) << "where i = " << i;

  b64_1.clear(0);
  b64_1.set(63, false);
  EXPECT_FALSE(b64_1.test(0));
  EXPECT_FALSE(b64_1.test(63));
  for (i = 1; i < 63; ++i) EXPECT_TRUE(b64_1.test(i)) << "where i = " << i;

  b129_0.set(128, true);
  ASSERT_EQ(129, b129_0.getSize());
  EXPECT_TRUE(b129_0.test(128));  
  EXPECT_FALSE(b129_0.test(127));

  // Returnwert ok?
  EXPECT_TRUE(b129_0.set(1).set(66).test(66));
}

// testet den Fehler-Fall von set und clear
TEST_F(BitArrayTest, SetClearBad)
{
  EXPECT_THROW(b0a.set(0), const char *);
  EXPECT_THROW(b1_1.clear(1), const char *);
  EXPECT_THROW(b64_1.clear(64), const char *);
  EXPECT_THROW(b129_0.set(129, false), const char *);

  EXPECT_THROW(b13_0.clear(999), const char *);
}

// testet den Copy-Konstruktor
TEST_F(BitArrayTest, CopyConstr)
{
  unsigned int i;
  
  BitArray cpy0 = b0a;
  ASSERT_EQ(0, cpy0.getSize());
  
  BitArray *cpy129a = new BitArray(b129_1);
  BitArray cpy129b(*cpy129a);
  ASSERT_EQ(129, cpy129b.getSize());
  for (i = 0; i < 129; ++i) EXPECT_TRUE(cpy129b.test(i)) << "where i = " << i;
  // Original unverändert?
  ASSERT_EQ(129, b129_1.getSize());
  for (i = 0; i < 129; ++i) EXPECT_TRUE(b129_1.test(i)) << "where i = " << i;

  // testet, ob die internen Daten wirklich eine Kopie sind
  // und kein geshartes Array (Änderung darf Original nicht ändern,
  // und Daten müssen da sein, auch wenn Original weg ist)
  cpy129a->clear(0);
  b129_1.clear(128);
  EXPECT_TRUE(cpy129a->test(128));
  EXPECT_TRUE(b129_1.test(0));
  
  delete cpy129a;
  ASSERT_EQ(129, cpy129b.getSize());
  for (i = 0; i < 129; ++i) EXPECT_TRUE(cpy129b.test(i)) << "where i = " << i;
}

// testet die Gut-Fälle von operator!
TEST_F(BitArrayTest, EmptyGood)
{
  unsigned int i;
  
  // Länge 0 liefert true
  EXPECT_TRUE(!b0a);

  EXPECT_TRUE(!b1_0);
  EXPECT_TRUE(!b129_0);

  EXPECT_FALSE(!b1_1);
  EXPECT_FALSE(!b129_1);

  EXPECT_TRUE(!b13_0);
  b13_0.set(7);
  EXPECT_FALSE(!b13_0);

  for (i = 0; i < 128; ++i) b129_1.clear(i);  
  EXPECT_FALSE(!b129_1);
  b129_1.clear(128);
  EXPECT_TRUE(!b129_1);
}

// testet die Gut-Fälle von operator==
TEST_F(BitArrayTest, EqualGood)
{
  unsigned int i;
  BitArray tmp = b64_1;
  
  EXPECT_TRUE(b0a == b0b);
  EXPECT_FALSE(b1_1 == b64_1);
  EXPECT_FALSE(b129_0 == b129_1);
  EXPECT_TRUE(b129_1 == b129_1);

  tmp.clear(0);
  EXPECT_FALSE(tmp == b64_1);
  
  for (i = 0; i < 128; ++i) b129_1.clear(i);  
  EXPECT_FALSE(b129_1 == b129_0);
  b129_1.clear(128);
  EXPECT_TRUE(b129_1 == b129_0);
}

// testet die Gut-Fälle von operator<=
TEST_F(BitArrayTest, LessEqualGood)
{
  unsigned int i;
  
  BitArray tmp1 = b13_0;
  BitArray tmp2 = b64_1;
  
  EXPECT_TRUE(b0a <= b0b);
  EXPECT_FALSE(b1_1 <= b64_1);
  EXPECT_FALSE(b13_0 <= b129_0);
  EXPECT_FALSE(b129_0 <= b13_0);
  EXPECT_FALSE(b129_1 <= b129_0);
  EXPECT_TRUE(b129_1 <= b129_1);

  tmp1.set(0);
  EXPECT_FALSE(tmp1 <= b13_0);
  EXPECT_TRUE(b13_0 <= tmp1);
  tmp2.clear(63);
  EXPECT_FALSE(b64_1 <= tmp2);
  EXPECT_TRUE(tmp2 <= b64_1);

  for (i = 0; i < 128; ++i) b129_1.clear(i);  
  EXPECT_FALSE(b129_1 <= b129_0);
  EXPECT_TRUE(b129_0 <= b129_1);
  b129_1.clear(128);
  EXPECT_TRUE(b129_1 <= b129_0);
  EXPECT_TRUE(b129_0 <= b129_1);
}

// testet die Gut-Fälle von invert
// verwendet schon getesteten operator== und operator!
TEST_F(BitArrayTest, InvertGood)
{
  unsigned int i;

  b0a.invert();
  EXPECT_TRUE(b0a == b0b);

  EXPECT_TRUE(b1_0.invert() == b1_1);
  EXPECT_TRUE(b129_1.invert() == b129_0);

  for (i = 0; i < 63; i += 2) b64_1.clear(i);
  b64_1.invert();
  for (i = 0; i < 63; i += 2) b64_1.clear(i);
  EXPECT_TRUE(!b64_1);
}

// testet die Gut-Fälle von operator=
TEST_F(BitArrayTest, AssignGood)
{
  unsigned int i;
  BitArray tmp1(0), tmp2(999);
  
  // Zuweisung gleichgroßer BitArrays
  b129_0 = b129_1;
  ASSERT_EQ(129, b129_0.getSize());
  for (i = 0; i < 129; ++i) EXPECT_TRUE(b129_0.test(i)) << "where i = " << i;

  // Zuweisung eines leeren BitArrays
  b0b = b0a;
  ASSERT_EQ(0, b0b.getSize());

  // Zuweisung verschieden großer BitArrays
  b0a = b64_1;
  ASSERT_EQ(64, b0a.getSize());
  for (i = 0; i < 64; ++i) EXPECT_TRUE(b0a.test(i)) << "where i = " << i;
  b64_1 = b0b;
  ASSERT_EQ(0, b64_1.getSize());
  b13_0 = b129_1;
  ASSERT_EQ(129, b129_1.getSize());
  for (i = 0; i < 129; ++i) EXPECT_TRUE(b129_1.test(i)) << "where i = " << i;

  // Returnwert muss stimmen (geschachtelte Zuweisung)
  // rechte Seite darf sich nicht ändern
  tmp2 = tmp1 = b13_0;
  ASSERT_EQ(129, tmp1.getSize());
  for (i = 0; i < 129; ++i) EXPECT_TRUE(tmp1.test(i)) << "where i = " << i;
  ASSERT_EQ(129, tmp2.getSize());
  for (i = 0; i < 129; ++i) EXPECT_TRUE(tmp2.test(i)) << "where i = " << i;
  // Original unverändert?
  ASSERT_EQ(129, b13_0.getSize());
  for (i = 0; i < 129; ++i) EXPECT_TRUE(b13_0.test(i)) << "where i = " << i;

  // Selbst-Zuweisung darf nichts ruinieren
  tmp2 = tmp2;
  ASSERT_EQ(129, tmp2.getSize());
  for (i = 0; i < 129; ++i) EXPECT_TRUE(tmp2.test(i)) << "where i = " << i;
}

// testet die Gut-Fälle von intersect
// verwendet schon getesteten operator== usw.!
TEST_F(BitArrayTest, IntersectGood)
{
  unsigned int i;
  BitArray tmp = b129_1;

  b0a.intersect(b0b);
  EXPECT_TRUE(b0a == b0b);

  EXPECT_TRUE(tmp.intersect(b129_0) == b129_0);
  tmp = b129_1;
  EXPECT_TRUE(tmp.intersect(b129_1) == b129_1);
  tmp = b129_0;
  EXPECT_TRUE(tmp.intersect(b129_0) == b129_0);
  tmp = b129_0;
  EXPECT_TRUE(tmp.intersect(b129_1) == b129_0);

  tmp = b129_1;
  for (i = 1; i < 129; i += 2) tmp.clear(i);
  for (i = 2; i < 129; i += 4) b129_1.clear(i);
  tmp.intersect(b129_1);
  for (i = 0; i < 129; ++i) {
    if (i % 4 == 0) EXPECT_TRUE(tmp.test(i)) << "where i = " << i;
    else EXPECT_FALSE(tmp.test(i)) << "where i = " << i;
  }
}

// testet den Fehler-Fall von intersect
TEST_F(BitArrayTest, IntersectBad) {
  EXPECT_THROW(b0a.intersect(b13_0), const char *);
  EXPECT_THROW(b13_0.intersect(b64_1), const char *);
  EXPECT_THROW(b129_0.intersect(b64_1), const char *);
}
