TEST_F( SegmentTest, Remove )
{
	std::unordered_map<TID, uint32_t> values; // TID -> testData entry
	// Insert some records
	for ( uint32_t i = 0; i < 500; ++i )
	{
		// Select string/record to insert
		uint64_t r = rand() % testData.size();
		const std::string s = testData[r];

		// Insert record
		TID tid = segment->Insert( Record( static_cast<uint32_t>(s.size()),
										   reinterpret_cast<const uint8_t*>(s.c_str()) ) );
		EXPECT_EQ( values.find( tid ), values.end() ); // TIDs should not be overwritten
		values[tid] = static_cast<uint32_t>(r);
	}
	std::unordered_map<TID, bool> removed; // TID -> is removed
	
	// Remove records
	for ( std::pair<TID, uint32_t> tidValue : values )
	{
		if ( rand() > RAND_MAX * 0.5 )
		{
			bool removeSuccess = segment->Remove( tidValue.first );
			EXPECT_EQ( true, removeSuccess );
			removed.insert( std::make_pair( tidValue.first, true ) );
		}
		else
		{
			removed.insert( std::make_pair( tidValue.first, false ) );
		}
	}

	// Check over every record if it was correctly removed/kept
	for ( std::pair<TID, uint32_t> tidValue : values )
	{
		if ( removed[tidValue.first] )
		{
			Record rec = segment->Lookup( tidValue.first );
			EXPECT_EQ( 0, rec.GetLen() );
			EXPECT_EQ( nullptr, rec.GetData() );
		}
		else
		{
			const std::string& value = testData[tidValue.second];
			uint32_t len = static_cast<uint32_t>(value.size());
			Record rec = segment->Lookup( tidValue.first );
			EXPECT_EQ( len, rec.GetLen() );
			EXPECT_EQ( 0, memcmp( rec.GetData(), value.c_str(), len ) );
		}
	}
};
TEST_F(SegmentTest, InsertLookup )
{
	std::unordered_map<TID, uint32_t> values; // TID -> testData entry

	// Insert some records
	for ( uint32_t i = 0; i < 500; ++i )
	{
		// Select string/record to insert
		uint64_t r = rand() % testData.size();
		const std::string s = testData[r];

		// Insert record
		TID tid = segment->Insert( Record( static_cast<uint32_t>(s.size()),
									  reinterpret_cast<const uint8_t*>(s.c_str()) ) );
		EXPECT_EQ( values.find( tid ), values.end() ); // TIDs should not be overwritten
		values[tid] = static_cast<uint32_t>(r);
	}
	// Every value has to be the same as it was inserted
	for ( std::pair<TID, uint32_t> tidValue : values )
	{
		const std::string& value = testData[tidValue.second];
		uint32_t len = static_cast<uint32_t>(value.size());
		Record rec = segment->Lookup( tidValue.first );
		EXPECT_EQ( len, rec.GetLen() );
		EXPECT_EQ( 0, memcmp( rec.GetData(), value.c_str(), len ) );
	}
};
TEST_F( SegmentTest, Update )
{
	std::unordered_map<TID, uint32_t> values; // TID -> testData entry
	std::vector<TID> tids;

	// Insert some records
	for ( uint32_t i = 0; i < 500; ++i )
	{
		// Select string/record to insert
		uint64_t r = rand() % testData.size();
		const std::string s = testData[r];

		// Insert record
		TID tid = segment->Insert( Record( static_cast<uint32_t>(s.size()),
										   reinterpret_cast<const uint8_t*>(s.c_str()) ) );
		tids.push_back( tid );
		EXPECT_EQ( values.find( tid ), values.end() ); // TIDs should not be overwritten
		values[tid] = static_cast<uint32_t>(r);
	}
	
	// Update random records
	for ( uint32_t i = 0; i < 1000; ++i )
	{
		// Select new string / record
		TID target = tids[rand() % tids.size()];
		uint32_t r = rand() % testData.size();
		const std::string s = testData[r];

		// Replace old with new value
		bool success = segment->Update( target, Record( static_cast<uint32_t>(s.size()),
								 reinterpret_cast<const uint8_t*>(s.c_str()) ) );
		EXPECT_EQ( true, success );
		values[target] = static_cast<uint32_t>(r);
	}

	// Every value has to be the same as it was inserted/updated to
	for ( std::pair<TID, uint32_t> tidValue : values )
	{
		const std::string& value = testData[tidValue.second];
		uint32_t len = static_cast<uint32_t>(value.size());
		Record rec = segment->Lookup( tidValue.first );
		EXPECT_EQ( len, rec.GetLen() );
		EXPECT_EQ( 0, memcmp( rec.GetData(), value.c_str(), len ) );
	}
};
TEST_F( SegmentTest, RandomOperations )
{
	// Random interspersed operations
	std::unordered_map<TID, uint32_t> values; // TID -> testData entry
	std::vector<TID> tids;

	// Insert some baseline records
	for ( uint32_t i = 0; i < 100; ++i )
	{
		// Select string/record to insert
		uint64_t r = rand() % testData.size();
		const std::string s = testData[r];

		// Insert record
		TID tid = segment->Insert( Record( static_cast<uint32_t>(s.size()),
										   reinterpret_cast<const uint8_t*>(s.c_str()) ) );
		tids.push_back( tid );
		EXPECT_EQ( values.find( tid ), values.end() ); // TIDs should not be overwritten
		values[tid] = static_cast<uint32_t>(r);
	}

	// Random ops
	for ( uint32_t i = 0; i < 10000; ++i )
	{
		float chance = static_cast<float>(rand()) / RAND_MAX;
		if (chance < 0.70)
		{
			// Insert
			// Select string/record to insert
			uint64_t r = rand() % testData.size();
			const std::string s = testData[r];

			// Insert record
			TID tid = segment->Insert( Record( static_cast<uint32_t>(s.size()),
											   reinterpret_cast<const uint8_t*>(s.c_str()) ) );

			EXPECT_EQ( values.find( tid ), values.end() ); // TIDs should not be overwritten
			tids.push_back( tid );
			values[tid] = static_cast<uint32_t>(r);
		}
		else if (chance < 0.9)
		{
			// Update
			// Select new string / record
			TID target = tids[rand() % tids.size()];
			uint32_t r = rand() % testData.size();
			const std::string s = testData[r];

			// Replace old with new value
			bool success = segment->Update( target, Record( static_cast<uint32_t>(s.size()),
															reinterpret_cast<const uint8_t*>(s.c_str()) ) );

			EXPECT_EQ( true, success );
			values[target] = static_cast<uint32_t>(r);
		}
		else
		{
			// Remove
			uint32_t inTid = rand() % tids.size();
			TID target = tids[inTid];
			bool removeSuccess = segment->Remove( target );
			EXPECT_EQ( true, removeSuccess );
			// Remove from our tid map and tid vector as well
			values.erase( target );
			tids[inTid] = tids[tids.size() - 1];
			tids.pop_back();
		}
	}

	// Every value has to be the same as it was inserted/updated to
	for ( std::pair<TID, uint32_t> tidValue : values )
	{
		// Not removed, normal check
		const std::string& value = testData[tidValue.second];
		uint32_t len = static_cast<uint32_t>(value.size());
		Record rec = segment->Lookup( tidValue.first );
		EXPECT_EQ( len, rec.GetLen() );
		EXPECT_EQ( 0, memcmp( rec.GetData(), value.c_str(), len ) );
	}
};