// Copyright (c) 2020 Metrolab Technology S.A., Geneva, Switzerland (www.metrolab.com)
// See the included file LICENSE.txt for the licensing conditions.

//////////////////////////////////////////////////////////////////////////
/// \file
/// \brief Test THM1176 API: Type conversions and THM1176 classes.

#include "gtest/gtest.h"
#include "THM1176TestUtilities.h"
#include "THM1176TypeConversions.h"

using namespace MTL::Instrument;
using namespace MTL::Instrument::THM1176Types;

/// \brief Test THM1176 API: Type conversions and THM1176 classes.
class CTHM1176BasicsTest : public ::testing::Test
{
};

/// \test Test the binary-to-integer conversion tests.
TEST_F(CTHM1176BasicsTest, TestTHM1176Types)
{
	EXPECT_EQ(true, TestTHM1176Types());
}

/// \test Test CFluxList class.
TEST_F(CTHM1176BasicsTest, CFluxList)
{
	// Set up the objects.
	CFluxList l_Object1;
	l_Object1.push_back(1.);
	l_Object1.push_back(2.);
	l_Object1.push_back(3.);
	CFluxList l_Object2 = l_Object1;
	
	// Test equality.
	EXPECT_EQ(true, l_Object1 == l_Object2);
	EXPECT_EQ(false, l_Object1 != l_Object2);
	
	// Test inequality: different number of elements.
	l_Object2.pop_back();
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: one element different.
	l_Object2 = l_Object1;
	l_Object2[2] = 4.;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test clear.
	l_Object2.clear();
	EXPECT_EQ(0, l_Object2.size());
}

/// \test Test CUnitsList class.
TEST_F(CTHM1176BasicsTest, CUnitsList)
{
	// Set up the objects.
	CUnitsList l_Object1;
	l_Object1.push_back(kT);
	l_Object1.push_back(kmT);
	l_Object1.push_back(kuT);
	CUnitsList l_Object2 = l_Object1;
	
	// Test equality.
	EXPECT_EQ(true, l_Object1 == l_Object2);
	EXPECT_EQ(false, l_Object1 != l_Object2);
	
	// Test inequality: different number of elements.
	l_Object2.pop_back();
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: one element different.
	l_Object2 = l_Object1;
	l_Object2[2] = knT;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test clear.
	l_Object2.clear();
	EXPECT_EQ(0, l_Object2.size());
}

/// \test Test CDivisorList class.
TEST_F(CTHM1176BasicsTest, CDivisorList)
{
	// Set up the objects.
	CDivisorList l_Object1;
	l_Object1.push_back(1);
	l_Object1.push_back(2);
	l_Object1.push_back(3);
	CDivisorList l_Object2 = l_Object1;
	
	// Test equality.
	EXPECT_EQ(true, l_Object1 == l_Object2);
	EXPECT_EQ(false, l_Object1 != l_Object2);
	
	// Test inequality: different number of elements.
	l_Object2.pop_back();
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: one element different.
	l_Object2 = l_Object1;
	l_Object2[2] = 4;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test clear.
	l_Object2.clear();
	EXPECT_EQ(0, l_Object2.size());
}

/// \test Test CErrorList class.
TEST_F(CTHM1176BasicsTest, CErrorList)
{
	// Set up the objects.
	sError l_Error;
	CErrorList l_Object1;
	l_Error = { 1, "a", "A" };
	l_Object1.push_back(l_Error);
	l_Error = { 1, "b", "B" };
	l_Object1.push_back(l_Error);
	l_Error = { 1, "c", "C" };
	l_Object1.push_back(l_Error);
	CErrorList l_Object2 = l_Object1;
	
	// Test equality.
	EXPECT_EQ(true, l_Object1 == l_Object2);
	EXPECT_EQ(false, l_Object1 != l_Object2);
	
	// Test inequality: different number of elements.
	l_Object2.pop_back();
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: one element different.
	l_Error = { 1, "d", "D" };
	l_Object1.push_back(l_Error);
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test clear.
	l_Object2.clear();
	EXPECT_EQ(0, l_Object2.size());
}

/// \test Test sIdentifier class.
TEST_F(CTHM1176BasicsTest, sIdentifier)
{
	// Set up the objects.
	sIdentifier l_Object1 = { "A", "B", 1, {2, 3}, {4, 5}, {6, 7}, eModelRevision::kA, eInstrModel::kTHM1176MF};
	sIdentifier l_Object2 = l_Object1;
	
	// Test equality.
	EXPECT_EQ(true, l_Object1 == l_Object2);
	EXPECT_EQ(false, l_Object1 != l_Object2);
	
	// Test inequality: different Manufacturer.
	l_Object2 = l_Object1;
	l_Object2.Manufacturer = "C";
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different Model.
	l_Object2 = l_Object1;
	l_Object2.Model = "C";
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different SerialNumber.
	l_Object2 = l_Object1;
	l_Object2.SerialNumber = 8;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different ElectronicsVersion.Major.
	l_Object2 = l_Object1;
	l_Object2.ElectronicsVersion.Major = 9;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different ElectronicsVersion.Minor.
	l_Object2 = l_Object1;
	l_Object2.ElectronicsVersion.Minor = 10;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different ProbeVersion.Major.
	l_Object2 = l_Object1;
	l_Object2.ProbeVersion.Major = 11;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different ProbeVersion.Minor.
	l_Object2 = l_Object1;
	l_Object2.ProbeVersion.Minor = 12;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different FirmwareVersion.Major.
	l_Object2 = l_Object1;
	l_Object2.FirmwareVersion.Major = 13;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different FirmwareVersion.Minor.
	l_Object2 = l_Object1;
	l_Object2.FirmwareVersion.Minor = 14;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);

	// Test inequality: different Model Revision.
	l_Object2 = l_Object1;
	l_Object2.ModelRevision = eModelRevision::kB;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);

	// Test inequality: different Instrument Model.
	l_Object2 = l_Object1;
	l_Object2.InstrModel = eInstrModel::kTFM1186;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test clear.
	l_Object1 = { "", "", 0, {0, 0}, {0, 0}, {0, 0}};
	l_Object2.clear();
	EXPECT_EQ(true, l_Object1 == l_Object2);
}

/// \test Test sAveraging<uParm> class.
TEST_F(CTHM1176BasicsTest, sAveraging_uParm)
{
	// Set up the objects.
	sAveraging<uParm> l_Object1 = { 1 };
	sAveraging<uParm> l_Object2 = l_Object1;
	
	// Test equality.
	EXPECT_EQ(true, l_Object1 == l_Object2);
	EXPECT_EQ(false, l_Object1 != l_Object2);
	
	// Test inequality: different NoPoints.
	l_Object2 = l_Object1;
	l_Object2.NoPoints = 2;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test clear.
	l_Object1 = { 0 };
	l_Object2.clear();
	EXPECT_EQ(true, l_Object1 == l_Object2);
}

/// \test Test sAveraging<sBoundedParm> class.
TEST_F(CTHM1176BasicsTest, sAveraging_sBoundedParm)
{
	// Set up the objects.
	sAveraging<sBoundedParm> l_Object1 = { { 1, 2, 3, 4} };
	sAveraging<sBoundedParm> l_Object2 = l_Object1;
	
	// Test equality.
	EXPECT_EQ(true, l_Object1 == l_Object2);
	EXPECT_EQ(false, l_Object1 != l_Object2);
	
	// Test inequality: different NoPoints.Val.
	l_Object2 = l_Object1;
	l_Object2.NoPoints.Val = 5;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different NoPoints.Min.
	l_Object2 = l_Object1;
	l_Object2.NoPoints.Min = 6;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different NoPoints.Max.
	l_Object2 = l_Object1;
	l_Object2.NoPoints.Max = 7;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different NoPoints.Def.
	l_Object2 = l_Object1;
	l_Object2.NoPoints.Def = 8;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test clear.
	l_Object1 = { { 0, 0, 0, 0 } };
	l_Object2.clear();
	EXPECT_EQ(true, l_Object1 == l_Object2);
}

/// \test Test sInputTrigger<uParm> class.
TEST_F(CTHM1176BasicsTest, sInputTrigger_uParm)
{
	// Set up the objects.
	sInputTrigger<uParm> l_Object1 = { kInputTrigSrcImmediate, 1., 2 };
	sInputTrigger<uParm> l_Object2 = l_Object1;
	
	// Test equality.
	EXPECT_EQ(true, l_Object1 == l_Object2);
	EXPECT_EQ(false, l_Object1 != l_Object2);
	
	// Test inequality: different Source.
	l_Object2 = l_Object1;
	l_Object2.Source = kInputTrigSrcTimer;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different Period_s.
	l_Object2 = l_Object1;
	l_Object2.Period_s = 3.;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different Count.
	l_Object2 = l_Object1;
	l_Object2.Count = 4;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test clear.
	l_Object1 = { kInputTrigSrcImmediate, 0., 0 };
	l_Object2.clear();
	EXPECT_EQ(true, l_Object1 == l_Object2);
}

/// \test Test sInputTrigger<sBoundedParm> class.
TEST_F(CTHM1176BasicsTest, sInputTrigger_sBoundedParm)
{
	// Set up the objects.
	sInputTrigger<sBoundedParm> l_Object1 = { kInputTrigSrcImmediate, { 1., 2., 3., 4.}, {5, 6, 7, 8 } };
	sInputTrigger<sBoundedParm> l_Object2 = l_Object1;
	
	// Test equality.
	EXPECT_EQ(true, l_Object1 == l_Object2);
	EXPECT_EQ(false, l_Object1 != l_Object2);
	
	// Test inequality: different Source.
	l_Object2 = l_Object1;
	l_Object2.Source = kInputTrigSrcTimer;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different Period_s.Val.
	l_Object2 = l_Object1;
	l_Object2.Period_s.Val = 9.;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different Period_s.Min.
	l_Object2 = l_Object1;
	l_Object2.Period_s.Min = 10.;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different Period_s.Max.
	l_Object2 = l_Object1;
	l_Object2.Period_s.Max = 11.;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different Period_s.Def.
	l_Object2 = l_Object1;
	l_Object2.Period_s.Def = 12.;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different Count.Val.
	l_Object2 = l_Object1;
	l_Object2.Count.Val = 13;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different Count.Min.
	l_Object2 = l_Object1;
	l_Object2.Count.Min = 14;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different Count.Max.
	l_Object2 = l_Object1;
	l_Object2.Count.Max = 15;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different Count.Def.
	l_Object2 = l_Object1;
	l_Object2.Count.Def = 16;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test clear.
	l_Object1 = { kInputTrigSrcImmediate, { 0., 0., 0., 0. }, { 0, 0, 0, 0 } };
	l_Object2.clear();
	EXPECT_EQ(true, l_Object1 == l_Object2);
}

/// \test Test sRange<uParm> class.
TEST_F(CTHM1176BasicsTest, sRange_uParm)
{
	// Set up the objects.
	sRange<uParm> l_Object1 = { true, 1. };
	sRange<uParm> l_Object2 = l_Object1;
	
	// Test equality.
	EXPECT_EQ(true, l_Object1 == l_Object2);
	EXPECT_EQ(false, l_Object1 != l_Object2);
	
	// Test inequality: different Auto.
	l_Object2 = l_Object1;
	l_Object2.Auto = false;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different Range.
	l_Object2 = l_Object1;
	l_Object2.Range = 2.;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test clear.
	l_Object1 = { false, 0. };
	l_Object2.clear();
	EXPECT_EQ(true, l_Object1 == l_Object2);
}

/// \test Test sRange<sBoundedParm> class.
TEST_F(CTHM1176BasicsTest, sRange_sBoundedParm)
{
	// Set up the objects.
	sRange<sBoundedParm> l_Object1 = { true, { 1., 2., 3., 4.} };
	sRange<sBoundedParm> l_Object2 = l_Object1;
	
	// Test equality.
	EXPECT_EQ(true, l_Object1 == l_Object2);
	EXPECT_EQ(false, l_Object1 != l_Object2);
	
	// Test inequality: different Auto.
	l_Object2 = l_Object1;
	l_Object2.Auto = false;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different Range.Val.
	l_Object2 = l_Object1;
	l_Object2.Range.Val = 5.;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different Range.Min.
	l_Object2 = l_Object1;
	l_Object2.Range.Min = 6.;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different Range.Max.
	l_Object2 = l_Object1;
	l_Object2.Range.Max = 7.;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different Range.Def.
	l_Object2 = l_Object1;
	l_Object2.Range.Def = 8.;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test clear.
	l_Object1 = { false, { 0., 0., 0., 0. } };
	l_Object2.clear();
	EXPECT_EQ(true, l_Object1 == l_Object2);
}

/// \test Test sFile class.
TEST_F(CTHM1176BasicsTest, sFile)
{
	// Set up the objects.
	sFile l_Object1 = { 1, "A", "B" };
	sFile l_Object2 = l_Object1;
	
	// Test equality.
	EXPECT_EQ(true, l_Object1 == l_Object2);
	EXPECT_EQ(false, l_Object1 != l_Object2);
	
	// Test inequality: different Path.
	l_Object2 = l_Object1;
	l_Object2.Path = "C";
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different Type.
	l_Object2 = l_Object1;
	l_Object2.Type = "D";
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different Size.
	l_Object2 = l_Object1;
	l_Object2.Size = 2;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test clear.
	l_Object1 = { 0, "", "" };
	l_Object2.clear();
	EXPECT_EQ(true, l_Object1 == l_Object2);
}

/// \test Test CAbsoluteTimestamp class.
TEST_F(CTHM1176BasicsTest, CAbsoluteTimestamp)
{
	// Set up the objects.
	CAbsoluteTimestamp l_Object1(1, 2);
	CAbsoluteTimestamp l_Object2 = l_Object1;
	CAbsoluteTimestamp l_Object3;
	
	// Test accessors.
	EXPECT_EQ(l_Object1.seconds(), 1);
	EXPECT_EQ(l_Object1.nanoseconds(), 2);

	// Test equality.
	EXPECT_EQ(true, l_Object1 == l_Object2);
	EXPECT_EQ(false, l_Object1 != l_Object2);
	
	// Test modulo operation in constructor.
	l_Object2 = CAbsoluteTimestamp(0, 1000000002);
	EXPECT_EQ(true, l_Object1 == l_Object2);
	EXPECT_EQ(false, l_Object1 != l_Object2);

	// Test inequality: different s.
	l_Object2 = CAbsoluteTimestamp(3, 2);
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different ns.
	l_Object2 = CAbsoluteTimestamp(1, 4);
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test addition: normal.
	l_Object2 = CAbsoluteTimestamp(3, 4);
	l_Object3 = l_Object1 + l_Object2;
	EXPECT_EQ(l_Object3.seconds(), 4);
	EXPECT_EQ(l_Object3.nanoseconds(), 6);
	
	// Test addition: with carry.
	l_Object2 = CAbsoluteTimestamp(3, 999999999);
	l_Object3 = l_Object1 + l_Object2;
	EXPECT_EQ(l_Object3.seconds(), 5);
	EXPECT_EQ(l_Object3.nanoseconds(), 1);
	
	// Test subtraction: normal.
	l_Object2 = CAbsoluteTimestamp(3, 4);
	l_Object3 = l_Object2 - l_Object1;
	EXPECT_EQ(l_Object3.seconds(), 2);
	EXPECT_EQ(l_Object3.nanoseconds(), 2);
	
	// Test subtraction: with carry.
	l_Object2 = CAbsoluteTimestamp(3, 1);
	l_Object3 = l_Object2 - l_Object1;
	EXPECT_EQ(l_Object3.seconds(), 1);
	EXPECT_EQ(l_Object3.nanoseconds(), 999999999);
}

/// \test Test sArbitraryMeasurements class.
TEST_F(CTHM1176BasicsTest, sArbitraryMeasurements)
{
	// Set up the objects.
	sArbitraryMeasurements l_Object1 = { false, false, false, false, false, 100 };
	sArbitraryMeasurements l_Object2 = l_Object1;
	
	// Test equality.
	EXPECT_EQ(true, l_Object1 == l_Object2);
	EXPECT_EQ(false, l_Object1 != l_Object2);
	
	// Test inequality: different Bx.
	l_Object2 = l_Object1;
	l_Object2.Bx = true;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different By.
	l_Object2 = l_Object1;
	l_Object2.By = true;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different Bz.
	l_Object2 = l_Object1;
	l_Object2.Bz = true;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different Temperature.
	l_Object2 = l_Object1;
	l_Object2.Temperature = true;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different Timestamp.
	l_Object2 = l_Object1;
	l_Object2.Timestamp = true;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test inequality: different NoMeasurements.
	l_Object2 = l_Object1;
	l_Object2.NoMeasurements = 200;
	EXPECT_EQ(false, l_Object1 == l_Object2);
	EXPECT_EQ(true, l_Object1 != l_Object2);
	
	// Test clear.
	l_Object1 = { true, true, true, true, true, 1 };
	l_Object2.clear();
	EXPECT_EQ(true, l_Object1 == l_Object2);
}
