// -*- mode: c++ -*-

// Copyright (c) 2010 Google Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>

// dwarf2diehander_unittest.cc: Unit tests for google_breakpad::DIEDispatcher.

#include <stdint.h>

#include <string>
#include <utility>

#include "breakpad_googletest_includes.h"

#include "common/dwarf/dwarf2diehandler.h"
#include "common/using_std_string.h"

using std::make_pair;

using ::testing::_;
using ::testing::ContainerEq;
using ::testing::ElementsAreArray;
using ::testing::Eq;
using ::testing::InSequence;
using ::testing::Return;
using ::testing::Sequence;
using ::testing::StrEq;

using dwarf2reader::DIEDispatcher;
using dwarf2reader::DIEHandler;
using dwarf2reader::DwarfAttribute;
using dwarf2reader::DwarfForm;
using dwarf2reader::DwarfTag;
using dwarf2reader::RootDIEHandler;

class MockDIEHandler: public DIEHandler {
 public:
  MOCK_METHOD3(ProcessAttributeUnsigned,
               void(DwarfAttribute, DwarfForm, uint64));
  MOCK_METHOD3(ProcessAttributeSigned,
               void(DwarfAttribute, DwarfForm, int64));
  MOCK_METHOD3(ProcessAttributeReference,
               void(DwarfAttribute, DwarfForm, uint64));
  MOCK_METHOD4(ProcessAttributeBuffer,
               void(DwarfAttribute, DwarfForm, const uint8_t *, uint64));
  MOCK_METHOD3(ProcessAttributeString,
               void(DwarfAttribute, DwarfForm, const string &));
  MOCK_METHOD3(ProcessAttributeSignature,
               void(DwarfAttribute, DwarfForm, uint64));
  MOCK_METHOD0(EndAttributes, bool());
  MOCK_METHOD2(FindChildHandler, DIEHandler *(uint64, DwarfTag));
  MOCK_METHOD0(Finish, void());
};

class MockRootDIEHandler: public RootDIEHandler {
 public:
  MOCK_METHOD3(ProcessAttributeUnsigned,
               void(DwarfAttribute, DwarfForm, uint64));
  MOCK_METHOD3(ProcessAttributeSigned,
               void(DwarfAttribute, DwarfForm, int64));
  MOCK_METHOD3(ProcessAttributeReference,
               void(DwarfAttribute, DwarfForm, uint64));
  MOCK_METHOD4(ProcessAttributeBuffer,
               void(DwarfAttribute, DwarfForm, const uint8_t *, uint64));
  MOCK_METHOD3(ProcessAttributeString,
               void(DwarfAttribute, DwarfForm, const string &));
  MOCK_METHOD3(ProcessAttributeSignature,
               void(DwarfAttribute, DwarfForm, uint64));
  MOCK_METHOD0(EndAttributes, bool());
  MOCK_METHOD2(FindChildHandler, DIEHandler *(uint64, DwarfTag));
  MOCK_METHOD0(Finish, void());
  MOCK_METHOD5(StartCompilationUnit, bool(uint64, uint8, uint8, uint64, uint8));
  MOCK_METHOD2(StartRootDIE, bool(uint64, DwarfTag));
};

// If the handler elects to skip the compilation unit, the dispatcher
// should tell the reader so.
TEST(Dwarf2DIEHandler, SkipCompilationUnit) {
  Sequence s;
  MockRootDIEHandler mock_root_handler;
  DIEDispatcher die_dispatcher(&mock_root_handler);

  EXPECT_CALL(mock_root_handler,
              StartCompilationUnit(0x8d42aed77cfccf3eLL,
                                   0x89, 0xdc,
                                   0x2ecb4dc778a80f21LL,
                                   0x66))
      .InSequence(s)
      .WillOnce(Return(false));

  EXPECT_FALSE(die_dispatcher.StartCompilationUnit(0x8d42aed77cfccf3eLL,
                                                   0x89, 0xdc,
                                                   0x2ecb4dc778a80f21LL,
                                                   0x66));
}

// If the handler elects to skip the root DIE, the dispatcher should
// tell the reader so.
TEST(Dwarf2DIEHandler, SkipRootDIE) {
  Sequence s;
  MockRootDIEHandler mock_root_handler;
  DIEDispatcher die_dispatcher(&mock_root_handler);

  EXPECT_CALL(mock_root_handler,
              StartCompilationUnit(0xde8994029fc8b999LL, 0xf4, 0x02,
                                   0xb00febffa76e2b2bLL, 0x5c))
      .InSequence(s)
      .WillOnce(Return(true));
  EXPECT_CALL(mock_root_handler,
              StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6))
      .InSequence(s)
      .WillOnce(Return(false));

  EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0xde8994029fc8b999LL, 
                                                  0xf4, 0x02,
                                                  0xb00febffa76e2b2bLL, 0x5c));
  EXPECT_FALSE(die_dispatcher.StartDIE(0x7d08242b4b510cf2LL,
                                       (DwarfTag) 0xb4f98da6));
  die_dispatcher.EndDIE(0x7d08242b4b510cf2LL);
}

// If the handler elects to skip the root DIE's children, the
// dispatcher should tell the reader so --- and avoid deleting the
// root handler.
TEST(Dwarf2DIEHandler, SkipRootDIEChildren) {
  MockRootDIEHandler mock_root_handler;
  DIEDispatcher die_dispatcher(&mock_root_handler);

  {
    InSequence s;

    EXPECT_CALL(mock_root_handler,
                StartCompilationUnit(0x15d6897480cc65a7LL, 0x26, 0xa0,
                                     0x09f8bf0767f91675LL, 0xdb))
      .WillOnce(Return(true));
    EXPECT_CALL(mock_root_handler,
                StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6))
      .WillOnce(Return(true));
    // Please don't tell me about my children.
    EXPECT_CALL(mock_root_handler, EndAttributes())
      .WillOnce(Return(false));
    EXPECT_CALL(mock_root_handler, Finish())
      .WillOnce(Return());
  }

  EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0x15d6897480cc65a7LL,
                                                  0x26, 0xa0,
                                                  0x09f8bf0767f91675LL, 0xdb));
  EXPECT_TRUE(die_dispatcher.StartDIE(0x7d08242b4b510cf2LL,
                                      (DwarfTag) 0xb4f98da6));
  EXPECT_FALSE(die_dispatcher.StartDIE(0x435150ceedccda18LL,
                                       (DwarfTag) 0xc3a17bba));
  die_dispatcher.EndDIE(0x435150ceedccda18LL);
  die_dispatcher.EndDIE(0x7d08242b4b510cf2LL);
}

// The dispatcher should pass attribute values through to the die
// handler accurately.
TEST(Dwarf2DIEHandler, PassAttributeValues) {
  MockRootDIEHandler mock_root_handler;
  DIEDispatcher die_dispatcher(&mock_root_handler);

  const uint8_t buffer[10] = {
    0x24, 0x24, 0x35, 0x9a, 0xca, 0xcf, 0xa8, 0x84, 0xa7, 0x18
  };
  string str = "\xc8\x26\x2e\x0d\xa4\x9c\x37\xd6\xfb\x1d";

  // Set expectations.
  {
    InSequence s;

    // We'll like the compilation unit header.
    EXPECT_CALL(mock_root_handler,
                StartCompilationUnit(0x8d42aed77cfccf3eLL, 0x89, 0xdc,
                                     0x2ecb4dc778a80f21LL, 0x66))
      .WillOnce(Return(true));

    // We'll like the root DIE.
    EXPECT_CALL(mock_root_handler,
                StartRootDIE(0xe2222da01e29f2a9LL, (DwarfTag) 0x9829445c))
      .WillOnce(Return(true));

    // Expect some attribute values.
    EXPECT_CALL(mock_root_handler,
                ProcessAttributeUnsigned((DwarfAttribute) 0x1cc0bfed,
                                         (DwarfForm) 0x424f1468,
                                         0xa592571997facda1ULL))
      .WillOnce(Return());
    EXPECT_CALL(mock_root_handler,
                ProcessAttributeSigned((DwarfAttribute) 0x43694dc9,
                                       (DwarfForm) 0xf6f78901L,
                                       0x92602a4e3bf1f446LL))
      .WillOnce(Return());
    EXPECT_CALL(mock_root_handler,
                ProcessAttributeReference((DwarfAttribute) 0x4033e8cL,
                                          (DwarfForm) 0xf66fbe0bL,
                                          0x50fddef44734fdecULL))
      .WillOnce(Return());
    EXPECT_CALL(mock_root_handler,
                ProcessAttributeBuffer((DwarfAttribute) 0x25d7e0af,
                                       (DwarfForm) 0xe99a539a,
                                       buffer, sizeof(buffer)))
      .WillOnce(Return());
    EXPECT_CALL(mock_root_handler,
                ProcessAttributeString((DwarfAttribute) 0x310ed065,
                                       (DwarfForm) 0x15762fec,
                                       StrEq(str)))
      .WillOnce(Return());
    EXPECT_CALL(mock_root_handler,
                ProcessAttributeSignature((DwarfAttribute) 0x58790d72,
                                          (DwarfForm) 0x4159f138,
                                          0x94682463613e6a5fULL))
      .WillOnce(Return());
    EXPECT_CALL(mock_root_handler, EndAttributes())
      .WillOnce(Return(true));
    EXPECT_CALL(mock_root_handler, FindChildHandler(_, _))
      .Times(0);
    EXPECT_CALL(mock_root_handler, Finish())
      .WillOnce(Return());
  }

  // Drive the dispatcher.

  // Report the CU header.
  EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0x8d42aed77cfccf3eLL,
                                                  0x89, 0xdc,
                                                  0x2ecb4dc778a80f21LL,
                                                  0x66));
  // Report the root DIE.
  EXPECT_TRUE(die_dispatcher.StartDIE(0xe2222da01e29f2a9LL,
                                      (DwarfTag) 0x9829445c));

  // Report some attribute values.
  die_dispatcher.ProcessAttributeUnsigned(0xe2222da01e29f2a9LL,
                                          (DwarfAttribute) 0x1cc0bfed,
                                          (DwarfForm) 0x424f1468,
                                          0xa592571997facda1ULL);
  die_dispatcher.ProcessAttributeSigned(0xe2222da01e29f2a9LL,
                                        (DwarfAttribute) 0x43694dc9,
                                        (DwarfForm) 0xf6f78901,
                                        0x92602a4e3bf1f446LL);
  die_dispatcher.ProcessAttributeReference(0xe2222da01e29f2a9LL,
                                           (DwarfAttribute) 0x4033e8c,
                                           (DwarfForm) 0xf66fbe0b,
                                           0x50fddef44734fdecULL);
  die_dispatcher.ProcessAttributeBuffer(0xe2222da01e29f2a9LL,
                                        (DwarfAttribute) 0x25d7e0af,
                                        (DwarfForm) 0xe99a539a,
                                        buffer, sizeof(buffer));
  die_dispatcher.ProcessAttributeString(0xe2222da01e29f2a9LL,
                                        (DwarfAttribute) 0x310ed065,
                                        (DwarfForm) 0x15762fec,
                                        str);
  die_dispatcher.ProcessAttributeSignature(0xe2222da01e29f2a9LL,
                                           (DwarfAttribute) 0x58790d72,
                                           (DwarfForm) 0x4159f138,
                                           0x94682463613e6a5fULL);

  // Finish the root DIE (and thus the CU).
  die_dispatcher.EndDIE(0xe2222da01e29f2a9LL);
}

TEST(Dwarf2DIEHandler, FindAndSkipChildren) {
  MockRootDIEHandler mock_root_handler;
  MockDIEHandler *mock_child1_handler = new(MockDIEHandler);
  MockDIEHandler *mock_child3_handler = new(MockDIEHandler);
  DIEDispatcher die_dispatcher(&mock_root_handler);

  {
    InSequence s;

    // We'll like the compilation unit header.
    EXPECT_CALL(mock_root_handler,
                StartCompilationUnit(0x9ec1e6d05e434a0eLL, 0xeb, 0x21,
                                     0x47dd3c764275a216LL, 0xa5))
      .WillOnce(Return(true));

    // Root DIE.
    {
      EXPECT_CALL(mock_root_handler,
                  StartRootDIE(0x15f0e06bdfe3c372LL, (DwarfTag) 0xf5d60c59))
        .WillOnce(Return(true));
      EXPECT_CALL(mock_root_handler,
                  ProcessAttributeSigned((DwarfAttribute) 0xf779a642,
                                         (DwarfForm) 0x2cb63027,
                                         0x18e744661769d08fLL))
        .WillOnce(Return());
      EXPECT_CALL(mock_root_handler, EndAttributes())
        .WillOnce(Return(true));

      // First child DIE.
      EXPECT_CALL(mock_root_handler,
                  FindChildHandler(0x149f644f8116fe8cLL,
                                   (DwarfTag) 0xac2cbd8c))
        .WillOnce(Return(mock_child1_handler));
      {
        EXPECT_CALL(*mock_child1_handler,
                    ProcessAttributeSigned((DwarfAttribute) 0xa6fd6f65,
                                           (DwarfForm) 0xe4f64c41,
                                           0x1b04e5444a55fe67LL))
          .WillOnce(Return());
        EXPECT_CALL(*mock_child1_handler, EndAttributes())
          .WillOnce(Return(false));
        // Skip first grandchild DIE and first great-grandchild DIE.
        EXPECT_CALL(*mock_child1_handler, Finish())
          .WillOnce(Return());
      }

      // Second child DIE.  Root handler will decline to return a handler
      // for this child.
      EXPECT_CALL(mock_root_handler,
                  FindChildHandler(0x97412be24875de9dLL,
                                   (DwarfTag) 0x505a068b))
        .WillOnce(Return((DIEHandler *) NULL));

      // Third child DIE.
      EXPECT_CALL(mock_root_handler,
                  FindChildHandler(0x753c964c8ab538aeLL,
                                   (DwarfTag) 0x8c22970e))
        .WillOnce(Return(mock_child3_handler));
      {
        EXPECT_CALL(*mock_child3_handler,
                    ProcessAttributeSigned((DwarfAttribute) 0x4e2b7cfb,
                                           (DwarfForm) 0x610b7ae1,
                                           0x3ea5c609d7d7560fLL))
          .WillOnce(Return());
        EXPECT_CALL(*mock_child3_handler, EndAttributes())
          .WillOnce(Return(true));
        EXPECT_CALL(*mock_child3_handler, Finish())
          .WillOnce(Return());
      }

      EXPECT_CALL(mock_root_handler, Finish())
        .WillOnce(Return());
    }
  }

    
  // Drive the dispatcher.

  // Report the CU header.
  EXPECT_TRUE(die_dispatcher
              .StartCompilationUnit(0x9ec1e6d05e434a0eLL, 0xeb, 0x21,
                                    0x47dd3c764275a216LL, 0xa5));
  // Report the root DIE.
  {
    EXPECT_TRUE(die_dispatcher.StartDIE(0x15f0e06bdfe3c372LL,
                                        (DwarfTag) 0xf5d60c59));
    die_dispatcher.ProcessAttributeSigned(0x15f0e06bdfe3c372LL,
                                          (DwarfAttribute) 0xf779a642,
                                          (DwarfForm) 0x2cb63027,
                                          0x18e744661769d08fLL);

    // First child DIE.
    {
      EXPECT_TRUE(die_dispatcher.StartDIE(0x149f644f8116fe8cLL,
                                          (DwarfTag) 0xac2cbd8c));
      die_dispatcher.ProcessAttributeSigned(0x149f644f8116fe8cLL,
                                            (DwarfAttribute) 0xa6fd6f65,
                                            (DwarfForm) 0xe4f64c41,
                                            0x1b04e5444a55fe67LL);

      // First grandchild DIE.  Will be skipped.
      {
        EXPECT_FALSE(die_dispatcher.StartDIE(0xd68de1ee0bd29419LL,
                                            (DwarfTag) 0x22f05a15));
        // First great-grandchild DIE.  Will be skipped without being
        // mentioned to any handler.
        {
          EXPECT_FALSE(die_dispatcher
                       .StartDIE(0xb3076285d25cac25LL,
                                 (DwarfTag) 0xcff4061b));
          die_dispatcher.EndDIE(0xb3076285d25cac25LL);          
        }
        die_dispatcher.EndDIE(0xd68de1ee0bd29419LL);
      }
      die_dispatcher.EndDIE(0x149f644f8116fe8cLL);
    }

    // Second child DIE.  Root handler will decline to find a handler for it.
    {
      EXPECT_FALSE(die_dispatcher.StartDIE(0x97412be24875de9dLL,
                                           (DwarfTag) 0x505a068b));
      die_dispatcher.EndDIE(0x97412be24875de9dLL);
    }
    
    // Third child DIE.
    {
      EXPECT_TRUE(die_dispatcher.StartDIE(0x753c964c8ab538aeLL,
                                          (DwarfTag) 0x8c22970e));
      die_dispatcher.ProcessAttributeSigned(0x753c964c8ab538aeLL,
                                            (DwarfAttribute) 0x4e2b7cfb,
                                            (DwarfForm) 0x610b7ae1,
                                            0x3ea5c609d7d7560fLL);
      die_dispatcher.EndDIE(0x753c964c8ab538aeLL);
    }
    
    // Finish the root DIE (and thus the CU).
    die_dispatcher.EndDIE(0x15f0e06bdfe3c372LL);
  }
}

// The DIEDispatcher destructor is supposed to delete all handlers on
// the stack, except for the root.
TEST(Dwarf2DIEHandler, FreeHandlersOnStack) {
  MockRootDIEHandler mock_root_handler;
  MockDIEHandler *mock_child_handler = new(MockDIEHandler);
  MockDIEHandler *mock_grandchild_handler = new(MockDIEHandler);

  {
    InSequence s;

    // We'll like the compilation unit header.
    EXPECT_CALL(mock_root_handler,
                StartCompilationUnit(0x87b41ba8381cd71cLL, 0xff, 0x89,
                                     0x76d392ff393ddda2LL, 0xbf))
      .WillOnce(Return(true));

    // Root DIE.
    {
      EXPECT_CALL(mock_root_handler,
                  StartRootDIE(0xbf13b761691ddc91LL, (DwarfTag) 0x98980361))
        .WillOnce(Return(true));
      EXPECT_CALL(mock_root_handler, EndAttributes())
        .WillOnce(Return(true));
      
      // Child DIE.
      EXPECT_CALL(mock_root_handler,
                  FindChildHandler(0x058f09240c5fc8c9LL,
                                   (DwarfTag) 0x898bf0d0))
        .WillOnce(Return(mock_child_handler));
      {
        EXPECT_CALL(*mock_child_handler, EndAttributes())
          .WillOnce(Return(true));

        // Grandchild DIE.
        EXPECT_CALL(*mock_child_handler,
                    FindChildHandler(0x32dc00c9945dc0c8LL,
                                     (DwarfTag) 0x2802d007))
          .WillOnce(Return(mock_grandchild_handler));
        {
          EXPECT_CALL(*mock_grandchild_handler,
                      ProcessAttributeSigned((DwarfAttribute) 0x4e2b7cfb,
                                             (DwarfForm) 0x610b7ae1,
                                             0x3ea5c609d7d7560fLL))
            .WillOnce(Return());

          // At this point, we abandon the traversal, so none of the
          // usual stuff should get called.
          EXPECT_CALL(*mock_grandchild_handler, EndAttributes())
            .Times(0);
          EXPECT_CALL(*mock_grandchild_handler, Finish())
            .Times(0);
        }

        EXPECT_CALL(*mock_child_handler, Finish())
          .Times(0);
      }

      EXPECT_CALL(mock_root_handler, Finish())
        .Times(0);
    }
  }

  // The dispatcher.
  DIEDispatcher die_dispatcher(&mock_root_handler);
  
  // Report the CU header.
  EXPECT_TRUE(die_dispatcher
              .StartCompilationUnit(0x87b41ba8381cd71cLL, 0xff, 0x89,
                                    0x76d392ff393ddda2LL, 0xbf));
  // Report the root DIE.
  {
    EXPECT_TRUE(die_dispatcher.StartDIE(0xbf13b761691ddc91LL,
                                        (DwarfTag) 0x98980361));

    // Child DIE.
    {
      EXPECT_TRUE(die_dispatcher.StartDIE(0x058f09240c5fc8c9LL,
                                          (DwarfTag) 0x898bf0d0));

      // Grandchild DIE.
      {
        EXPECT_TRUE(die_dispatcher.StartDIE(0x32dc00c9945dc0c8LL,
                                            (DwarfTag) 0x2802d007));
        die_dispatcher.ProcessAttributeSigned(0x32dc00c9945dc0c8LL,
                                              (DwarfAttribute) 0x4e2b7cfb,
                                              (DwarfForm) 0x610b7ae1,
                                              0x3ea5c609d7d7560fLL);

        // Stop the traversal abruptly, so that there will still be
        // handlers on the stack when the dispatcher is destructed.

        // No EndDIE call...
      }
      // No EndDIE call...
    }
    // No EndDIE call...
  }
}
