// Copyright 2015 The Chromium 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 "cast/streaming/expanded_value_base.h"

#include "gtest/gtest.h"

namespace openscreen {
namespace cast {

namespace {

// A basic subclass of ExpandedValueBase to use for testing.
class TestValue : public ExpandedValueBase<int64_t, TestValue> {
 public:
  explicit TestValue(int64_t value) : ExpandedValueBase(value) {}
};

}  // namespace

// Tests the various scenarios of truncating and then re-expanding values, and
// confirming the correct behavior.  Note that, while the code below just tests
// truncation/expansion to/from 8 bits, the 16- and 32-bit cases are implicitly
// confirmed because the Expand() method uses the compiler to derive all of its
// constants (based on the type of its argument).
TEST(ExpandedValueBaseTest, TruncationAndExpansion) {
  // Test that expansion works when the reference is always equal to the value
  // that was truncated.
  for (int64_t i = -512; i <= 512; ++i) {
    const TestValue original_value(i);
    const uint8_t truncated = original_value.lower_8_bits();
    const TestValue reference(i);
    ASSERT_EQ(original_value, reference.Expand(truncated)) << "i=" << i;
  }

  // Test that expansion works when the reference is always one less than the
  // value that was truncated.
  for (int64_t i = -512; i <= 512; ++i) {
    const TestValue original_value(i);
    const uint8_t truncated = original_value.lower_8_bits();
    const TestValue reference(i - 1);
    ASSERT_EQ(original_value, reference.Expand(truncated)) << "i=" << i;
  }

  // Test that expansion works when the reference is always one greater than the
  // value that was truncated.
  for (int64_t i = -512; i <= 512; ++i) {
    const TestValue original_value(i);
    const uint8_t truncated = original_value.lower_8_bits();
    const TestValue reference(i + 1);
    ASSERT_EQ(original_value, reference.Expand(truncated)) << "i=" << i;
  }

  // Test cases where the difference between the original value and the fixed
  // reference is within the range [-128,+127].  The truncated value should
  // always be re-expanded to the original value.
  for (int64_t bias = -5; bias <= 5; ++bias) {
    for (int64_t i = -128; i <= 127; ++i) {
      const TestValue original_value(bias + i);
      const uint8_t truncated = original_value.lower_8_bits();
      const TestValue reference(bias);
      ASSERT_EQ(original_value, reference.Expand(truncated))
          << "bias=" << bias << ", i=" << i;
    }
  }

  // Test cases where the difference between the original value and the fixed
  // reference is within the range [+128,+255].  When the truncated value is
  // re-expanded, it should be 256 less than the original value.
  for (int64_t bias = -5; bias <= 5; ++bias) {
    for (int64_t i = 128; i <= 255; ++i) {
      // Example: Let |original_value| be 192.  Then, the truncated 8-bit value
      // will be 0xc0.  When a |reference| of zero is asked to expand 0xc0 back
      // to the original value, it should produce -64 since -64 is closer to
      // |reference| than 192.
      const TestValue original_value(bias + i);
      const uint8_t truncated = original_value.lower_8_bits();
      const TestValue reexpanded_value(bias + i - 256);
      ASSERT_EQ(reexpanded_value.lower_8_bits(), truncated);
      const TestValue reference(bias);
      ASSERT_EQ(reexpanded_value, reference.Expand(truncated))
          << "bias=" << bias << ", i=" << i;
    }
  }

  // Test cases where the difference between the original value and the fixed
  // reference is within the range [-256,-129].  When the truncated value is
  // re-expanded, it should be 256 more than the original value.
  for (int64_t bias = -5; bias <= 5; ++bias) {
    for (int64_t i = -256; i <= -129; ++i) {
      // Example: Let |original_value| be -192.  Then, the truncated 8-bit value
      // will be 0x40.  When a |reference| of zero is asked to expand 0x40 back
      // to the original value, it should produce 64 since 64 is closer to the
      // |reference| than -192.
      const TestValue original_value(bias + i);
      const uint8_t truncated = original_value.lower_8_bits();
      const TestValue reexpanded_value(bias + i + 256);
      ASSERT_EQ(reexpanded_value.lower_8_bits(), truncated);
      const TestValue reference(bias);
      ASSERT_EQ(reexpanded_value, reference.Expand(truncated))
          << "bias=" << bias << ", i=" << i;
    }
  }
}

}  // namespace cast
}  // namespace openscreen
