// Copyright 2011 Google Inc. All Rights Reserved.

#include "dex_file.h"

#include "UniquePtr.h"
#include "common_test.h"

namespace art {

class DexFileTest : public CommonTest {};

TEST_F(DexFileTest, Open) {
  UniquePtr<const DexFile> dex(OpenTestDexFile("Nested"));
  ASSERT_TRUE(dex.get() != NULL);
}

// Although this is the same content logically as the Nested test dex,
// the DexFileHeader test is sensitive to subtle changes in the
// contents due to the checksum etc, so we embed the exact input here.
//
// class Nested {
//     class Inner {
//     }
// }
static const char kRawDex[] =
  "ZGV4CjAzNQAQedgAe7gM1B/WHsWJ6L7lGAISGC7yjD2IAwAAcAAAAHhWNBIAAAAAAAAAAMQCAAAP"
  "AAAAcAAAAAcAAACsAAAAAgAAAMgAAAABAAAA4AAAAAMAAADoAAAAAgAAAAABAABIAgAAQAEAAK4B"
  "AAC2AQAAvQEAAM0BAADXAQAA+wEAABsCAAA+AgAAUgIAAF8CAABiAgAAZgIAAHMCAAB5AgAAgQIA"
  "AAIAAAADAAAABAAAAAUAAAAGAAAABwAAAAkAAAAJAAAABgAAAAAAAAAKAAAABgAAAKgBAAAAAAEA"
  "DQAAAAAAAQAAAAAAAQAAAAAAAAAFAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAIAAAAiAEAAKsCAAAA"
  "AAAAAQAAAAAAAAAFAAAAAAAAAAgAAACYAQAAuAIAAAAAAAACAAAAlAIAAJoCAAABAAAAowIAAAIA"
  "AgABAAAAiAIAAAYAAABbAQAAcBACAAAADgABAAEAAQAAAI4CAAAEAAAAcBACAAAADgBAAQAAAAAA"
  "AAAAAAAAAAAATAEAAAAAAAAAAAAAAAAAAAEAAAABAAY8aW5pdD4ABUlubmVyAA5MTmVzdGVkJElu"
  "bmVyOwAITE5lc3RlZDsAIkxkYWx2aWsvYW5ub3RhdGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2"
  "aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAhTGRhbHZpay9hbm5vdGF0aW9uL01lbWJlckNsYXNz"
  "ZXM7ABJMamF2YS9sYW5nL09iamVjdDsAC05lc3RlZC5qYXZhAAFWAAJWTAALYWNjZXNzRmxhZ3MA"
  "BG5hbWUABnRoaXMkMAAFdmFsdWUAAgEABw4AAQAHDjwAAgIBDhgBAgMCCwQADBcBAgQBDhwBGAAA"
  "AQEAAJAgAICABNQCAAABAAGAgATwAgAAEAAAAAAAAAABAAAAAAAAAAEAAAAPAAAAcAAAAAIAAAAH"
  "AAAArAAAAAMAAAACAAAAyAAAAAQAAAABAAAA4AAAAAUAAAADAAAA6AAAAAYAAAACAAAAAAEAAAMQ"
  "AAACAAAAQAEAAAEgAAACAAAAVAEAAAYgAAACAAAAiAEAAAEQAAABAAAAqAEAAAIgAAAPAAAArgEA"
  "AAMgAAACAAAAiAIAAAQgAAADAAAAlAIAAAAgAAACAAAAqwIAAAAQAAABAAAAxAIAAA==";

TEST_F(DexFileTest, Header) {
  ScratchFile tmp;
  UniquePtr<const DexFile> raw(OpenDexFileBase64(kRawDex, tmp.GetFilename()));
  ASSERT_TRUE(raw.get() != NULL);

  const DexFile::Header& header = raw->GetHeader();
  // TODO: header.magic_
  EXPECT_EQ(0x00d87910U, header.checksum_);
  // TODO: header.signature_
  EXPECT_EQ(904U, header.file_size_);
  EXPECT_EQ(112U, header.header_size_);
  EXPECT_EQ(0U, header.link_size_);
  EXPECT_EQ(0U, header.link_off_);
  EXPECT_EQ(15U, header.string_ids_size_);
  EXPECT_EQ(112U, header.string_ids_off_);
  EXPECT_EQ(7U, header.type_ids_size_);
  EXPECT_EQ(172U, header.type_ids_off_);
  EXPECT_EQ(2U, header.proto_ids_size_);
  EXPECT_EQ(200U, header.proto_ids_off_);
  EXPECT_EQ(1U, header.field_ids_size_);
  EXPECT_EQ(224U, header.field_ids_off_);
  EXPECT_EQ(3U, header.method_ids_size_);
  EXPECT_EQ(232U, header.method_ids_off_);
  EXPECT_EQ(2U, header.class_defs_size_);
  EXPECT_EQ(256U, header.class_defs_off_);
  EXPECT_EQ(584U, header.data_size_);
  EXPECT_EQ(320U, header.data_off_);
}

TEST_F(DexFileTest, ClassDefs) {
  UniquePtr<const DexFile> raw(OpenTestDexFile("Nested"));
  ASSERT_TRUE(raw.get() != NULL);
  EXPECT_EQ(2U, raw->NumClassDefs());

  const DexFile::ClassDef& c0 = raw->GetClassDef(0);
  EXPECT_STREQ("LNested$Inner;", raw->GetClassDescriptor(c0));

  const DexFile::ClassDef& c1 = raw->GetClassDef(1);
  EXPECT_STREQ("LNested;", raw->GetClassDescriptor(c1));
}

TEST_F(DexFileTest, CreateMethodDescriptor) {
  UniquePtr<const DexFile> raw(OpenTestDexFile("CreateMethodDescriptor"));
  ASSERT_TRUE(raw.get() != NULL);
  EXPECT_EQ(1U, raw->NumClassDefs());

  const DexFile::ClassDef& class_def = raw->GetClassDef(0);
  ASSERT_STREQ("LCreateMethodDescriptor;", raw->GetClassDescriptor(class_def));

  const byte* class_data = raw->GetClassData(class_def);
  ASSERT_TRUE(class_data != NULL);
  DexFile::ClassDataHeader header = raw->ReadClassDataHeader(&class_data);

  EXPECT_EQ(1u, header.direct_methods_size_);

  // Check the descriptor for the static initializer.
  {
    uint32_t last_idx = 0;
    ASSERT_EQ(1U, header.direct_methods_size_);
    DexFile::Method method;
    raw->dexReadClassDataMethod(&class_data, &method, &last_idx);
    const DexFile::MethodId& method_id = raw->GetMethodId(method.method_idx_);
    uint32_t proto_idx = method_id.proto_idx_;
    const char* name = raw->dexStringById(method_id.name_idx_);
    ASSERT_STREQ("<init>", name);
    int32_t length;
    std::string descriptor(raw->CreateMethodDescriptor(proto_idx, &length));
    ASSERT_EQ("()V", descriptor);
  }

  // Check both virtual methods.
  ASSERT_EQ(2U, header.virtual_methods_size_);
  uint32_t last_idx = 0;

  {
    DexFile::Method method;
    raw->dexReadClassDataMethod(&class_data, &method, &last_idx);
    const DexFile::MethodId& method_id = raw->GetMethodId(method.method_idx_);

    const char* name = raw->dexStringById(method_id.name_idx_);
    ASSERT_STREQ("m1", name);

    uint32_t proto_idx = method_id.proto_idx_;
    int32_t length;
    std::string descriptor(raw->CreateMethodDescriptor(proto_idx, &length));
    ASSERT_EQ("(IDJLjava/lang/Object;)Ljava/lang/Float;", descriptor);
  }

  {
    DexFile::Method method;
    raw->dexReadClassDataMethod(&class_data, &method, &last_idx);
    const DexFile::MethodId& method_id = raw->GetMethodId(method.method_idx_);

    const char* name = raw->dexStringById(method_id.name_idx_);
    ASSERT_STREQ("m2", name);

    uint32_t proto_idx = method_id.proto_idx_;
    int32_t length;
    std::string descriptor(raw->CreateMethodDescriptor(proto_idx, &length));
    ASSERT_EQ("(ZSC)LCreateMethodDescriptor;", descriptor);
  }
}

}  // namespace art