Add unit test for top-level bookmarks.

Null FPDF_BOOKMARK represents the "root" bookmark, and must
not segv when asking about titles or children.

R=thestig@chromium.org

Review URL: https://codereview.chromium.org/1404723002 .
diff --git a/fpdfsdk/src/fpdfdoc_embeddertest.cpp b/fpdfsdk/src/fpdfdoc_embeddertest.cpp
index 8b1c4fb..c83b2eb 100644
--- a/fpdfsdk/src/fpdfdoc_embeddertest.cpp
+++ b/fpdfsdk/src/fpdfdoc_embeddertest.cpp
@@ -2,9 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "../../core/include/fxcrt/fx_string.h"
 #include "../../public/fpdf_doc.h"
 #include "../../public/fpdfview.h"
 #include "../../testing/embedder_test.h"
+#include "../../testing/fx_string_testhelpers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 class FPDFDocEmbeddertest : public EmbedderTest {};
@@ -60,3 +62,40 @@
 
   FPDF_ClosePage(page);
 }
+
+TEST_F(FPDFDocEmbeddertest, NoBookmarks) {
+  // Open a file with no bookmarks.
+  EXPECT_TRUE(OpenDocument("testing/resources/named_dests.pdf"));
+
+  // The non-existent top-level bookmark has no title.
+  unsigned short buf[128];
+  EXPECT_EQ(0, FPDFBookmark_GetTitle(nullptr, buf, sizeof(buf)));
+
+  // The non-existent top-level bookmark has no children.
+  EXPECT_EQ(nullptr, FPDFBookmark_GetFirstChild(document(), nullptr));
+}
+
+TEST_F(FPDFDocEmbeddertest, Bookmarks) {
+  // Open a file with two bookmarks.
+  EXPECT_TRUE(OpenDocument("testing/resources/bookmarks.pdf"));
+
+  // The existent top-level bookmark has no title.
+  unsigned short buf[128];
+  EXPECT_EQ(0, FPDFBookmark_GetTitle(nullptr, buf, sizeof(buf)));
+
+  FPDF_BOOKMARK child = FPDFBookmark_GetFirstChild(document(), nullptr);
+  EXPECT_NE(nullptr, child);
+  EXPECT_EQ(34, FPDFBookmark_GetTitle(child, buf, sizeof(buf)));
+  EXPECT_EQ(CFX_WideString(L"A Good Beginning"),
+            CFX_WideString::FromUTF16LE(buf, 16));
+
+  EXPECT_EQ(nullptr, FPDFBookmark_GetFirstChild(document(), child));
+
+  FPDF_BOOKMARK sibling = FPDFBookmark_GetNextSibling(document(), child);
+  EXPECT_NE(nullptr, sibling);
+  EXPECT_EQ(28, FPDFBookmark_GetTitle(sibling, buf, sizeof(buf)));
+  EXPECT_EQ(CFX_WideString(L"A Good Ending"),
+            CFX_WideString::FromUTF16LE(buf, 13));
+
+  EXPECT_EQ(nullptr, FPDFBookmark_GetNextSibling(document(), sibling));
+}
diff --git a/testing/resources/bookmarks.in b/testing/resources/bookmarks.in
new file mode 100644
index 0000000..dfa6e14
--- /dev/null
+++ b/testing/resources/bookmarks.in
@@ -0,0 +1,98 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /Outlines 14 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /Count 2
+  /Kids [
+    3 0 R
+    4 0 R
+  ]
+>>
+endobj
+% Page number 0.
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <</F1 15 0 R>>
+  >>
+  /Contents [21 0 R]
+  /MediaBox [0 0 612 792]
+>>
+endobj
+% Page number 1.
+{{object 4 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources  <<
+    /Font <</F1 15 0 R>>
+  >>
+  /Contents [22 0 R]
+  /MediaBox [0 0 612 792]
+>>
+endobj
+% First bookmark
+{{object 10 0}} <<
+  /Title (A Good Beginning)
+  /Parent 14 0 R
+  /Next 11 0 R
+  /Dest (foo)
+>>
+endobj
+% Last bookmark
+{{object 11 0}} <<
+  /Title (A Good Ending)
+  /Parent 14 0 R
+  /Prev 10 0 R
+  /Dest (bar)
+>>
+endobj
+% Root bookmark
+{{object 14 0}} <<
+  /Type /Outlines
+  /First 10 0 R
+  /Last  11 0 R
+  /Count 2
+>>
+endobj
+% Font resource.
+{{object 15 0}} <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Arial
+>>
+endobj
+% Content for page 0.
+{{object 21 0}} <<
+  /Length 0
+>>
+stream
+BT
+/F1 20 Tf
+100 600 TD (Page1)Tj
+ET
+endstream
+endobj
+% Content for page 1.
+{{object 22 0}} <<
+  /Length 0
+>>
+stream
+BT
+/F1 20 Tf
+100 600 TD (Page2)Tj
+ET
+endstream
+endobj
+{{xref}}
+trailer <<
+  /Size 6
+  /Root 1 0 R
+>>
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bookmarks.pdf b/testing/resources/bookmarks.pdf
new file mode 100644
index 0000000..824ce78
--- /dev/null
+++ b/testing/resources/bookmarks.pdf
@@ -0,0 +1,124 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /Outlines 14 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /Count 2
+  /Kids [
+    3 0 R
+    4 0 R
+  ]
+>>
+endobj
+% Page number 0.
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources <<
+    /Font <</F1 15 0 R>>
+  >>
+  /Contents [21 0 R]
+  /MediaBox [0 0 612 792]
+>>
+endobj
+% Page number 1.
+4 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Resources  <<
+    /Font <</F1 15 0 R>>
+  >>
+  /Contents [22 0 R]
+  /MediaBox [0 0 612 792]
+>>
+endobj
+% First bookmark
+10 0 obj <<
+  /Title (A Good Beginning)
+  /Parent 14 0 R
+  /Next 11 0 R
+  /Dest (foo)
+>>
+endobj
+% Last bookmark
+11 0 obj <<
+  /Title (A Good Ending)
+  /Parent 14 0 R
+  /Prev 10 0 R
+  /Dest (bar)
+>>
+endobj
+% Root bookmark
+14 0 obj <<
+  /Type /Outlines
+  /First 10 0 R
+  /Last  11 0 R
+  /Count 2
+>>
+endobj
+% Font resource.
+15 0 obj <<
+  /Type /Font
+  /Subtype /Type1
+  /BaseFont /Arial
+>>
+endobj
+% Content for page 0.
+21 0 obj <<
+  /Length 0
+>>
+stream
+BT
+/F1 20 Tf
+100 600 TD (Page1)Tj
+ET
+endstream
+endobj
+% Content for page 1.
+22 0 obj <<
+  /Length 0
+>>
+stream
+BT
+/F1 20 Tf
+100 600 TD (Page2)Tj
+ET
+endstream
+endobj
+xref
+0 23
+0000000000 65535 f 
+0000000015 00000 n 
+0000000087 00000 n 
+0000000185 00000 n 
+0000000346 00000 n 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000508 00000 n 
+0000000620 00000 n 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000729 00000 n 
+0000000829 00000 n 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000000 65535 f 
+0000000924 00000 n 
+0000001034 00000 n 
+trailer <<
+  /Size 6
+  /Root 1 0 R
+>>
+startxref
+1122
+%%EOF