text: /**
  * @file tests/bin2llvmir/providers/tests/fileimage_tests.cpp
  * @brief Tests for the @c FileImageProvider.
  * @copyright (c) 2017 Avast Software, licensed under the MIT license
  */
  
  #include "retdec/common/tool_info.h"
  #include "retdec/utils/system.h"
  #include "retdec/bin2llvmir/providers/fileimage.h"
  #include "bin2llvmir/utils/llvmir_tests.h"
  #include "retdec/fileformat/file_format/raw_data/raw_data_format.h"
  #include "retdec/loader/image_factory.h"
  #include "retdec/loader/loader/raw_data/raw_data_image.h"
  
  using namespace ::testing;
  using namespace llvm;
  using namespace retdec::fileformat;
  using namespace retdec::loader;
  
  namespace retdec {
  namespace bin2llvmir {
  namespace tests {
  
  //
  //=============================================================================
  //  FileImage
  //=============================================================================
  //
  
  /**
   * @brief Tests for the @c FileImage pass.
   */
  class FileImageTests: public LlvmIrTests
  {
  
  };
  
  //
  //llvm::Constant* getConstant(
  //		llvm::Type* type,
  //		retdec::common::Address addr)
  //
  
  TEST_F(FileImageTests, getConstantReadsCorrectValuesForI1)
  {
  	auto format = createFormat();
  	int8_t i1 = 1;
  	auto i1Pos = format->appendData(i1); // alignment
  
  	auto c = Config::empty(module.get());
  	auto image = FileImage(module.get(), format, &c);
  	Type* i1Type = Type::getInt1Ty(module->getContext());
  	ConstantInt* i1Const = dyn_cast_or_null<ConstantInt>(image.getConstant(
  			i1Type, i1Pos));
  	ASSERT_NE(nullptr, i1Const);
  	EXPECT_EQ(i1Type, i1Const->getType());
  	EXPECT_EQ(i1, i1Const->getZExtValue());
  }
  
  TEST_F(FileImageTests, getConstantReadsCorrectValuesForI8)
  {
  	auto format = createFormat();
  	int8_t i8 = 123;
  	auto i8Pos = format->appendData(i8);
  
  	auto c = Config::empty(module.get());
  	auto image = FileImage(module.get(), format, &c);
  	Type* i8Type = Type::getInt8Ty(module->getContext());
  	ConstantInt* i8Const = dyn_cast_or_null<ConstantInt>(image.getConstant(
  			i8Type, i8Pos));
  	ASSERT_NE(nullptr, i8Const);
  	EXPECT_EQ(i8Type, i8Const->getType());
  	EXPECT_EQ(i8, i8Const->getSExtValue());
  }
  
  TEST_F(FileImageTests, getConstantReadsCorrectValuesForI16)
  {
  	auto format = createFormat();
  	int16_t i16 = 456;
  	auto i16Pos = format->appendData(i16);
  
  	auto c = Config::empty(module.get());
  	auto image = FileImage(module.get(), format, &c);
  	Type* i16Type = Type::getInt16Ty(module->getContext());
  	ConstantInt* i16Const = dyn_cast_or_null<ConstantInt>(image.getConstant(
  			i16Type, i16Pos));
  
  	ASSERT_NE(nullptr, i16Const);
  	EXPECT_EQ(i16Type, i16Const->getType());
  	EXPECT_EQ(i16, i16Const->getSExtValue());
  }
  
  TEST_F(FileImageTests, getConstantReadsCorrectValuesForPositiveI32)
  {
  	auto format = createFormat();
  	int32_t i32 = 789;
  	auto i32Pos = format->appendData(i32);
  
  	auto c = Config::empty(module.get());
  	auto image = FileImage(module.get(), format, &c);
  	Type* i32Type = Type::getInt32Ty(module->getContext());
  	ConstantInt* i32Const = dyn_cast_or_null<ConstantInt>(image.getConstant(
  			i32Type, i32Pos));
  
  	ASSERT_NE(nullptr, i32Const);
  	EXPECT_EQ(i32Type, i32Const->getType());
  	EXPECT_EQ(i32, i32Const->getSExtValue());
  }
  
  TEST_F(FileImageTests, getConstantReadsCorrectValuesForNegativeI32)
  {
  	auto format = createFormat();
  	int32_t i32 = -789;
  	auto i32Pos = format->appendData(i32);
  
  	auto c = Config::empty(module.get());
  	auto image = FileImage(module.get(), format, &c);
  	Type* i32Type = Type::getInt32Ty(module->getContext());
  	ConstantInt* i32Const = dyn_cast_or_null<ConstantInt>(image.getConstant(
  			i32Type, i32Pos));
  
  	ASSERT_NE(nullptr, i32Const);
  	EXPECT_EQ(i32Type, i32Const->getType());
  	EXPECT_EQ(i32, i32Const->getSExtValue());
  }
  
  TEST_F(FileImageTests, getConstantReadsCorrectValuesForI64)
  {
  	auto format = createFormat();
  	int64_t i64 = 987;
  	auto i64Pos = format->appendData(i64);
  
  	auto c = Config::empty(module.get());
  	auto image = FileImage(module.get(), format, &c);
  	Type* i64Type = Type::getInt64Ty(module->getContext());
  	ConstantInt* i64Const = dyn_cast_or_null<ConstantInt>(image.getConstant(
  			i64Type, i64Pos));
  
  	ASSERT_NE(nullptr, i64Const);
  	EXPECT_EQ(i64Type, i64Const->getType());
  	EXPECT_EQ(i64, i64Const->getSExtValue());
  }
  
  TEST_F(FileImageTests, getConstantReadsCorrectValuesForHalf)
  {
  	auto format = createFormat();
  	float f = 2.0;
  	auto fPos = format->appendData(f);
  
  	auto c = Config::empty(module.get());
  	auto image = FileImage(module.get(), format, &c);
  	Type* fType = Type::getHalfTy(module->getContext());
  	ConstantFP* fConst = dyn_cast_or_null<ConstantFP>(image.getConstant(
  			fType, fPos));
  
  	ASSERT_NE(nullptr, fConst);
  	EXPECT_EQ(fType, fConst->getType());
  	EXPECT_TRUE(fConst->isExactlyValue(f));
  }
  
  TEST_F(FileImageTests, getConstantReadsCorrectValuesForFloat)
  {
  	auto format = createFormat();
  	float f = 2.0;
  	auto fPos = format->appendData(f);
  
  	auto c = Config::empty(module.get());
  	auto image = FileImage(module.get(), format, &c);
  	Type* fType = Type::getFloatTy(module->getContext());
  	ConstantFP* fConst = dyn_cast_or_null<ConstantFP>(image.getConstant(
  			fType, fPos));
  
  	ASSERT_NE(nullptr, fConst);
  	EXPECT_EQ(fType, fConst->getType());
  	EXPECT_TRUE(fConst->isExactlyValue(f));
  }
  
  TEST_F(FileImageTests, getConstantReadsCorrectValuesForDouble)
  {
  	auto format = createFormat();
  	double d = 4.0;
  	auto dPos = format->appendData(d);
  
  	auto c = Config::empty(module.get());
  	auto image = FileImage(module.get(), format, &c);
  	Type* dType = Type::getDoubleTy(module->getContext());
  	ConstantFP* dConst = dyn_cast_or_null<ConstantFP>(image.getConstant(
  			dType, dPos));
  
  	ASSERT_NE(nullptr, dConst);
  	EXPECT_EQ(dType, dConst->getType());
  	EXPECT_TRUE(dConst->isExactlyValue(d));
  }
  
  TEST_F(FileImageTests, getConstantReadsCorrectValuesForFp128)
  {
  	auto format = createFormat();
  	double d = 4.0;
  	auto dPos = format->appendData(d);
  
  	auto c = Config::empty(module.get());
  	auto image = FileImage(module.get(), format, &c);
  	Type* fp128Type = Type::getFP128Ty(module->getContext());
  	ConstantFP* dConst = dyn_cast_or_null<ConstantFP>(image.getConstant(
  			fp128Type, dPos));
  
  	ASSERT_NE(nullptr, dConst);
  	EXPECT_EQ(fp128Type, dConst->getType());
  	EXPECT_TRUE(dConst->isExactlyValue(d));
  }
  
  TEST_F(FileImageTests, getConstantReadsCorrectValuesForLongDouble)
  {
  	auto format = createFormat();
  	if (retdec::utils::systemHasLongDouble())
  	{
  		long double ld = 10.0;
  		auto ldPos = format->appendData(ld);
  
  		auto c = Config::empty(module.get());
  		auto image = FileImage(module.get(), format, &c);
  		Type* ldType = Type::getX86_FP80Ty(module->getContext());
  		ConstantFP* ldConst = dyn_cast_or_null<ConstantFP>(image.getConstant(
  				ldType, ldPos));
  		ASSERT_NE(nullptr, ldConst);
  		EXPECT_EQ(ldType, ldConst->getType());
  		EXPECT_TRUE(ldConst->isExactlyValue(ld));
  	}
  }
  
  TEST_F(FileImageTests, getConstantReadsCorrectValuesForPointerType)
  {
  	auto format = createFormat();
  	int32_t i32 = 789;
  	auto i32Pos = format->appendData(i32);
  
  	auto c = Config::empty(module.get());
  	auto image = FileImage(module.get(), format, &c);
  	Type* ptrType = Type::getInt32PtrTy(module->getContext());
  	ConstantExpr* ptrConst = dyn_cast_or_null<ConstantExpr>(image.getConstant(
  			ptrType, i32Pos));
  	ASSERT_NE(nullptr, ptrConst);
  
  	ConstantInt* intConst = dyn_cast<ConstantInt>(ptrConst->getOperand(0));
  	ASSERT_NE(nullptr, intConst);
  
  	EXPECT_EQ(i32, intConst->getSExtValue());
  }
  
  TEST_F(FileImageTests, getConstantReadsCorrectValuesForStringType)
  {
  	auto format = createFormat();
  	char str[] = "hello, how are you today?";
  	auto strPos = format->appendData(str);
  
  	auto c = Config::empty(module.get());
  	auto image = FileImage(module.get(), format, &c);
  	Type* strType = Type::getInt8PtrTy(module->getContext());
  
  	ConstantExpr* i8Const = dyn_cast_or_null<ConstantExpr>(
  			image.getConstant(strType, strPos));
  	ASSERT_NE(nullptr, i8Const);
  
  	GlobalVariable* strGv = dyn_cast<GlobalVariable>(
  			i8Const->getOperand(0));
  	ASSERT_NE(nullptr, strGv);
  
  	ConstantDataArray* strConst = dyn_cast_or_null<ConstantDataArray>(
  			strGv->getInitializer());
  	ASSERT_NE(nullptr, strConst);
  
  	EXPECT_EQ(std::string(str), strConst->getAsCString().str());
  }
  
  TEST_F(FileImageTests, getConstantReadsCorrectValuesForArrayType)
  {
  	auto format = createFormat();
  	auto pos = format->appendData(int32_t(1));
  	format->appendData(int32_t(2));
  	format->appendData(int32_t(3));
  	format->appendData(int32_t(4));
  	format->appendData(int32_t(5));
  	format->appendData(int32_t(6));
  	Type* i32Type = Type::getInt32Ty(module->getContext());
  	Type* arrayType = ArrayType::get(i32Type, 6);
  
  	auto c = Config::empty(module.get());
  	auto image = FileImage(module.get(), format, &c);
  	ConstantDataArray* arrayConst = dyn_cast_or_null<ConstantDataArray>(
  			image.getConstant(arrayType, pos));
  
  	ASSERT_NE(nullptr, arrayConst);
  	EXPECT_EQ(arrayType, arrayConst->getType());
  	EXPECT_EQ(6, arrayConst->getNumElements());
  
  	EXPECT_EQ(1, arrayConst->getElementAsInteger(0));
  	EXPECT_EQ(2, arrayConst->getElementAsInteger(1));
  	EXPECT_EQ(3, arrayConst->getElementAsInteger(2));
  	EXPECT_EQ(4, arrayConst->getElementAsInteger(3));
  	EXPECT_EQ(5, arrayConst->getElementAsInteger(4));
  	EXPECT_EQ(6, arrayConst->getElementAsInteger(5));
  }
  
  TEST_F(FileImageTests, getConstantReadsCorrectValuesForStructureType)
  {
  	auto format = createFormat();
  	struct s
  	{
  		int32_t i32;
  		float f;
  		double d;
  	} x = {123, 3.14f, 2.71};
  	auto pos = format->appendData(x);
  	std::vector<Type*> vc;
  	vc.push_back(Type::getInt32Ty(module->getContext()));
  	vc.push_back(Type::getFloatTy(module->getContext()));
  	vc.push_back(Type::getDoubleTy(module->getContext()));
  	Type* type = StructType::create(vc);
  
  	auto c = Config::empty(module.get());
  	auto image = FileImage(module.get(), format, &c);
  	ConstantStruct* structConst = dyn_cast_or_null<ConstantStruct>(
  			image.getConstant(type, pos));
  
  	ASSERT_NE(nullptr, structConst);
  	EXPECT_EQ(3, structConst->getNumOperands());
  	EXPECT_EQ(123, dyn_cast<ConstantInt>(structConst->getOperand(0))->getSExtValue());
  	EXPECT_TRUE(dyn_cast<ConstantFP>(structConst->getOperand(1))->isExactlyValue(3.14));
  	EXPECT_TRUE(dyn_cast<ConstantFP>(structConst->getOperand(2))->isExactlyValue(2.71));
  }
  
  //
  //llvm::Constant* getConstant(
  //		llvm::Module* module,
  //		retdec::loader::Image* objf,
  //		DebugFormat* dbgf,
  //		retdec::common::Address addr)
  //
  
  TEST_F(FileImageTests, getConstantReadsDetectsAndReads32BitInteger)
  {
  	auto format = createFormat();
  	int32_t i32 = 456;
  	format->appendData(123);
  	auto i32Pos = format->appendData(i32);
  	format->appendData(789);
  
  	auto c = Config::empty(module.get());
  	auto image = FileImage(module.get(), format, &c);
  	ConstantInt* i32Const = dyn_cast_or_null<ConstantInt>(image.getConstant(
  			nullptr, nullptr, i32Pos));
  
  	ASSERT_NE(nullptr, i32Const);
  	EXPECT_EQ(i32, i32Const->getSExtValue());
  }
  
  //
  //=============================================================================
  //  FileImageProviderTests
  //=============================================================================
  //
  
  /**
   * @brief Tests for the @c FileFileProvider pass.
   */
  class FileImageProviderTests: public LlvmIrTests
  {
  
  };
  
  TEST_F(FileImageProviderTests, addFileImageAddsFileImageForModule)
  {
  	std::stringstream emptySs;
  	auto format = std::make_unique<RawDataFormat>(emptySs);
  	if (format == nullptr)
  	{
  		throw std::runtime_error("failed to create RawDataFormat");
  	}
  	std::shared_ptr<RawDataFormat> formatShared(std::move(format));
  	auto c = Config::empty(module.get());
  	auto* r1 = FileImageProvider::addFileImage(module.get(), formatShared, &c);
  	auto* r2 = FileImageProvider::getFileImage(module.get());
  	FileImage* r3 = nullptr;
  	bool b = FileImageProvider::getFileImage(module.get(), r3);
  
  	EXPECT_NE(nullptr, r1);
  	EXPECT_EQ(r1, r2);
  	EXPECT_EQ(r1, r3);
  	EXPECT_TRUE(b);
  }
  
  // TODO: If we checked throw message here, we could replace regression tests
  // service.decompiler.errors.NoSectionsOrSegmentsErrorTest
  // with this unit test.
  //
  TEST_F(FileImageProviderTests, addFileImageThrowsForModuleWhenBadPathProvided)
  {
  	std::string path = "/this/is/a/bad/path";
  	auto c = Config::empty(module.get());
  	ASSERT_ANY_THROW(FileImageProvider::addFileImage(module.get(), path, &c));
  }
  
  TEST_F(FileImageProviderTests, clearRemovesAllData)
  {
  	std::stringstream emptySs;
  	auto format = std::make_unique<RawDataFormat>(emptySs);
  	if (format == nullptr)
  	{
  		throw std::runtime_error("failed to create RawDataFormat");
  	}
  	std::shared_ptr<RawDataFormat> formatShared(std::move(format));
  	auto c = Config::empty(module.get());
  	FileImageProvider::addFileImage(module.get(), formatShared, &c);
  	auto* r1 = FileImageProvider::getFileImage(module.get());
  	EXPECT_NE(nullptr, r1);
  
  	FileImageProvider::clear();
  	auto* r2 = FileImageProvider::getFileImage(module.get());
  	EXPECT_EQ(nullptr, r2);
  }
  
  } // namespace tests
  } // namespace bin2llvmir
  } // namespace retdec
  