[clang] [clang][ssaf] Convert `JSONFormat` tests for `TUSummary` and `TUSummaryEncoding` to lit tests (PR #192187)

Aviral Goel via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 15 15:54:45 PDT 2026


https://github.com/aviralg updated https://github.com/llvm/llvm-project/pull/192187

>From b4630bef3259f58f8e8881d89121570dfe99bdad Mon Sep 17 00:00:00 2001
From: Aviral Goel <agoel26 at apple.com>
Date: Tue, 14 Apr 2026 22:39:17 -0700
Subject: [PATCH] [clang][ssaf] Add `JSONFormat` lit tests for `TUSummary`

---
 .../Inputs/data-element-not-object.json       |    9 +
 .../Inputs/data-entry-missing-data.json       |   13 +
 .../data-entry-missing-summary-name.json      |   13 +
 .../TUSummary/Inputs/data-not-array.json      |    9 +
 .../duplicate-entity-id-in-data-map.json      |   43 +
 .../TUSummary/Inputs/duplicate-entity.json    |   45 +
 .../Inputs/duplicate-summary-name.json        |   18 +
 .../entity-data-element-not-object.json       |   14 +
 .../Inputs/entity-data-missing-entity-id.json |   18 +
 .../entity-data-missing-entity-summary.json   |   18 +
 .../Inputs/entity-id-not-uint64.json          |   19 +
 .../Inputs/entity-name-missing-namespace.json |   22 +
 .../Inputs/entity-name-missing-suffix.json    |   22 +
 .../Inputs/entity-name-missing-usr.json       |   22 +
 .../Inputs/id-table-element-not-object.json   |    9 +
 .../Inputs/id-table-entry-id-not-uint64.json  |   18 +
 .../Inputs/id-table-entry-missing-id.json     |   17 +
 .../Inputs/id-table-entry-missing-name.json   |   18 +
 .../TUSummary/Inputs/id-table-not-array.json  |    9 +
 .../TUSummary/Inputs/invalid-kind.json        |    9 +
 .../TUSummary/Inputs/invalid-syntax.json      |    1 +
 .../Inputs/linkage-table-duplicate-id.json    |   32 +
 .../linkage-table-element-not-object.json     |    9 +
 .../linkage-table-entry-id-not-uint64.json    |   14 +
 ...kage-table-entry-linkage-invalid-type.json |   14 +
 ...kage-table-entry-linkage-missing-type.json |   14 +
 .../linkage-table-entry-missing-id.json       |   13 +
 .../linkage-table-entry-missing-linkage.json  |   13 +
 .../Inputs/linkage-table-extra-id.json        |   14 +
 .../Inputs/linkage-table-missing-id.json      |   23 +
 .../Inputs/linkage-table-not-array.json       |    9 +
 .../TUSummary/Inputs/missing-data.json        |    8 +
 .../TUSummary/Inputs/missing-id-table.json    |    7 +
 .../TUSummary/Inputs/missing-kind.json        |    8 +
 .../Inputs/missing-linkage-table.json         |    8 +
 .../TUSummary/Inputs/missing-name.json        |    8 +
 .../Inputs/missing-tu-namespace.json          |    5 +
 .../namespace-element-invalid-kind.json       |   28 +
 .../namespace-element-missing-kind.json       |   27 +
 .../namespace-element-missing-name.json       |   27 +
 .../Inputs/namespace-element-not-object.json  |   23 +
 .../TUSummary/Inputs/not-json-extension.txt   |    1 +
 .../TUSummary/Inputs/not-object.json          |    1 +
 .../Inputs/pairs-element-not-object.json      |   21 +
 .../Inputs/pairs-invalid-first-field.json     |   26 +
 .../pairs-invalid-pairs-field-type.json       |   21 +
 .../Inputs/pairs-invalid-second-field.json    |   26 +
 .../Inputs/pairs-missing-first-field.json     |   25 +
 .../Inputs/pairs-missing-pairs-field.json     |   19 +
 .../Inputs/pairs-missing-second-field.json    |   25 +
 .../read-entity-summary-no-format-info.json   |   19 +
 .../TUSummary/Inputs/rt-empty-data-entry.json |   14 +
 .../TUSummary/Inputs/rt-empty.json            |    9 +
 .../TUSummary/Inputs/rt-link-unit.json        |    9 +
 .../TUSummary/Inputs/rt-linkage-external.json |   30 +
 .../TUSummary/Inputs/rt-linkage-internal.json |   30 +
 .../TUSummary/Inputs/rt-linkage-multiple.json |   68 +
 .../TUSummary/Inputs/rt-linkage-none.json     |   30 +
 .../Inputs/rt-two-summary-types.json          |  238 ++
 .../ssaf-format/TUSummary/permissions.test    |   40 +
 .../ssaf-format/TUSummary/serialization.test  |  612 +++++
 .../JSONFormatTest/TUSummaryTest.cpp          | 2058 +----------------
 62 files changed, 2010 insertions(+), 1982 deletions(-)
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/data-element-not-object.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/data-entry-missing-data.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/data-entry-missing-summary-name.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/data-not-array.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/duplicate-entity-id-in-data-map.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/duplicate-entity.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/duplicate-summary-name.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-data-element-not-object.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-data-missing-entity-id.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-data-missing-entity-summary.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-id-not-uint64.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-name-missing-namespace.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-name-missing-suffix.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-name-missing-usr.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/id-table-element-not-object.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/id-table-entry-id-not-uint64.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/id-table-entry-missing-id.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/id-table-entry-missing-name.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/id-table-not-array.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/invalid-kind.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/invalid-syntax.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-duplicate-id.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-element-not-object.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-entry-id-not-uint64.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-entry-linkage-invalid-type.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-entry-linkage-missing-type.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-entry-missing-id.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-entry-missing-linkage.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-extra-id.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-missing-id.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-not-array.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/missing-data.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/missing-id-table.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/missing-kind.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/missing-linkage-table.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/missing-name.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/missing-tu-namespace.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/namespace-element-invalid-kind.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/namespace-element-missing-kind.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/namespace-element-missing-name.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/namespace-element-not-object.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/not-json-extension.txt
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/not-object.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-element-not-object.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-invalid-first-field.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-invalid-pairs-field-type.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-invalid-second-field.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-missing-first-field.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-missing-pairs-field.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-missing-second-field.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/read-entity-summary-no-format-info.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-empty-data-entry.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-empty.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-link-unit.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-linkage-external.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-linkage-internal.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-linkage-multiple.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-linkage-none.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-two-summary-types.json
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/permissions.test
 create mode 100644 clang/test/Analysis/Scalable/ssaf-format/TUSummary/serialization.test

diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/data-element-not-object.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/data-element-not-object.json
new file mode 100644
index 0000000000000..c96c7156c5494
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/data-element-not-object.json
@@ -0,0 +1,9 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": [],
+  "data": ["invalid"]
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/data-entry-missing-data.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/data-entry-missing-data.json
new file mode 100644
index 0000000000000..3214233104187
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/data-entry-missing-data.json
@@ -0,0 +1,13 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": [],
+  "data": [
+    {
+      "summary_name": "PairsEntitySummary"
+    }
+  ]
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/data-entry-missing-summary-name.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/data-entry-missing-summary-name.json
new file mode 100644
index 0000000000000..2304f3f90d5ca
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/data-entry-missing-summary-name.json
@@ -0,0 +1,13 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": [],
+  "data": [
+    {
+      "summary_data": []
+    }
+  ]
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/data-not-array.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/data-not-array.json
new file mode 100644
index 0000000000000..4a8e3ac953ce6
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/data-not-array.json
@@ -0,0 +1,9 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": [],
+  "data": {}
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/duplicate-entity-id-in-data-map.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/duplicate-entity-id-in-data-map.json
new file mode 100644
index 0000000000000..8c42f0d344d38
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/duplicate-entity-id-in-data-map.json
@@ -0,0 +1,43 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [
+    {
+      "id": 0,
+      "name": {
+        "usr": "c:@F at foo",
+        "suffix": "",
+        "namespace": []
+      }
+    }
+  ],
+  "linkage_table": [
+    {
+      "id": 0,
+      "linkage": {
+        "type": "None"
+      }
+    }
+  ],
+  "data": [
+    {
+      "summary_name": "PairsEntitySummary",
+      "summary_data": [
+        {
+          "entity_id": 0,
+          "entity_summary": {
+            "pairs": []
+          }
+        },
+        {
+          "entity_id": 0,
+          "entity_summary": {
+            "pairs": []
+          }
+        }
+      ]
+    }
+  ]
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/duplicate-entity.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/duplicate-entity.json
new file mode 100644
index 0000000000000..de755207997a0
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/duplicate-entity.json
@@ -0,0 +1,45 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [
+    {
+      "id": 0,
+      "name": {
+        "usr": "c:@F at foo",
+        "suffix": "",
+        "namespace": [
+          {
+            "kind": "CompilationUnit",
+            "name": "test.cpp"
+          }
+        ]
+      }
+    },
+    {
+      "id": 1,
+      "name": {
+        "usr": "c:@F at foo",
+        "suffix": "",
+        "namespace": [
+          {
+            "kind": "CompilationUnit",
+            "name": "test.cpp"
+          }
+        ]
+      }
+    }
+  ],
+  "linkage_table": [
+    {
+      "id": 0,
+      "linkage": { "type": "Internal" }
+    },
+    {
+      "id": 1,
+      "linkage": { "type": "External" }
+    }
+  ],
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/duplicate-summary-name.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/duplicate-summary-name.json
new file mode 100644
index 0000000000000..ec190677f1650
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/duplicate-summary-name.json
@@ -0,0 +1,18 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": [],
+  "data": [
+    {
+      "summary_name": "PairsEntitySummary",
+      "summary_data": []
+    },
+    {
+      "summary_name": "PairsEntitySummary",
+      "summary_data": []
+    }
+  ]
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-data-element-not-object.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-data-element-not-object.json
new file mode 100644
index 0000000000000..7d203771a4baa
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-data-element-not-object.json
@@ -0,0 +1,14 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": [],
+  "data": [
+    {
+      "summary_name": "PairsEntitySummary",
+      "summary_data": ["invalid"]
+    }
+  ]
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-data-missing-entity-id.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-data-missing-entity-id.json
new file mode 100644
index 0000000000000..db708c4ee2fd6
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-data-missing-entity-id.json
@@ -0,0 +1,18 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": [],
+  "data": [
+    {
+      "summary_name": "PairsEntitySummary",
+      "summary_data": [
+        {
+          "entity_summary": {}
+        }
+      ]
+    }
+  ]
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-data-missing-entity-summary.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-data-missing-entity-summary.json
new file mode 100644
index 0000000000000..3108bce074bed
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-data-missing-entity-summary.json
@@ -0,0 +1,18 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": [],
+  "data": [
+    {
+      "summary_name": "PairsEntitySummary",
+      "summary_data": [
+        {
+          "entity_id": 0
+        }
+      ]
+    }
+  ]
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-id-not-uint64.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-id-not-uint64.json
new file mode 100644
index 0000000000000..2cac6f854e2a4
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-id-not-uint64.json
@@ -0,0 +1,19 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": [],
+  "data": [
+    {
+      "summary_name": "PairsEntitySummary",
+      "summary_data": [
+        {
+          "entity_id": "not_a_number",
+          "entity_summary": {}
+        }
+      ]
+    }
+  ]
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-name-missing-namespace.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-name-missing-namespace.json
new file mode 100644
index 0000000000000..ab429bf06d74a
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-name-missing-namespace.json
@@ -0,0 +1,22 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [
+    {
+      "id": 0,
+      "name": {
+        "usr": "c:@F at foo",
+        "suffix": ""
+      }
+    }
+  ],
+  "linkage_table": [
+    {
+      "id": 0,
+      "linkage": { "type": "None" }
+    }
+  ],
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-name-missing-suffix.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-name-missing-suffix.json
new file mode 100644
index 0000000000000..51b08a56653e0
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-name-missing-suffix.json
@@ -0,0 +1,22 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [
+    {
+      "id": 0,
+      "name": {
+        "usr": "c:@F at foo",
+        "namespace": []
+      }
+    }
+  ],
+  "linkage_table": [
+    {
+      "id": 0,
+      "linkage": { "type": "External" }
+    }
+  ],
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-name-missing-usr.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-name-missing-usr.json
new file mode 100644
index 0000000000000..798f6c1db0afd
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/entity-name-missing-usr.json
@@ -0,0 +1,22 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [
+    {
+      "id": 0,
+      "name": {
+        "suffix": "",
+        "namespace": []
+      }
+    }
+  ],
+  "linkage_table": [
+    {
+      "id": 0,
+      "linkage": { "type": "Internal" }
+    }
+  ],
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/id-table-element-not-object.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/id-table-element-not-object.json
new file mode 100644
index 0000000000000..56046b99cea11
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/id-table-element-not-object.json
@@ -0,0 +1,9 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [123],
+  "linkage_table": [],
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/id-table-entry-id-not-uint64.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/id-table-entry-id-not-uint64.json
new file mode 100644
index 0000000000000..d623ce420d716
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/id-table-entry-id-not-uint64.json
@@ -0,0 +1,18 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [
+    {
+      "id": "not_a_number",
+      "name": {
+        "usr": "c:@F at foo",
+        "suffix": "",
+        "namespace": []
+      }
+    }
+  ],
+  "linkage_table": [],
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/id-table-entry-missing-id.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/id-table-entry-missing-id.json
new file mode 100644
index 0000000000000..82d522b4b04e7
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/id-table-entry-missing-id.json
@@ -0,0 +1,17 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [
+    {
+      "name": {
+        "usr": "c:@F at foo",
+        "suffix": "",
+        "namespace": []
+      }
+    }
+  ],
+  "linkage_table": [],
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/id-table-entry-missing-name.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/id-table-entry-missing-name.json
new file mode 100644
index 0000000000000..6aa76b6c81c34
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/id-table-entry-missing-name.json
@@ -0,0 +1,18 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [
+    {
+      "id": 0
+    }
+  ],
+  "linkage_table": [
+    {
+      "id": 0,
+      "linkage": { "type": "None" }
+    }
+  ],
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/id-table-not-array.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/id-table-not-array.json
new file mode 100644
index 0000000000000..7bfef9104a8b1
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/id-table-not-array.json
@@ -0,0 +1,9 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": {},
+  "linkage_table": [],
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/invalid-kind.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/invalid-kind.json
new file mode 100644
index 0000000000000..3dd960cc76ca9
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/invalid-kind.json
@@ -0,0 +1,9 @@
+{
+  "tu_namespace": {
+    "kind": "invalid_kind",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": [],
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/invalid-syntax.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/invalid-syntax.json
new file mode 100644
index 0000000000000..b0e13f61aa06e
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/invalid-syntax.json
@@ -0,0 +1 @@
+{ invalid json }
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-duplicate-id.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-duplicate-id.json
new file mode 100644
index 0000000000000..54136fa99a0bb
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-duplicate-id.json
@@ -0,0 +1,32 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [
+    {
+      "id": 0,
+      "name": {
+        "usr": "c:@F at foo",
+        "suffix": "",
+        "namespace": [
+          {
+            "kind": "CompilationUnit",
+            "name": "test.cpp"
+          }
+        ]
+      }
+    }
+  ],
+  "linkage_table": [
+    {
+      "id": 0,
+      "linkage": { "type": "External" }
+    },
+    {
+      "id": 0,
+      "linkage": { "type": "Internal" }
+    }
+  ],
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-element-not-object.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-element-not-object.json
new file mode 100644
index 0000000000000..660a98049add5
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-element-not-object.json
@@ -0,0 +1,9 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": ["invalid"],
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-entry-id-not-uint64.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-entry-id-not-uint64.json
new file mode 100644
index 0000000000000..b3668bd39ee29
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-entry-id-not-uint64.json
@@ -0,0 +1,14 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": [
+    {
+      "id": "not_a_number",
+      "linkage": { "type": "External" }
+    }
+  ],
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-entry-linkage-invalid-type.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-entry-linkage-invalid-type.json
new file mode 100644
index 0000000000000..e1cb23b8f5d21
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-entry-linkage-invalid-type.json
@@ -0,0 +1,14 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": [
+    {
+      "id": 0,
+      "linkage": { "type": "invalid_type" }
+    }
+  ],
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-entry-linkage-missing-type.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-entry-linkage-missing-type.json
new file mode 100644
index 0000000000000..1a7f39e93af81
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-entry-linkage-missing-type.json
@@ -0,0 +1,14 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": [
+    {
+      "id": 0,
+      "linkage": {}
+    }
+  ],
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-entry-missing-id.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-entry-missing-id.json
new file mode 100644
index 0000000000000..7c3e96cb75cb3
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-entry-missing-id.json
@@ -0,0 +1,13 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": [
+    {
+      "linkage": { "type": "External" }
+    }
+  ],
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-entry-missing-linkage.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-entry-missing-linkage.json
new file mode 100644
index 0000000000000..b837fb327d8f4
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-entry-missing-linkage.json
@@ -0,0 +1,13 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": [
+    {
+      "id": 0
+    }
+  ],
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-extra-id.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-extra-id.json
new file mode 100644
index 0000000000000..ea22b8a23cfdb
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-extra-id.json
@@ -0,0 +1,14 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": [
+    {
+      "id": 0,
+      "linkage": { "type": "External" }
+    }
+  ],
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-missing-id.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-missing-id.json
new file mode 100644
index 0000000000000..0a44babcb9e2b
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-missing-id.json
@@ -0,0 +1,23 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [
+    {
+      "id": 0,
+      "name": {
+        "usr": "c:@F at foo",
+        "suffix": "",
+        "namespace": [
+          {
+            "kind": "CompilationUnit",
+            "name": "test.cpp"
+          }
+        ]
+      }
+    }
+  ],
+  "linkage_table": [],
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-not-array.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-not-array.json
new file mode 100644
index 0000000000000..11f8239d72d8a
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/linkage-table-not-array.json
@@ -0,0 +1,9 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": {},
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/missing-data.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/missing-data.json
new file mode 100644
index 0000000000000..a872795383a61
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/missing-data.json
@@ -0,0 +1,8 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/missing-id-table.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/missing-id-table.json
new file mode 100644
index 0000000000000..61ce449823c20
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/missing-id-table.json
@@ -0,0 +1,7 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/missing-kind.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/missing-kind.json
new file mode 100644
index 0000000000000..c78bc51c55863
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/missing-kind.json
@@ -0,0 +1,8 @@
+{
+  "tu_namespace": {
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": [],
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/missing-linkage-table.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/missing-linkage-table.json
new file mode 100644
index 0000000000000..400a330603a37
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/missing-linkage-table.json
@@ -0,0 +1,8 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/missing-name.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/missing-name.json
new file mode 100644
index 0000000000000..3e521780ffceb
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/missing-name.json
@@ -0,0 +1,8 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit"
+  },
+  "id_table": [],
+  "linkage_table": [],
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/missing-tu-namespace.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/missing-tu-namespace.json
new file mode 100644
index 0000000000000..5f9b7dd71944c
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/missing-tu-namespace.json
@@ -0,0 +1,5 @@
+{
+  "id_table": [],
+  "linkage_table": [],
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/namespace-element-invalid-kind.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/namespace-element-invalid-kind.json
new file mode 100644
index 0000000000000..bebdfdff67efc
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/namespace-element-invalid-kind.json
@@ -0,0 +1,28 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [
+    {
+      "id": 0,
+      "name": {
+        "usr": "c:@F at foo",
+        "suffix": "",
+        "namespace": [
+          {
+            "kind": "invalid_kind",
+            "name": "test.cpp"
+          }
+        ]
+      }
+    }
+  ],
+  "linkage_table": [
+    {
+      "id": 0,
+      "linkage": { "type": "External" }
+    }
+  ],
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/namespace-element-missing-kind.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/namespace-element-missing-kind.json
new file mode 100644
index 0000000000000..cb354c7772df6
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/namespace-element-missing-kind.json
@@ -0,0 +1,27 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [
+    {
+      "id": 0,
+      "name": {
+        "usr": "c:@F at foo",
+        "suffix": "",
+        "namespace": [
+          {
+            "name": "test.cpp"
+          }
+        ]
+      }
+    }
+  ],
+  "linkage_table": [
+    {
+      "id": 0,
+      "linkage": { "type": "Internal" }
+    }
+  ],
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/namespace-element-missing-name.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/namespace-element-missing-name.json
new file mode 100644
index 0000000000000..16a78edf99279
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/namespace-element-missing-name.json
@@ -0,0 +1,27 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [
+    {
+      "id": 0,
+      "name": {
+        "usr": "c:@F at foo",
+        "suffix": "",
+        "namespace": [
+          {
+            "kind": "CompilationUnit"
+          }
+        ]
+      }
+    }
+  ],
+  "linkage_table": [
+    {
+      "id": 0,
+      "linkage": { "type": "None" }
+    }
+  ],
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/namespace-element-not-object.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/namespace-element-not-object.json
new file mode 100644
index 0000000000000..c898d6ec14a7f
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/namespace-element-not-object.json
@@ -0,0 +1,23 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [
+    {
+      "id": 0,
+      "name": {
+        "usr": "c:@F at foo",
+        "suffix": "",
+        "namespace": ["invalid"]
+      }
+    }
+  ],
+  "linkage_table": [
+    {
+      "id": 0,
+      "linkage": { "type": "None" }
+    }
+  ],
+  "data": []
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/not-json-extension.txt b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/not-json-extension.txt
new file mode 100644
index 0000000000000..0967ef424bce6
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/not-json-extension.txt
@@ -0,0 +1 @@
+{}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/not-object.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/not-object.json
new file mode 100644
index 0000000000000..fe51488c7066f
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/not-object.json
@@ -0,0 +1 @@
+[]
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-element-not-object.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-element-not-object.json
new file mode 100644
index 0000000000000..e5af7ec227db5
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-element-not-object.json
@@ -0,0 +1,21 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": [],
+  "data": [
+    {
+      "summary_name": "PairsEntitySummary",
+      "summary_data": [
+        {
+          "entity_id": 0,
+          "entity_summary": {
+            "pairs": ["not_an_object"]
+          }
+        }
+      ]
+    }
+  ]
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-invalid-first-field.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-invalid-first-field.json
new file mode 100644
index 0000000000000..dfe0ded6832b7
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-invalid-first-field.json
@@ -0,0 +1,26 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": [],
+  "data": [
+    {
+      "summary_name": "PairsEntitySummary",
+      "summary_data": [
+        {
+          "entity_id": 0,
+          "entity_summary": {
+            "pairs": [
+              {
+                "first": "not_a_number",
+                "second": {"@": 1}
+              }
+            ]
+          }
+        }
+      ]
+    }
+  ]
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-invalid-pairs-field-type.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-invalid-pairs-field-type.json
new file mode 100644
index 0000000000000..457a9ebe0a4a8
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-invalid-pairs-field-type.json
@@ -0,0 +1,21 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": [],
+  "data": [
+    {
+      "summary_name": "PairsEntitySummary",
+      "summary_data": [
+        {
+          "entity_id": 0,
+          "entity_summary": {
+            "pairs": "not_an_array"
+          }
+        }
+      ]
+    }
+  ]
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-invalid-second-field.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-invalid-second-field.json
new file mode 100644
index 0000000000000..546ec248e349d
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-invalid-second-field.json
@@ -0,0 +1,26 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": [],
+  "data": [
+    {
+      "summary_name": "PairsEntitySummary",
+      "summary_data": [
+        {
+          "entity_id": 0,
+          "entity_summary": {
+            "pairs": [
+              {
+                "first": {"@": 0},
+                "second": "not_a_number"
+              }
+            ]
+          }
+        }
+      ]
+    }
+  ]
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-missing-first-field.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-missing-first-field.json
new file mode 100644
index 0000000000000..096385b144088
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-missing-first-field.json
@@ -0,0 +1,25 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": [],
+  "data": [
+    {
+      "summary_name": "PairsEntitySummary",
+      "summary_data": [
+        {
+          "entity_id": 0,
+          "entity_summary": {
+            "pairs": [
+              {
+                "second": {"@": 1}
+              }
+            ]
+          }
+        }
+      ]
+    }
+  ]
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-missing-pairs-field.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-missing-pairs-field.json
new file mode 100644
index 0000000000000..3ff28b1458135
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-missing-pairs-field.json
@@ -0,0 +1,19 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": [],
+  "data": [
+    {
+      "summary_name": "PairsEntitySummary",
+      "summary_data": [
+        {
+          "entity_id": 0,
+          "entity_summary": {}
+        }
+      ]
+    }
+  ]
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-missing-second-field.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-missing-second-field.json
new file mode 100644
index 0000000000000..ffc544ab0e05d
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/pairs-missing-second-field.json
@@ -0,0 +1,25 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": [],
+  "data": [
+    {
+      "summary_name": "PairsEntitySummary",
+      "summary_data": [
+        {
+          "entity_id": 0,
+          "entity_summary": {
+            "pairs": [
+              {
+                "first": {"@": 0}
+              }
+            ]
+          }
+        }
+      ]
+    }
+  ]
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/read-entity-summary-no-format-info.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/read-entity-summary-no-format-info.json
new file mode 100644
index 0000000000000..ec4f06978088f
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/read-entity-summary-no-format-info.json
@@ -0,0 +1,19 @@
+{
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  },
+  "id_table": [],
+  "linkage_table": [],
+  "data": [
+    {
+      "summary_name": "UnregisteredEntitySummary",
+      "summary_data": [
+        {
+          "entity_id": 0,
+          "entity_summary": {}
+        }
+      ]
+    }
+  ]
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-empty-data-entry.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-empty-data-entry.json
new file mode 100644
index 0000000000000..ecc9c224c4e41
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-empty-data-entry.json
@@ -0,0 +1,14 @@
+{
+  "data": [
+    {
+      "summary_data": [],
+      "summary_name": "PairsEntitySummary"
+    }
+  ],
+  "id_table": [],
+  "linkage_table": [],
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  }
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-empty.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-empty.json
new file mode 100644
index 0000000000000..9c9fd1e54cd34
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-empty.json
@@ -0,0 +1,9 @@
+{
+  "data": [],
+  "id_table": [],
+  "linkage_table": [],
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  }
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-link-unit.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-link-unit.json
new file mode 100644
index 0000000000000..9c2ebf5e18b9c
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-link-unit.json
@@ -0,0 +1,9 @@
+{
+  "data": [],
+  "id_table": [],
+  "linkage_table": [],
+  "tu_namespace": {
+    "kind": "LinkUnit",
+    "name": "libtest.so"
+  }
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-linkage-external.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-linkage-external.json
new file mode 100644
index 0000000000000..253ec97903e36
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-linkage-external.json
@@ -0,0 +1,30 @@
+{
+  "data": [],
+  "id_table": [
+    {
+      "id": 0,
+      "name": {
+        "namespace": [
+          {
+            "kind": "CompilationUnit",
+            "name": "test.cpp"
+          }
+        ],
+        "suffix": "",
+        "usr": "c:@F at baz"
+      }
+    }
+  ],
+  "linkage_table": [
+    {
+      "id": 0,
+      "linkage": {
+        "type": "External"
+      }
+    }
+  ],
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  }
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-linkage-internal.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-linkage-internal.json
new file mode 100644
index 0000000000000..bea95a580b215
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-linkage-internal.json
@@ -0,0 +1,30 @@
+{
+  "data": [],
+  "id_table": [
+    {
+      "id": 0,
+      "name": {
+        "namespace": [
+          {
+            "kind": "CompilationUnit",
+            "name": "test.cpp"
+          }
+        ],
+        "suffix": "",
+        "usr": "c:@F at bar"
+      }
+    }
+  ],
+  "linkage_table": [
+    {
+      "id": 0,
+      "linkage": {
+        "type": "Internal"
+      }
+    }
+  ],
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  }
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-linkage-multiple.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-linkage-multiple.json
new file mode 100644
index 0000000000000..e97a8991207c9
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-linkage-multiple.json
@@ -0,0 +1,68 @@
+{
+  "data": [],
+  "id_table": [
+    {
+      "id": 1,
+      "name": {
+        "namespace": [
+          {
+            "kind": "CompilationUnit",
+            "name": "test.cpp"
+          }
+        ],
+        "suffix": "",
+        "usr": "c:@F at bar"
+      }
+    },
+    {
+      "id": 2,
+      "name": {
+        "namespace": [
+          {
+            "kind": "CompilationUnit",
+            "name": "test.cpp"
+          }
+        ],
+        "suffix": "",
+        "usr": "c:@F at baz"
+      }
+    },
+    {
+      "id": 0,
+      "name": {
+        "namespace": [
+          {
+            "kind": "CompilationUnit",
+            "name": "test.cpp"
+          }
+        ],
+        "suffix": "",
+        "usr": "c:@F at foo"
+      }
+    }
+  ],
+  "linkage_table": [
+    {
+      "id": 0,
+      "linkage": {
+        "type": "None"
+      }
+    },
+    {
+      "id": 1,
+      "linkage": {
+        "type": "Internal"
+      }
+    },
+    {
+      "id": 2,
+      "linkage": {
+        "type": "External"
+      }
+    }
+  ],
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  }
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-linkage-none.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-linkage-none.json
new file mode 100644
index 0000000000000..3f6c14327ee74
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-linkage-none.json
@@ -0,0 +1,30 @@
+{
+  "data": [],
+  "id_table": [
+    {
+      "id": 0,
+      "name": {
+        "namespace": [
+          {
+            "kind": "CompilationUnit",
+            "name": "test.cpp"
+          }
+        ],
+        "suffix": "",
+        "usr": "c:@F at foo"
+      }
+    }
+  ],
+  "linkage_table": [
+    {
+      "id": 0,
+      "linkage": {
+        "type": "None"
+      }
+    }
+  ],
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  }
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-two-summary-types.json b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-two-summary-types.json
new file mode 100644
index 0000000000000..fb2e633a2e7e3
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/Inputs/rt-two-summary-types.json
@@ -0,0 +1,238 @@
+{
+  "data": [
+    {
+      "summary_data": [
+        {
+          "entity_id": 0,
+          "entity_summary": {
+            "pairs": []
+          }
+        },
+        {
+          "entity_id": 1,
+          "entity_summary": {
+            "pairs": [
+              {
+                "first": {
+                  "@": 1
+                },
+                "second": {
+                  "@": 3
+                }
+              }
+            ]
+          }
+        },
+        {
+          "entity_id": 2,
+          "entity_summary": {
+            "pairs": [
+              {
+                "first": {
+                  "@": 2
+                },
+                "second": {
+                  "@": 4
+                }
+              },
+              {
+                "first": {
+                  "@": 2
+                },
+                "second": {
+                  "@": 3
+                }
+              }
+            ]
+          }
+        },
+        {
+          "entity_id": 3,
+          "entity_summary": {
+            "pairs": [
+              {
+                "first": {
+                  "@": 3
+                },
+                "second": {
+                  "@": 1
+                }
+              }
+            ]
+          }
+        },
+        {
+          "entity_id": 4,
+          "entity_summary": {
+            "pairs": [
+              {
+                "first": {
+                  "@": 4
+                },
+                "second": {
+                  "@": 0
+                }
+              },
+              {
+                "first": {
+                  "@": 4
+                },
+                "second": {
+                  "@": 2
+                }
+              }
+            ]
+          }
+        }
+      ],
+      "summary_name": "PairsEntitySummary"
+    },
+    {
+      "summary_data": [
+        {
+          "entity_id": 0,
+          "entity_summary": {
+            "tags": [
+              "entry-point"
+            ]
+          }
+        },
+        {
+          "entity_id": 1,
+          "entity_summary": {
+            "tags": [
+              "internal-only"
+            ]
+          }
+        },
+        {
+          "entity_id": 2,
+          "entity_summary": {
+            "tags": []
+          }
+        },
+        {
+          "entity_id": 3,
+          "entity_summary": {
+            "tags": [
+              "internal-only"
+            ]
+          }
+        },
+        {
+          "entity_id": 4,
+          "entity_summary": {
+            "tags": [
+              "exported",
+              "hot"
+            ]
+          }
+        }
+      ],
+      "summary_name": "TagsEntitySummary"
+    }
+  ],
+  "id_table": [
+    {
+      "id": 1,
+      "name": {
+        "namespace": [
+          {
+            "kind": "CompilationUnit",
+            "name": "test.cpp"
+          }
+        ],
+        "suffix": "",
+        "usr": "c:@F at bar"
+      }
+    },
+    {
+      "id": 2,
+      "name": {
+        "namespace": [
+          {
+            "kind": "CompilationUnit",
+            "name": "test.cpp"
+          }
+        ],
+        "suffix": "",
+        "usr": "c:@F at baz"
+      }
+    },
+    {
+      "id": 0,
+      "name": {
+        "namespace": [
+          {
+            "kind": "CompilationUnit",
+            "name": "test.cpp"
+          }
+        ],
+        "suffix": "",
+        "usr": "c:@F at foo"
+      }
+    },
+    {
+      "id": 4,
+      "name": {
+        "namespace": [
+          {
+            "kind": "CompilationUnit",
+            "name": "test.cpp"
+          }
+        ],
+        "suffix": "",
+        "usr": "c:@F at quux"
+      }
+    },
+    {
+      "id": 3,
+      "name": {
+        "namespace": [
+          {
+            "kind": "CompilationUnit",
+            "name": "test.cpp"
+          }
+        ],
+        "suffix": "",
+        "usr": "c:@F at qux"
+      }
+    }
+  ],
+  "linkage_table": [
+    {
+      "id": 0,
+      "linkage": {
+        "type": "None"
+      }
+    },
+    {
+      "id": 1,
+      "linkage": {
+        "type": "None"
+      }
+    },
+    {
+      "id": 2,
+      "linkage": {
+        "type": "Internal"
+      }
+    },
+    {
+      "id": 3,
+      "linkage": {
+        "type": "Internal"
+      }
+    },
+    {
+      "id": 4,
+      "linkage": {
+        "type": "External"
+      }
+    }
+  ],
+  "tu_namespace": {
+    "kind": "CompilationUnit",
+    "name": "test.cpp"
+  }
+}
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/permissions.test b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/permissions.test
new file mode 100644
index 0000000000000..86e6ccb82e7b3
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/permissions.test
@@ -0,0 +1,40 @@
+// Tests for clang-ssaf-format --type tu that require filesystem permission
+// support (symlinks, chmod).
+
+// UNSUPPORTED: system-windows
+// REQUIRES: non-root-user
+
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+
+// ============================================================================
+// Broken symlink
+// ============================================================================
+
+// RUN: ln -sf %t/nonexistent-target.json %t/broken-symlink.json
+// RUN: not clang-ssaf-format --type tu %t/broken-symlink.json 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=BROKEN-SYMLINK
+// BROKEN-SYMLINK: clang-ssaf-format: error: failed to validate path '{{.*}}broken-symlink.json': Path does not exist
+
+// ============================================================================
+// No read permission
+// ============================================================================
+
+// RUN: cp %S/Inputs/rt-empty.json %t/no-read-permission.json
+// RUN: chmod -r %t/no-read-permission.json
+// RUN: not clang-ssaf-format --type tu %t/no-read-permission.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=NO-READ-PERMISSION
+// RUN: chmod +r %t/no-read-permission.json
+// NO-READ-PERMISSION:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}no-read-permission.json'
+// NO-READ-PERMISSION-NEXT: failed to read file '{{.*}}no-read-permission.json': {{.*}}
+
+// ============================================================================
+// Write to directory without write permission
+// ============================================================================
+
+// RUN: mkdir -p %t/write-protected-dir
+// RUN: chmod -w %t/write-protected-dir
+// RUN: not clang-ssaf-format --type tu %S/Inputs/rt-empty.json -o %t/write-protected-dir/test.json 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=WRITE-STREAM-OPEN-FAILURE
+// RUN: chmod +w %t/write-protected-dir
+// WRITE-STREAM-OPEN-FAILURE: clang-ssaf-format: error: failed to validate path '{{.*}}write-protected-dir{{.}}test.json': Parent directory is not writable
diff --git a/clang/test/Analysis/Scalable/ssaf-format/TUSummary/serialization.test b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/serialization.test
new file mode 100644
index 0000000000000..88443b86b17df
--- /dev/null
+++ b/clang/test/Analysis/Scalable/ssaf-format/TUSummary/serialization.test
@@ -0,0 +1,612 @@
+// Tests for TUSummary and TUSummaryEncoding JSON serialization.
+
+// REQUIRES: plugins
+
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+
+// ============================================================================
+// readJSON() errors
+// ============================================================================
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/nonexistent.json 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=NONEXISTENT-FILE
+// NONEXISTENT-FILE: clang-ssaf-format: error: failed to validate path '{{.*}}nonexistent.json': Path does not exist
+
+// RUN: mkdir -p %t/test-directory.json
+// RUN: not clang-ssaf-format --type tu %t/test-directory.json 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=PATH-IS-DIRECTORY
+// PATH-IS-DIRECTORY: clang-ssaf-format: error: failed to validate path '{{.*}}test-directory.json': Path is not a file
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/not-json-extension.txt 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=NOT-JSON-EXTENSION
+// NOT-JSON-EXTENSION: clang-ssaf-format: error: failed to validate path '{{.*}}not-json-extension.txt': No format registered for extension 'txt'
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/invalid-syntax.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=INVALID-SYNTAX
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/invalid-syntax.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=INVALID-SYNTAX
+// INVALID-SYNTAX:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}invalid-syntax.json'
+// INVALID-SYNTAX-NEXT: {{.*}}Expected object key
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/not-object.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=NOT-OBJECT
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/not-object.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=NOT-OBJECT
+// NOT-OBJECT:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}not-object.json'
+// NOT-OBJECT-NEXT: failed to read TUSummary: expected JSON object
+
+// ============================================================================
+// entityLinkageFromJSON() errors
+// ============================================================================
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/linkage-table-entry-linkage-missing-type.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=LINKAGE-TABLE-ENTRY-LINKAGE-MISSING-TYPE
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/linkage-table-entry-linkage-missing-type.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=LINKAGE-TABLE-ENTRY-LINKAGE-MISSING-TYPE
+// LINKAGE-TABLE-ENTRY-LINKAGE-MISSING-TYPE:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}linkage-table-entry-linkage-missing-type.json'
+// LINKAGE-TABLE-ENTRY-LINKAGE-MISSING-TYPE-NEXT: reading LinkageTable from field 'linkage_table'
+// LINKAGE-TABLE-ENTRY-LINKAGE-MISSING-TYPE-NEXT: reading LinkageTable entry from index '0'
+// LINKAGE-TABLE-ENTRY-LINKAGE-MISSING-TYPE-NEXT: reading EntityLinkage from field 'linkage'
+// LINKAGE-TABLE-ENTRY-LINKAGE-MISSING-TYPE-NEXT: failed to read EntityLinkageType from field 'type': expected JSON string
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/linkage-table-entry-linkage-invalid-type.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=LINKAGE-TABLE-ENTRY-LINKAGE-INVALID-TYPE
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/linkage-table-entry-linkage-invalid-type.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=LINKAGE-TABLE-ENTRY-LINKAGE-INVALID-TYPE
+// LINKAGE-TABLE-ENTRY-LINKAGE-INVALID-TYPE:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}linkage-table-entry-linkage-invalid-type.json'
+// LINKAGE-TABLE-ENTRY-LINKAGE-INVALID-TYPE-NEXT: reading LinkageTable from field 'linkage_table'
+// LINKAGE-TABLE-ENTRY-LINKAGE-INVALID-TYPE-NEXT: reading LinkageTable entry from index '0'
+// LINKAGE-TABLE-ENTRY-LINKAGE-INVALID-TYPE-NEXT: reading EntityLinkage from field 'linkage'
+// LINKAGE-TABLE-ENTRY-LINKAGE-INVALID-TYPE-NEXT: reading EntityLinkageType from field 'type'
+// LINKAGE-TABLE-ENTRY-LINKAGE-INVALID-TYPE-NEXT: invalid EntityLinkageType value 'invalid_type' for field 'type'
+
+// ============================================================================
+// linkageTableEntryFromJSON() errors
+// ============================================================================
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/linkage-table-entry-missing-id.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=LINKAGE-TABLE-ENTRY-MISSING-ID
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/linkage-table-entry-missing-id.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=LINKAGE-TABLE-ENTRY-MISSING-ID
+// LINKAGE-TABLE-ENTRY-MISSING-ID:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}linkage-table-entry-missing-id.json'
+// LINKAGE-TABLE-ENTRY-MISSING-ID-NEXT: reading LinkageTable from field 'linkage_table'
+// LINKAGE-TABLE-ENTRY-MISSING-ID-NEXT: reading LinkageTable entry from index '0'
+// LINKAGE-TABLE-ENTRY-MISSING-ID-NEXT: failed to read EntityId from field 'id': expected JSON number (unsigned 64-bit integer)
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/linkage-table-entry-id-not-uint64.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=LINKAGE-TABLE-ENTRY-ID-NOT-UINT64
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/linkage-table-entry-id-not-uint64.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=LINKAGE-TABLE-ENTRY-ID-NOT-UINT64
+// LINKAGE-TABLE-ENTRY-ID-NOT-UINT64:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}linkage-table-entry-id-not-uint64.json'
+// LINKAGE-TABLE-ENTRY-ID-NOT-UINT64-NEXT: reading LinkageTable from field 'linkage_table'
+// LINKAGE-TABLE-ENTRY-ID-NOT-UINT64-NEXT: reading LinkageTable entry from index '0'
+// LINKAGE-TABLE-ENTRY-ID-NOT-UINT64-NEXT: failed to read EntityId from field 'id': expected JSON number (unsigned 64-bit integer)
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/linkage-table-entry-missing-linkage.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=LINKAGE-TABLE-ENTRY-MISSING-LINKAGE
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/linkage-table-entry-missing-linkage.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=LINKAGE-TABLE-ENTRY-MISSING-LINKAGE
+// LINKAGE-TABLE-ENTRY-MISSING-LINKAGE:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}linkage-table-entry-missing-linkage.json'
+// LINKAGE-TABLE-ENTRY-MISSING-LINKAGE-NEXT: reading LinkageTable from field 'linkage_table'
+// LINKAGE-TABLE-ENTRY-MISSING-LINKAGE-NEXT: reading LinkageTable entry from index '0'
+// LINKAGE-TABLE-ENTRY-MISSING-LINKAGE-NEXT: failed to read EntityLinkage from field 'linkage': expected JSON object
+
+// ============================================================================
+// linkageTableFromJSON() errors
+// ============================================================================
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/linkage-table-not-array.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=LINKAGE-TABLE-NOT-ARRAY
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/linkage-table-not-array.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=LINKAGE-TABLE-NOT-ARRAY
+// LINKAGE-TABLE-NOT-ARRAY:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}linkage-table-not-array.json'
+// LINKAGE-TABLE-NOT-ARRAY-NEXT: failed to read LinkageTable from field 'linkage_table': expected JSON array
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/linkage-table-element-not-object.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=LINKAGE-TABLE-ELEMENT-NOT-OBJECT
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/linkage-table-element-not-object.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=LINKAGE-TABLE-ELEMENT-NOT-OBJECT
+// LINKAGE-TABLE-ELEMENT-NOT-OBJECT:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}linkage-table-element-not-object.json'
+// LINKAGE-TABLE-ELEMENT-NOT-OBJECT-NEXT: reading LinkageTable from field 'linkage_table'
+// LINKAGE-TABLE-ELEMENT-NOT-OBJECT-NEXT: failed to read LinkageTable entry from index '0': expected JSON object
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/linkage-table-extra-id.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=LINKAGE-TABLE-EXTRA-ID
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/linkage-table-extra-id.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=LINKAGE-TABLE-EXTRA-ID
+// LINKAGE-TABLE-EXTRA-ID:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}linkage-table-extra-id.json'
+// LINKAGE-TABLE-EXTRA-ID-NEXT: reading LinkageTable from field 'linkage_table'
+// LINKAGE-TABLE-EXTRA-ID-NEXT: reading LinkageTable entry from index '0'
+// LINKAGE-TABLE-EXTRA-ID-NEXT: failed to deserialize LinkageTable: extra 'EntityId(0)' not present in IdTable
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/linkage-table-missing-id.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=LINKAGE-TABLE-MISSING-ID
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/linkage-table-missing-id.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=LINKAGE-TABLE-MISSING-ID
+// LINKAGE-TABLE-MISSING-ID:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}linkage-table-missing-id.json'
+// LINKAGE-TABLE-MISSING-ID-NEXT: reading LinkageTable from field 'linkage_table'
+// LINKAGE-TABLE-MISSING-ID-NEXT: failed to deserialize LinkageTable: missing 'EntityId(0)' present in IdTable
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/linkage-table-duplicate-id.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=LINKAGE-TABLE-DUPLICATE-ID
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/linkage-table-duplicate-id.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=LINKAGE-TABLE-DUPLICATE-ID
+// LINKAGE-TABLE-DUPLICATE-ID:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}linkage-table-duplicate-id.json'
+// LINKAGE-TABLE-DUPLICATE-ID-NEXT: reading LinkageTable from field 'linkage_table'
+// LINKAGE-TABLE-DUPLICATE-ID-NEXT: failed to insert LinkageTable entry at index '1': encountered duplicate 'EntityId(0)'
+
+// ============================================================================
+// buildNamespaceKindFromJSON() errors
+// ============================================================================
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/invalid-kind.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=INVALID-KIND
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/invalid-kind.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=INVALID-KIND
+// INVALID-KIND:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}invalid-kind.json'
+// INVALID-KIND-NEXT: reading BuildNamespace from field 'tu_namespace'
+// INVALID-KIND-NEXT: reading BuildNamespaceKind from field 'kind'
+// INVALID-KIND-NEXT: invalid BuildNamespaceKind value 'invalid_kind' for field 'kind'
+
+// ============================================================================
+// buildNamespaceFromJSON() errors
+// ============================================================================
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/missing-kind.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=MISSING-KIND
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/missing-kind.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=MISSING-KIND
+// MISSING-KIND:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}missing-kind.json'
+// MISSING-KIND-NEXT: reading BuildNamespace from field 'tu_namespace'
+// MISSING-KIND-NEXT: failed to read BuildNamespaceKind from field 'kind': expected JSON string
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/missing-name.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=MISSING-NAME
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/missing-name.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=MISSING-NAME
+// MISSING-NAME:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}missing-name.json'
+// MISSING-NAME-NEXT: reading BuildNamespace from field 'tu_namespace'
+// MISSING-NAME-NEXT: failed to read BuildNamespaceName from field 'name': expected JSON string
+
+// ============================================================================
+// nestedBuildNamespaceFromJSON() errors
+// ============================================================================
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/namespace-element-not-object.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=NAMESPACE-ELEMENT-NOT-OBJECT
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/namespace-element-not-object.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=NAMESPACE-ELEMENT-NOT-OBJECT
+// NAMESPACE-ELEMENT-NOT-OBJECT:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}namespace-element-not-object.json'
+// NAMESPACE-ELEMENT-NOT-OBJECT-NEXT: reading IdTable from field 'id_table'
+// NAMESPACE-ELEMENT-NOT-OBJECT-NEXT: reading EntityIdTable entry from index '0'
+// NAMESPACE-ELEMENT-NOT-OBJECT-NEXT: reading EntityName from field 'name'
+// NAMESPACE-ELEMENT-NOT-OBJECT-NEXT: reading NestedBuildNamespace from field 'namespace'
+// NAMESPACE-ELEMENT-NOT-OBJECT-NEXT: failed to read BuildNamespace from index '0': expected JSON object
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/namespace-element-missing-kind.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=NAMESPACE-ELEMENT-MISSING-KIND
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/namespace-element-missing-kind.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=NAMESPACE-ELEMENT-MISSING-KIND
+// NAMESPACE-ELEMENT-MISSING-KIND:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}namespace-element-missing-kind.json'
+// NAMESPACE-ELEMENT-MISSING-KIND-NEXT: reading IdTable from field 'id_table'
+// NAMESPACE-ELEMENT-MISSING-KIND-NEXT: reading EntityIdTable entry from index '0'
+// NAMESPACE-ELEMENT-MISSING-KIND-NEXT: reading EntityName from field 'name'
+// NAMESPACE-ELEMENT-MISSING-KIND-NEXT: reading NestedBuildNamespace from field 'namespace'
+// NAMESPACE-ELEMENT-MISSING-KIND-NEXT: reading BuildNamespace from index '0'
+// NAMESPACE-ELEMENT-MISSING-KIND-NEXT: failed to read BuildNamespaceKind from field 'kind': expected JSON string
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/namespace-element-invalid-kind.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=NAMESPACE-ELEMENT-INVALID-KIND
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/namespace-element-invalid-kind.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=NAMESPACE-ELEMENT-INVALID-KIND
+// NAMESPACE-ELEMENT-INVALID-KIND:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}namespace-element-invalid-kind.json'
+// NAMESPACE-ELEMENT-INVALID-KIND-NEXT: reading IdTable from field 'id_table'
+// NAMESPACE-ELEMENT-INVALID-KIND-NEXT: reading EntityIdTable entry from index '0'
+// NAMESPACE-ELEMENT-INVALID-KIND-NEXT: reading EntityName from field 'name'
+// NAMESPACE-ELEMENT-INVALID-KIND-NEXT: reading NestedBuildNamespace from field 'namespace'
+// NAMESPACE-ELEMENT-INVALID-KIND-NEXT: reading BuildNamespace from index '0'
+// NAMESPACE-ELEMENT-INVALID-KIND-NEXT: reading BuildNamespaceKind from field 'kind'
+// NAMESPACE-ELEMENT-INVALID-KIND-NEXT: invalid BuildNamespaceKind value 'invalid_kind' for field 'kind'
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/namespace-element-missing-name.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=NAMESPACE-ELEMENT-MISSING-NAME
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/namespace-element-missing-name.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=NAMESPACE-ELEMENT-MISSING-NAME
+// NAMESPACE-ELEMENT-MISSING-NAME:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}namespace-element-missing-name.json'
+// NAMESPACE-ELEMENT-MISSING-NAME-NEXT: reading IdTable from field 'id_table'
+// NAMESPACE-ELEMENT-MISSING-NAME-NEXT: reading EntityIdTable entry from index '0'
+// NAMESPACE-ELEMENT-MISSING-NAME-NEXT: reading EntityName from field 'name'
+// NAMESPACE-ELEMENT-MISSING-NAME-NEXT: reading NestedBuildNamespace from field 'namespace'
+// NAMESPACE-ELEMENT-MISSING-NAME-NEXT: reading BuildNamespace from index '0'
+// NAMESPACE-ELEMENT-MISSING-NAME-NEXT: failed to read BuildNamespaceName from field 'name': expected JSON string
+
+// ============================================================================
+// entityNameFromJSON() errors
+// ============================================================================
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/entity-name-missing-usr.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=ENTITY-NAME-MISSING-USR
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/entity-name-missing-usr.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=ENTITY-NAME-MISSING-USR
+// ENTITY-NAME-MISSING-USR:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}entity-name-missing-usr.json'
+// ENTITY-NAME-MISSING-USR-NEXT: reading IdTable from field 'id_table'
+// ENTITY-NAME-MISSING-USR-NEXT: reading EntityIdTable entry from index '0'
+// ENTITY-NAME-MISSING-USR-NEXT: reading EntityName from field 'name'
+// ENTITY-NAME-MISSING-USR-NEXT: failed to read USR from field 'usr': expected JSON string
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/entity-name-missing-suffix.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=ENTITY-NAME-MISSING-SUFFIX
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/entity-name-missing-suffix.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=ENTITY-NAME-MISSING-SUFFIX
+// ENTITY-NAME-MISSING-SUFFIX:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}entity-name-missing-suffix.json'
+// ENTITY-NAME-MISSING-SUFFIX-NEXT: reading IdTable from field 'id_table'
+// ENTITY-NAME-MISSING-SUFFIX-NEXT: reading EntityIdTable entry from index '0'
+// ENTITY-NAME-MISSING-SUFFIX-NEXT: reading EntityName from field 'name'
+// ENTITY-NAME-MISSING-SUFFIX-NEXT: failed to read Suffix from field 'suffix': expected JSON string
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/entity-name-missing-namespace.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=ENTITY-NAME-MISSING-NAMESPACE
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/entity-name-missing-namespace.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=ENTITY-NAME-MISSING-NAMESPACE
+// ENTITY-NAME-MISSING-NAMESPACE:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}entity-name-missing-namespace.json'
+// ENTITY-NAME-MISSING-NAMESPACE-NEXT: reading IdTable from field 'id_table'
+// ENTITY-NAME-MISSING-NAMESPACE-NEXT: reading EntityIdTable entry from index '0'
+// ENTITY-NAME-MISSING-NAMESPACE-NEXT: reading EntityName from field 'name'
+// ENTITY-NAME-MISSING-NAMESPACE-NEXT: failed to read NestedBuildNamespace from field 'namespace': expected JSON array
+
+// ============================================================================
+// entityIdTableEntryFromJSON() errors
+// ============================================================================
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/id-table-entry-missing-id.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=ID-TABLE-ENTRY-MISSING-ID
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/id-table-entry-missing-id.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=ID-TABLE-ENTRY-MISSING-ID
+// ID-TABLE-ENTRY-MISSING-ID:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}id-table-entry-missing-id.json'
+// ID-TABLE-ENTRY-MISSING-ID-NEXT: reading IdTable from field 'id_table'
+// ID-TABLE-ENTRY-MISSING-ID-NEXT: reading EntityIdTable entry from index '0'
+// ID-TABLE-ENTRY-MISSING-ID-NEXT: failed to read EntityId from field 'id': expected JSON number (unsigned 64-bit integer)
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/id-table-entry-missing-name.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=ID-TABLE-ENTRY-MISSING-NAME
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/id-table-entry-missing-name.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=ID-TABLE-ENTRY-MISSING-NAME
+// ID-TABLE-ENTRY-MISSING-NAME:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}id-table-entry-missing-name.json'
+// ID-TABLE-ENTRY-MISSING-NAME-NEXT: reading IdTable from field 'id_table'
+// ID-TABLE-ENTRY-MISSING-NAME-NEXT: reading EntityIdTable entry from index '0'
+// ID-TABLE-ENTRY-MISSING-NAME-NEXT: failed to read EntityName from field 'name': expected JSON object
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/id-table-entry-id-not-uint64.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=ID-TABLE-ENTRY-ID-NOT-UINT64
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/id-table-entry-id-not-uint64.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=ID-TABLE-ENTRY-ID-NOT-UINT64
+// ID-TABLE-ENTRY-ID-NOT-UINT64:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}id-table-entry-id-not-uint64.json'
+// ID-TABLE-ENTRY-ID-NOT-UINT64-NEXT: reading IdTable from field 'id_table'
+// ID-TABLE-ENTRY-ID-NOT-UINT64-NEXT: reading EntityIdTable entry from index '0'
+// ID-TABLE-ENTRY-ID-NOT-UINT64-NEXT: failed to read EntityId from field 'id': expected JSON number (unsigned 64-bit integer)
+
+// ============================================================================
+// entityIdTableFromJSON() errors
+// ============================================================================
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/id-table-not-array.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=ID-TABLE-NOT-ARRAY
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/id-table-not-array.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=ID-TABLE-NOT-ARRAY
+// ID-TABLE-NOT-ARRAY:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}id-table-not-array.json'
+// ID-TABLE-NOT-ARRAY-NEXT: failed to read IdTable from field 'id_table': expected JSON array
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/id-table-element-not-object.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=ID-TABLE-ELEMENT-NOT-OBJECT
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/id-table-element-not-object.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=ID-TABLE-ELEMENT-NOT-OBJECT
+// ID-TABLE-ELEMENT-NOT-OBJECT:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}id-table-element-not-object.json'
+// ID-TABLE-ELEMENT-NOT-OBJECT-NEXT: reading IdTable from field 'id_table'
+// ID-TABLE-ELEMENT-NOT-OBJECT-NEXT: failed to read EntityIdTable entry from index '0': expected JSON object
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/duplicate-entity.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=DUPLICATE-ENTITY
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/duplicate-entity.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=DUPLICATE-ENTITY
+// DUPLICATE-ENTITY:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}duplicate-entity.json'
+// DUPLICATE-ENTITY-NEXT: reading IdTable from field 'id_table'
+// DUPLICATE-ENTITY-NEXT: failed to insert EntityIdTable entry at index '1': encountered duplicate 'EntityId(0)'
+
+// ============================================================================
+// entitySummaryFromJSON() / encodingDataMapEntryFromJSON() errors
+// ============================================================================
+
+// Note: --encoding variants are not applicable for EntitySummary deserialization
+// tests because the encoding path stores raw JSON without deserializing.
+// RUN: not clang-ssaf-format --type tu %S/Inputs/read-entity-summary-no-format-info.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=READ-ENTITY-SUMMARY-NO-FORMAT-INFO
+// READ-ENTITY-SUMMARY-NO-FORMAT-INFO:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}read-entity-summary-no-format-info.json'
+// READ-ENTITY-SUMMARY-NO-FORMAT-INFO-NEXT: reading SummaryData entries from field 'data'
+// READ-ENTITY-SUMMARY-NO-FORMAT-INFO-NEXT: reading SummaryData entry from index '0'
+// READ-ENTITY-SUMMARY-NO-FORMAT-INFO-NEXT: reading EntitySummary entries from field 'summary_data'
+// READ-ENTITY-SUMMARY-NO-FORMAT-INFO-NEXT: reading EntitySummary entry from index '0'
+// READ-ENTITY-SUMMARY-NO-FORMAT-INFO-NEXT: reading EntitySummary from field 'entity_summary'
+// READ-ENTITY-SUMMARY-NO-FORMAT-INFO-NEXT: failed to deserialize EntitySummary: no FormatInfo registered for 'SummaryName(UnregisteredEntitySummary)'
+
+// ============================================================================
+// PairsEntitySummaryForJSONFormatTest Deserialization Error Tests
+// ============================================================================
+
+// RUN: not %clang-ssaf-format-with-plugin --type tu %S/Inputs/pairs-missing-pairs-field.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=PAIRS-MISSING-PAIRS-FIELD
+// PAIRS-MISSING-PAIRS-FIELD:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}pairs-missing-pairs-field.json'
+// PAIRS-MISSING-PAIRS-FIELD-NEXT: reading SummaryData entries from field 'data'
+// PAIRS-MISSING-PAIRS-FIELD-NEXT: reading SummaryData entry from index '0'
+// PAIRS-MISSING-PAIRS-FIELD-NEXT: reading EntitySummary entries from field 'summary_data'
+// PAIRS-MISSING-PAIRS-FIELD-NEXT: reading EntitySummary entry from index '0'
+// PAIRS-MISSING-PAIRS-FIELD-NEXT: reading EntitySummary from field 'entity_summary'
+// PAIRS-MISSING-PAIRS-FIELD-NEXT: missing or invalid field 'pairs'
+
+// RUN: not %clang-ssaf-format-with-plugin --type tu %S/Inputs/pairs-invalid-pairs-field-type.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=PAIRS-INVALID-PAIRS-FIELD-TYPE
+// PAIRS-INVALID-PAIRS-FIELD-TYPE:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}pairs-invalid-pairs-field-type.json'
+// PAIRS-INVALID-PAIRS-FIELD-TYPE-NEXT: reading SummaryData entries from field 'data'
+// PAIRS-INVALID-PAIRS-FIELD-TYPE-NEXT: reading SummaryData entry from index '0'
+// PAIRS-INVALID-PAIRS-FIELD-TYPE-NEXT: reading EntitySummary entries from field 'summary_data'
+// PAIRS-INVALID-PAIRS-FIELD-TYPE-NEXT: reading EntitySummary entry from index '0'
+// PAIRS-INVALID-PAIRS-FIELD-TYPE-NEXT: reading EntitySummary from field 'entity_summary'
+// PAIRS-INVALID-PAIRS-FIELD-TYPE-NEXT: missing or invalid field 'pairs'
+
+// RUN: not %clang-ssaf-format-with-plugin --type tu %S/Inputs/pairs-element-not-object.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=PAIRS-ELEMENT-NOT-OBJECT
+// PAIRS-ELEMENT-NOT-OBJECT:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}pairs-element-not-object.json'
+// PAIRS-ELEMENT-NOT-OBJECT-NEXT: reading SummaryData entries from field 'data'
+// PAIRS-ELEMENT-NOT-OBJECT-NEXT: reading SummaryData entry from index '0'
+// PAIRS-ELEMENT-NOT-OBJECT-NEXT: reading EntitySummary entries from field 'summary_data'
+// PAIRS-ELEMENT-NOT-OBJECT-NEXT: reading EntitySummary entry from index '0'
+// PAIRS-ELEMENT-NOT-OBJECT-NEXT: reading EntitySummary from field 'entity_summary'
+// PAIRS-ELEMENT-NOT-OBJECT-NEXT: pairs element at index 0 is not a JSON object
+
+// RUN: not %clang-ssaf-format-with-plugin --type tu %S/Inputs/pairs-missing-first-field.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=PAIRS-MISSING-FIRST-FIELD
+// PAIRS-MISSING-FIRST-FIELD:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}pairs-missing-first-field.json'
+// PAIRS-MISSING-FIRST-FIELD-NEXT: reading SummaryData entries from field 'data'
+// PAIRS-MISSING-FIRST-FIELD-NEXT: reading SummaryData entry from index '0'
+// PAIRS-MISSING-FIRST-FIELD-NEXT: reading EntitySummary entries from field 'summary_data'
+// PAIRS-MISSING-FIRST-FIELD-NEXT: reading EntitySummary entry from index '0'
+// PAIRS-MISSING-FIRST-FIELD-NEXT: reading EntitySummary from field 'entity_summary'
+// PAIRS-MISSING-FIRST-FIELD-NEXT: missing or invalid 'first' field at index '0'
+
+// RUN: not %clang-ssaf-format-with-plugin --type tu %S/Inputs/pairs-invalid-first-field.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=PAIRS-INVALID-FIRST-FIELD
+// PAIRS-INVALID-FIRST-FIELD:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}pairs-invalid-first-field.json'
+// PAIRS-INVALID-FIRST-FIELD-NEXT: reading SummaryData entries from field 'data'
+// PAIRS-INVALID-FIRST-FIELD-NEXT: reading SummaryData entry from index '0'
+// PAIRS-INVALID-FIRST-FIELD-NEXT: reading EntitySummary entries from field 'summary_data'
+// PAIRS-INVALID-FIRST-FIELD-NEXT: reading EntitySummary entry from index '0'
+// PAIRS-INVALID-FIRST-FIELD-NEXT: reading EntitySummary from field 'entity_summary'
+// PAIRS-INVALID-FIRST-FIELD-NEXT: missing or invalid 'first' field at index '0'
+
+// RUN: not %clang-ssaf-format-with-plugin --type tu %S/Inputs/pairs-missing-second-field.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=PAIRS-MISSING-SECOND-FIELD
+// PAIRS-MISSING-SECOND-FIELD:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}pairs-missing-second-field.json'
+// PAIRS-MISSING-SECOND-FIELD-NEXT: reading SummaryData entries from field 'data'
+// PAIRS-MISSING-SECOND-FIELD-NEXT: reading SummaryData entry from index '0'
+// PAIRS-MISSING-SECOND-FIELD-NEXT: reading EntitySummary entries from field 'summary_data'
+// PAIRS-MISSING-SECOND-FIELD-NEXT: reading EntitySummary entry from index '0'
+// PAIRS-MISSING-SECOND-FIELD-NEXT: reading EntitySummary from field 'entity_summary'
+// PAIRS-MISSING-SECOND-FIELD-NEXT: missing or invalid 'second' field at index '0'
+
+// RUN: not %clang-ssaf-format-with-plugin --type tu %S/Inputs/pairs-invalid-second-field.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=PAIRS-INVALID-SECOND-FIELD
+// PAIRS-INVALID-SECOND-FIELD:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}pairs-invalid-second-field.json'
+// PAIRS-INVALID-SECOND-FIELD-NEXT: reading SummaryData entries from field 'data'
+// PAIRS-INVALID-SECOND-FIELD-NEXT: reading SummaryData entry from index '0'
+// PAIRS-INVALID-SECOND-FIELD-NEXT: reading EntitySummary entries from field 'summary_data'
+// PAIRS-INVALID-SECOND-FIELD-NEXT: reading EntitySummary entry from index '0'
+// PAIRS-INVALID-SECOND-FIELD-NEXT: reading EntitySummary from field 'entity_summary'
+// PAIRS-INVALID-SECOND-FIELD-NEXT: missing or invalid 'second' field at index '0'
+
+// ============================================================================
+// entityDataMapEntryFromJSON() errors
+// ============================================================================
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/entity-data-missing-entity-id.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=ENTITY-DATA-MISSING-ENTITY-ID
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/entity-data-missing-entity-id.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=ENTITY-DATA-MISSING-ENTITY-ID
+// ENTITY-DATA-MISSING-ENTITY-ID:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}entity-data-missing-entity-id.json'
+// ENTITY-DATA-MISSING-ENTITY-ID-NEXT: reading SummaryData entries from field 'data'
+// ENTITY-DATA-MISSING-ENTITY-ID-NEXT: reading SummaryData entry from index '0'
+// ENTITY-DATA-MISSING-ENTITY-ID-NEXT: reading EntitySummary entries from field 'summary_data'
+// ENTITY-DATA-MISSING-ENTITY-ID-NEXT: reading EntitySummary entry from index '0'
+// ENTITY-DATA-MISSING-ENTITY-ID-NEXT: failed to read EntityId from field 'entity_id': expected JSON number (unsigned 64-bit integer)
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/entity-data-missing-entity-summary.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=ENTITY-DATA-MISSING-ENTITY-SUMMARY
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/entity-data-missing-entity-summary.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=ENTITY-DATA-MISSING-ENTITY-SUMMARY
+// ENTITY-DATA-MISSING-ENTITY-SUMMARY:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}entity-data-missing-entity-summary.json'
+// ENTITY-DATA-MISSING-ENTITY-SUMMARY-NEXT: reading SummaryData entries from field 'data'
+// ENTITY-DATA-MISSING-ENTITY-SUMMARY-NEXT: reading SummaryData entry from index '0'
+// ENTITY-DATA-MISSING-ENTITY-SUMMARY-NEXT: reading EntitySummary entries from field 'summary_data'
+// ENTITY-DATA-MISSING-ENTITY-SUMMARY-NEXT: reading EntitySummary entry from index '0'
+// ENTITY-DATA-MISSING-ENTITY-SUMMARY-NEXT: failed to read EntitySummary from field 'entity_summary': expected JSON object
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/entity-id-not-uint64.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=ENTITY-ID-NOT-UINT64
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/entity-id-not-uint64.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=ENTITY-ID-NOT-UINT64
+// ENTITY-ID-NOT-UINT64:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}entity-id-not-uint64.json'
+// ENTITY-ID-NOT-UINT64-NEXT: reading SummaryData entries from field 'data'
+// ENTITY-ID-NOT-UINT64-NEXT: reading SummaryData entry from index '0'
+// ENTITY-ID-NOT-UINT64-NEXT: reading EntitySummary entries from field 'summary_data'
+// ENTITY-ID-NOT-UINT64-NEXT: reading EntitySummary entry from index '0'
+// ENTITY-ID-NOT-UINT64-NEXT: failed to read EntityId from field 'entity_id': expected JSON number (unsigned 64-bit integer)
+
+// ============================================================================
+// entityDataMapFromJSON() errors
+// ============================================================================
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/entity-data-element-not-object.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=ENTITY-DATA-ELEMENT-NOT-OBJECT
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/entity-data-element-not-object.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=ENTITY-DATA-ELEMENT-NOT-OBJECT
+// ENTITY-DATA-ELEMENT-NOT-OBJECT:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}entity-data-element-not-object.json'
+// ENTITY-DATA-ELEMENT-NOT-OBJECT-NEXT: reading SummaryData entries from field 'data'
+// ENTITY-DATA-ELEMENT-NOT-OBJECT-NEXT: reading SummaryData entry from index '0'
+// ENTITY-DATA-ELEMENT-NOT-OBJECT-NEXT: reading EntitySummary entries from field 'summary_data'
+// ENTITY-DATA-ELEMENT-NOT-OBJECT-NEXT: failed to read EntitySummary entry from index '0': expected JSON object
+
+// RUN: not %clang-ssaf-format-with-plugin --type tu %S/Inputs/duplicate-entity-id-in-data-map.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=DUPLICATE-ENTITY-ID-IN-DATA-MAP
+// DUPLICATE-ENTITY-ID-IN-DATA-MAP:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}duplicate-entity-id-in-data-map.json'
+// DUPLICATE-ENTITY-ID-IN-DATA-MAP-NEXT: reading SummaryData entries from field 'data'
+// DUPLICATE-ENTITY-ID-IN-DATA-MAP-NEXT: reading SummaryData entry from index '0'
+// DUPLICATE-ENTITY-ID-IN-DATA-MAP-NEXT: reading EntitySummary entries from field 'summary_data'
+// DUPLICATE-ENTITY-ID-IN-DATA-MAP-NEXT: failed to insert EntitySummary entry at index '1': encountered duplicate 'EntityId(0)'
+
+// ============================================================================
+// summaryDataMapEntryFromJSON() errors
+// ============================================================================
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/data-entry-missing-summary-name.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=DATA-ENTRY-MISSING-SUMMARY-NAME
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/data-entry-missing-summary-name.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=DATA-ENTRY-MISSING-SUMMARY-NAME
+// DATA-ENTRY-MISSING-SUMMARY-NAME:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}data-entry-missing-summary-name.json'
+// DATA-ENTRY-MISSING-SUMMARY-NAME-NEXT: reading SummaryData entries from field 'data'
+// DATA-ENTRY-MISSING-SUMMARY-NAME-NEXT: reading SummaryData entry from index '0'
+// DATA-ENTRY-MISSING-SUMMARY-NAME-NEXT: failed to read SummaryName from field 'summary_name': expected JSON string
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/data-entry-missing-data.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=DATA-ENTRY-MISSING-DATA
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/data-entry-missing-data.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=DATA-ENTRY-MISSING-DATA
+// DATA-ENTRY-MISSING-DATA:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}data-entry-missing-data.json'
+// DATA-ENTRY-MISSING-DATA-NEXT: reading SummaryData entries from field 'data'
+// DATA-ENTRY-MISSING-DATA-NEXT: reading SummaryData entry from index '0'
+// DATA-ENTRY-MISSING-DATA-NEXT: failed to read EntitySummary entries from field 'summary_data': expected JSON array
+
+// ============================================================================
+// summaryDataMapFromJSON() / encodingSummaryDataMapFromJSON() errors
+// ============================================================================
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/data-not-array.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=DATA-NOT-ARRAY
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/data-not-array.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=DATA-NOT-ARRAY
+// DATA-NOT-ARRAY:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}data-not-array.json'
+// DATA-NOT-ARRAY-NEXT: failed to read SummaryData entries from field 'data': expected JSON array
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/data-element-not-object.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=DATA-ELEMENT-NOT-OBJECT
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/data-element-not-object.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=DATA-ELEMENT-NOT-OBJECT
+// DATA-ELEMENT-NOT-OBJECT:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}data-element-not-object.json'
+// DATA-ELEMENT-NOT-OBJECT-NEXT: reading SummaryData entries from field 'data'
+// DATA-ELEMENT-NOT-OBJECT-NEXT: failed to read SummaryData entry from index '0': expected JSON object
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/duplicate-summary-name.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=DUPLICATE-SUMMARY-NAME
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/duplicate-summary-name.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=DUPLICATE-SUMMARY-NAME
+// DUPLICATE-SUMMARY-NAME:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}duplicate-summary-name.json'
+// DUPLICATE-SUMMARY-NAME-NEXT: reading SummaryData entries from field 'data'
+// DUPLICATE-SUMMARY-NAME-NEXT: failed to insert SummaryData entry at index '1': encountered duplicate 'SummaryName(PairsEntitySummary)'
+
+// ============================================================================
+// readTUSummary() / readTUSummaryEncoding() errors
+// ============================================================================
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/missing-tu-namespace.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=MISSING-TU-NAMESPACE
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/missing-tu-namespace.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=MISSING-TU-NAMESPACE
+// MISSING-TU-NAMESPACE:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}missing-tu-namespace.json'
+// MISSING-TU-NAMESPACE-NEXT: failed to read BuildNamespace from field 'tu_namespace': expected JSON object
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/missing-id-table.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=MISSING-ID-TABLE
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/missing-id-table.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=MISSING-ID-TABLE
+// MISSING-ID-TABLE:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}missing-id-table.json'
+// MISSING-ID-TABLE-NEXT: failed to read IdTable from field 'id_table': expected JSON array
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/missing-linkage-table.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=MISSING-LINKAGE-TABLE
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/missing-linkage-table.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=MISSING-LINKAGE-TABLE
+// MISSING-LINKAGE-TABLE:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}missing-linkage-table.json'
+// MISSING-LINKAGE-TABLE-NEXT: failed to read LinkageTable from field 'linkage_table': expected JSON array
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/missing-data.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=MISSING-DATA
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/missing-data.json 2>&1 \
+// RUN:   | FileCheck %s --match-full-lines --check-prefix=MISSING-DATA
+// MISSING-DATA:      clang-ssaf-format: error: reading TUSummary from file '{{.*}}missing-data.json'
+// MISSING-DATA-NEXT: failed to read SummaryData entries from field 'data': expected JSON array
+
+// ============================================================================
+// writeJSON() errors
+// ============================================================================
+
+// RUN: echo '{}' > %t/existing.json
+// RUN: not clang-ssaf-format --type tu %S/Inputs/rt-empty.json -o %t/existing.json 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=WRITE-FILE-ALREADY-EXISTS
+// RUN: echo '{}' > %t/existing-enc.json
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/rt-empty.json -o %t/existing-enc.json 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=WRITE-FILE-ALREADY-EXISTS
+// WRITE-FILE-ALREADY-EXISTS: clang-ssaf-format: error: failed to validate path '{{.*}}existing{{.*}}.json': File already exists
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/rt-empty.json -o %t/nonexistent-dir/test.json 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=WRITE-PARENT-DIRECTORY-NOT-FOUND
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/rt-empty.json -o %t/nonexistent-dir/test.json 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=WRITE-PARENT-DIRECTORY-NOT-FOUND
+// WRITE-PARENT-DIRECTORY-NOT-FOUND: clang-ssaf-format: error: failed to validate path '{{.*}}nonexistent-dir{{.}}test.json': Parent directory does not exist
+
+// RUN: not clang-ssaf-format --type tu %S/Inputs/rt-empty.json -o %t/test.txt 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=WRITE-NOT-JSON-EXTENSION
+// RUN: not clang-ssaf-format --type tu --encoding %S/Inputs/rt-empty.json -o %t/test.txt 2>&1 \
+// RUN:   | FileCheck %s --check-prefix=WRITE-NOT-JSON-EXTENSION
+// WRITE-NOT-JSON-EXTENSION: clang-ssaf-format: error: failed to validate path '{{.*}}test.txt': No format registered for extension 'txt'
+
+// ============================================================================
+// Round-Trip Tests
+// ============================================================================
+
+// RUN: clang-ssaf-format --type tu %S/Inputs/rt-empty.json -o %t/rt-empty.json
+// RUN: diff %S/Inputs/rt-empty.json %t/rt-empty.json
+// RUN: clang-ssaf-format --type tu --encoding %S/Inputs/rt-empty.json -o %t/rt-empty-enc.json
+// RUN: diff %S/Inputs/rt-empty.json %t/rt-empty-enc.json
+
+// RUN: %clang-ssaf-format-with-plugin --type tu %S/Inputs/rt-two-summary-types.json -o %t/rt-two-summary-types.json
+// RUN: diff %S/Inputs/rt-two-summary-types.json %t/rt-two-summary-types.json
+// RUN: %clang-ssaf-format-with-plugin --type tu --encoding %S/Inputs/rt-two-summary-types.json -o %t/rt-two-summary-types-enc.json
+// RUN: diff %S/Inputs/rt-two-summary-types.json %t/rt-two-summary-types-enc.json
+
+// RUN: clang-ssaf-format --type tu %S/Inputs/rt-link-unit.json -o %t/rt-link-unit.json
+// RUN: diff %S/Inputs/rt-link-unit.json %t/rt-link-unit.json
+// RUN: clang-ssaf-format --type tu --encoding %S/Inputs/rt-link-unit.json -o %t/rt-link-unit-enc.json
+// RUN: diff %S/Inputs/rt-link-unit.json %t/rt-link-unit-enc.json
+
+// RUN: %clang-ssaf-format-with-plugin --type tu %S/Inputs/rt-empty-data-entry.json -o %t/rt-empty-data-entry.json
+// RUN: diff %S/Inputs/rt-empty-data-entry.json %t/rt-empty-data-entry.json
+// RUN: %clang-ssaf-format-with-plugin --type tu --encoding %S/Inputs/rt-empty-data-entry.json -o %t/rt-empty-data-entry-enc.json
+// RUN: diff %S/Inputs/rt-empty-data-entry.json %t/rt-empty-data-entry-enc.json
+
+// RUN: clang-ssaf-format --type tu %S/Inputs/rt-linkage-none.json -o %t/rt-linkage-none.json
+// RUN: diff %S/Inputs/rt-linkage-none.json %t/rt-linkage-none.json
+// RUN: clang-ssaf-format --type tu --encoding %S/Inputs/rt-linkage-none.json -o %t/rt-linkage-none-enc.json
+// RUN: diff %S/Inputs/rt-linkage-none.json %t/rt-linkage-none-enc.json
+
+// RUN: clang-ssaf-format --type tu %S/Inputs/rt-linkage-internal.json -o %t/rt-linkage-internal.json
+// RUN: diff %S/Inputs/rt-linkage-internal.json %t/rt-linkage-internal.json
+// RUN: clang-ssaf-format --type tu --encoding %S/Inputs/rt-linkage-internal.json -o %t/rt-linkage-internal-enc.json
+// RUN: diff %S/Inputs/rt-linkage-internal.json %t/rt-linkage-internal-enc.json
+
+// RUN: clang-ssaf-format --type tu %S/Inputs/rt-linkage-external.json -o %t/rt-linkage-external.json
+// RUN: diff %S/Inputs/rt-linkage-external.json %t/rt-linkage-external.json
+// RUN: clang-ssaf-format --type tu --encoding %S/Inputs/rt-linkage-external.json -o %t/rt-linkage-external-enc.json
+// RUN: diff %S/Inputs/rt-linkage-external.json %t/rt-linkage-external-enc.json
+
+// RUN: clang-ssaf-format --type tu %S/Inputs/rt-linkage-multiple.json -o %t/rt-linkage-multiple.json
+// RUN: diff %S/Inputs/rt-linkage-multiple.json %t/rt-linkage-multiple.json
+// RUN: clang-ssaf-format --type tu --encoding %S/Inputs/rt-linkage-multiple.json -o %t/rt-linkage-multiple-enc.json
+// RUN: diff %S/Inputs/rt-linkage-multiple.json %t/rt-linkage-multiple-enc.json
diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Serialization/JSONFormatTest/TUSummaryTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Serialization/JSONFormatTest/TUSummaryTest.cpp
index 7d0e34dd7c569..cd78c04cf15e1 100644
--- a/clang/unittests/ScalableStaticAnalysisFramework/Serialization/JSONFormatTest/TUSummaryTest.cpp
+++ b/clang/unittests/ScalableStaticAnalysisFramework/Serialization/JSONFormatTest/TUSummaryTest.cpp
@@ -209,1560 +209,46 @@ TEST_P(TUSummaryTest, NoReadPermission) {
   EXPECT_THAT_ERROR(std::move(RestoreError), Succeeded());
 }
 
-TEST_P(TUSummaryTest, InvalidSyntax) {
-  auto Result = readFromString("{ invalid json }");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(AllOf(HasSubstr("reading TUSummary from file"),
-                              HasSubstr("Expected object key"))));
-}
-
-TEST_P(TUSummaryTest, NotObject) {
-  auto Result = readFromString("[]");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(AllOf(HasSubstr("reading TUSummary from file"),
-                              HasSubstr("failed to read TUSummary"),
-                              HasSubstr("expected JSON object"))));
-}
-
-// ============================================================================
-// JSONFormat::entityLinkageFromJSON() Error Tests
-// ============================================================================
-
-TEST_P(TUSummaryTest, LinkageTableEntryLinkageMissingType) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": [
-      {
-        "id": 0,
-        "linkage": {}
-      }
-    ],
-    "data": []
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(
-          AllOf(HasSubstr("reading TUSummary from file"),
-                HasSubstr("reading LinkageTable from field 'linkage_table'"),
-                HasSubstr("reading LinkageTable entry from index '0'"),
-                HasSubstr("reading EntityLinkage from field 'linkage'"),
-                HasSubstr("failed to read EntityLinkageType from field 'type'"),
-                HasSubstr("expected JSON string"))));
-}
-
-TEST_P(TUSummaryTest, LinkageTableEntryLinkageInvalidType) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": [
-      {
-        "id": 0,
-        "linkage": { "type": "invalid_type" }
-      }
-    ],
-    "data": []
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(
-          AllOf(HasSubstr("reading TUSummary from file"),
-                HasSubstr("reading LinkageTable from field 'linkage_table'"),
-                HasSubstr("reading LinkageTable entry from index '0'"),
-                HasSubstr("reading EntityLinkage from field 'linkage'"),
-                HasSubstr("invalid EntityLinkageType value 'invalid_type' for "
-                          "field 'type'"))));
-}
-
-// ============================================================================
-// JSONFormat::linkageTableEntryFromJSON() Error Tests
-// ============================================================================
-
-TEST_P(TUSummaryTest, LinkageTableEntryMissingId) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": [
-      {
-        "linkage": { "type": "External" }
-      }
-    ],
-    "data": []
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(
-          AllOf(HasSubstr("reading TUSummary from file"),
-                HasSubstr("reading LinkageTable from field 'linkage_table'"),
-                HasSubstr("reading LinkageTable entry from index '0'"),
-                HasSubstr("failed to read EntityId from field 'id'"),
-                HasSubstr("expected JSON number (unsigned 64-bit integer)"))));
-}
-
-TEST_P(TUSummaryTest, LinkageTableEntryIdNotUInt64) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": [
-      {
-        "id": "not_a_number",
-        "linkage": { "type": "External" }
-      }
-    ],
-    "data": []
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(
-          AllOf(HasSubstr("reading TUSummary from file"),
-                HasSubstr("reading LinkageTable from field 'linkage_table'"),
-                HasSubstr("reading LinkageTable entry from index '0'"),
-                HasSubstr("failed to read EntityId from field 'id'"),
-                HasSubstr("expected JSON number (unsigned 64-bit integer)"))));
-}
-
-TEST_P(TUSummaryTest, LinkageTableEntryMissingLinkage) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": [
-      {
-        "id": 0
-      }
-    ],
-    "data": []
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(
-          AllOf(HasSubstr("reading TUSummary from file"),
-                HasSubstr("reading LinkageTable from field 'linkage_table'"),
-                HasSubstr("reading LinkageTable entry from index '0'"),
-                HasSubstr("failed to read EntityLinkage from field 'linkage'"),
-                HasSubstr("expected JSON object"))));
-}
-
-// ============================================================================
-// JSONFormat::linkageTableFromJSON() Error Tests
-// ============================================================================
-
-TEST_P(TUSummaryTest, LinkageTableNotArray) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": {},
-    "data": []
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("failed to read LinkageTable from field 'linkage_table'"),
-          HasSubstr("expected JSON array"))));
-}
-
-TEST_P(TUSummaryTest, LinkageTableElementNotObject) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": ["invalid"],
-    "data": []
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(
-          AllOf(HasSubstr("reading TUSummary from file"),
-                HasSubstr("reading LinkageTable from field 'linkage_table'"),
-                HasSubstr("failed to read LinkageTable entry from index '0'"),
-                HasSubstr("expected JSON object"))));
-}
-
-TEST_P(TUSummaryTest, LinkageTableExtraId) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": [
-      {
-        "id": 0,
-        "linkage": { "type": "External" }
-      }
-    ],
-    "data": []
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(
-          AllOf(HasSubstr("reading TUSummary from file"),
-                HasSubstr("reading LinkageTable from field 'linkage_table'"),
-                HasSubstr("reading LinkageTable entry from index '0'"),
-                HasSubstr("failed to deserialize LinkageTable"),
-                HasSubstr("extra 'EntityId(0)' not present in IdTable"))));
-}
-
-TEST_P(TUSummaryTest, LinkageTableMissingId) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [
-      {
-        "id": 0,
-        "name": {
-          "usr": "c:@F at foo",
-          "suffix": "",
-          "namespace": [
-            {
-              "kind": "CompilationUnit",
-              "name": "test.cpp"
-            }
-          ]
-        }
-      }
-    ],
-    "linkage_table": [],
-    "data": []
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(
-          AllOf(HasSubstr("reading TUSummary from file"),
-                HasSubstr("reading LinkageTable from field 'linkage_table'"),
-                HasSubstr("failed to deserialize LinkageTable"),
-                HasSubstr("missing 'EntityId(0)' present in IdTable"))));
-}
-
-TEST_P(TUSummaryTest, LinkageTableDuplicateId) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [
-      {
-        "id": 0,
-        "name": {
-          "usr": "c:@F at foo",
-          "suffix": "",
-          "namespace": [
-            {
-              "kind": "CompilationUnit",
-              "name": "test.cpp"
-            }
-          ]
-        }
-      }
-    ],
-    "linkage_table": [
-      {
-        "id": 0,
-        "linkage": { "type": "External" }
-      },
-      {
-        "id": 0,
-        "linkage": { "type": "Internal" }
-      }
-    ],
-    "data": []
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(
-          AllOf(HasSubstr("reading TUSummary from file"),
-                HasSubstr("reading LinkageTable from field 'linkage_table'"),
-                HasSubstr("failed to insert LinkageTable entry at index '1'"),
-                HasSubstr("encountered duplicate 'EntityId(0)'"))));
-}
-
-// ============================================================================
-// JSONFormat::buildNamespaceKindFromJSON() Error Tests
-// ============================================================================
-
-TEST_P(TUSummaryTest, InvalidKind) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "invalid_kind",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": [],
-    "data": []
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(
-          AllOf(HasSubstr("reading TUSummary from file"),
-                HasSubstr("reading BuildNamespace from field 'tu_namespace'"),
-                HasSubstr("reading BuildNamespaceKind from field 'kind'"),
-                HasSubstr("invalid BuildNamespaceKind value 'invalid_kind' for "
-                          "field 'kind'"))));
-}
-
-// ============================================================================
-// JSONFormat::buildNamespaceFromJSON() Error Tests
-// ============================================================================
-
-TEST_P(TUSummaryTest, MissingKind) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": [],
-    "data": []
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("reading BuildNamespace from field 'tu_namespace'"),
-          HasSubstr("failed to read BuildNamespaceKind from field 'kind'"),
-          HasSubstr("expected JSON string"))));
-}
-
-TEST_P(TUSummaryTest, MissingName) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit"
-    },
-    "id_table": [],
-    "linkage_table": [],
-    "data": []
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("reading BuildNamespace from field 'tu_namespace'"),
-          HasSubstr("failed to read BuildNamespaceName from field 'name'"),
-          HasSubstr("expected JSON string"))));
-}
-
-// ============================================================================
-// JSONFormat::nestedBuildNamespaceFromJSON() Error Tests
-// ============================================================================
-
-TEST_P(TUSummaryTest, NamespaceElementNotObject) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [
-      {
-        "id": 0,
-        "name": {
-          "usr": "c:@F at foo",
-          "suffix": "",
-          "namespace": ["invalid"]
-        }
-      }
-    ],
-    "linkage_table": [
-      {
-        "id": 0,
-        "linkage": { "type": "None" }
-      }
-    ],
-    "data": []
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("reading IdTable from field 'id_table'"),
-          HasSubstr("reading EntityIdTable entry from index '0'"),
-          HasSubstr("reading EntityName from field 'name'"),
-          HasSubstr("reading NestedBuildNamespace from field 'namespace'"),
-          HasSubstr("failed to read BuildNamespace from index '0'"),
-          HasSubstr("expected JSON object"))));
-}
-
-TEST_P(TUSummaryTest, NamespaceElementMissingKind) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [
-      {
-        "id": 0,
-        "name": {
-          "usr": "c:@F at foo",
-          "suffix": "",
-          "namespace": [
-            {
-              "name": "test.cpp"
-            }
-          ]
-        }
-      }
-    ],
-    "linkage_table": [
-      {
-        "id": 0,
-        "linkage": { "type": "Internal" }
-      }
-    ],
-    "data": []
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("reading IdTable from field 'id_table'"),
-          HasSubstr("reading EntityIdTable entry from index '0'"),
-          HasSubstr("reading EntityName from field 'name'"),
-          HasSubstr("reading NestedBuildNamespace from field 'namespace'"),
-          HasSubstr("reading BuildNamespace from index '0'"),
-          HasSubstr("failed to read BuildNamespaceKind from field 'kind'"),
-          HasSubstr("expected JSON string"))));
-}
-
-TEST_P(TUSummaryTest, NamespaceElementInvalidKind) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [
-      {
-        "id": 0,
-        "name": {
-          "usr": "c:@F at foo",
-          "suffix": "",
-          "namespace": [
-            {
-              "kind": "invalid_kind",
-              "name": "test.cpp"
-            }
-          ]
-        }
-      }
-    ],
-    "linkage_table": [
-      {
-        "id": 0,
-        "linkage": { "type": "External" }
-      }
-    ],
-    "data": []
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("reading IdTable from field 'id_table'"),
-          HasSubstr("reading EntityIdTable entry from index '0'"),
-          HasSubstr("reading EntityName from field 'name'"),
-          HasSubstr("reading NestedBuildNamespace from field 'namespace'"),
-          HasSubstr("reading BuildNamespace from index '0'"),
-          HasSubstr("reading BuildNamespaceKind from field 'kind'"),
-          HasSubstr("invalid BuildNamespaceKind value 'invalid_kind' for field "
-                    "'kind'"))));
-}
-
-TEST_P(TUSummaryTest, NamespaceElementMissingName) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [
-      {
-        "id": 0,
-        "name": {
-          "usr": "c:@F at foo",
-          "suffix": "",
-          "namespace": [
-            {
-              "kind": "CompilationUnit"
-            }
-          ]
-        }
-      }
-    ],
-    "linkage_table": [
-      {
-        "id": 0,
-        "linkage": { "type": "None" }
-      }
-    ],
-    "data": []
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("reading IdTable from field 'id_table'"),
-          HasSubstr("reading EntityIdTable entry from index '0'"),
-          HasSubstr("reading EntityName from field 'name'"),
-          HasSubstr("reading NestedBuildNamespace from field 'namespace'"),
-          HasSubstr("reading BuildNamespace from index '0'"),
-          HasSubstr("failed to read BuildNamespaceName from field 'name'"),
-          HasSubstr("expected JSON string"))));
-}
-
-// ============================================================================
-// JSONFormat::entityNameFromJSON() Error Tests
-// ============================================================================
-
-TEST_P(TUSummaryTest, EntityNameMissingUSR) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [
-      {
-        "id": 0,
-        "name": {
-          "suffix": "",
-          "namespace": []
-        }
-      }
-    ],
-    "linkage_table": [
-      {
-        "id": 0,
-        "linkage": { "type": "Internal" }
-      }
-    ],
-    "data": []
-  })");
-
-  EXPECT_THAT_ERROR(std::move(Result),
-                    FailedWithMessage(AllOf(
-                        HasSubstr("reading TUSummary from file"),
-                        HasSubstr("reading IdTable from field 'id_table'"),
-                        HasSubstr("reading EntityIdTable entry from index '0'"),
-                        HasSubstr("reading EntityName from field 'name'"),
-                        HasSubstr("failed to read USR from field 'usr'"),
-                        HasSubstr("expected JSON string"))));
-}
-
-TEST_P(TUSummaryTest, EntityNameMissingSuffix) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [
-      {
-        "id": 0,
-        "name": {
-          "usr": "c:@F at foo",
-          "namespace": []
-        }
-      }
-    ],
-    "linkage_table": [
-      {
-        "id": 0,
-        "linkage": { "type": "External" }
-      }
-    ],
-    "data": []
-  })");
-
-  EXPECT_THAT_ERROR(std::move(Result),
-                    FailedWithMessage(AllOf(
-                        HasSubstr("reading TUSummary from file"),
-                        HasSubstr("reading IdTable from field 'id_table'"),
-                        HasSubstr("reading EntityIdTable entry from index '0'"),
-                        HasSubstr("reading EntityName from field 'name'"),
-                        HasSubstr("failed to read Suffix from field 'suffix'"),
-                        HasSubstr("expected JSON string"))));
-}
-
-TEST_P(TUSummaryTest, EntityNameMissingNamespace) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [
-      {
-        "id": 0,
-        "name": {
-          "usr": "c:@F at foo",
-          "suffix": ""
-        }
-      }
-    ],
-    "linkage_table": [
-      {
-        "id": 0,
-        "linkage": { "type": "None" }
-      }
-    ],
-    "data": []
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("reading IdTable from field 'id_table'"),
-          HasSubstr("reading EntityIdTable entry from index '0'"),
-          HasSubstr("reading EntityName from field 'name'"),
-          HasSubstr(
-              "failed to read NestedBuildNamespace from field 'namespace'"),
-          HasSubstr("expected JSON array"))));
-}
-
-// ============================================================================
-// JSONFormat::entityIdTableEntryFromJSON() Error Tests
-// ============================================================================
-
-TEST_P(TUSummaryTest, IDTableEntryMissingID) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [
-      {
-        "name": {
-          "usr": "c:@F at foo",
-          "suffix": "",
-          "namespace": []
-        }
-      }
-    ],
-    "linkage_table": [],
-    "data": []
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(
-          AllOf(HasSubstr("reading TUSummary from file"),
-                HasSubstr("reading IdTable from field 'id_table'"),
-                HasSubstr("reading EntityIdTable entry from index '0'"),
-                HasSubstr("failed to read EntityId from field 'id'"),
-                HasSubstr("expected JSON number (unsigned 64-bit integer)"))));
-}
-
-TEST_P(TUSummaryTest, IDTableEntryMissingName) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [
-      {
-        "id": 0
-      }
-    ],
-    "linkage_table": [
-      {
-        "id": 0,
-        "linkage": { "type": "None" }
-      }
-    ],
-    "data": []
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(
-          AllOf(HasSubstr("reading TUSummary from file"),
-                HasSubstr("reading IdTable from field 'id_table'"),
-                HasSubstr("reading EntityIdTable entry from index '0'"),
-                HasSubstr("failed to read EntityName from field 'name'"),
-                HasSubstr("expected JSON object"))));
-}
-
-TEST_P(TUSummaryTest, IDTableEntryIDNotUInt64) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [
-      {
-        "id": "not_a_number",
-        "name": {
-          "usr": "c:@F at foo",
-          "suffix": "",
-          "namespace": []
-        }
-      }
-    ],
-    "linkage_table": [],
-    "data": []
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(
-          AllOf(HasSubstr("reading TUSummary from file"),
-                HasSubstr("reading IdTable from field 'id_table'"),
-                HasSubstr("reading EntityIdTable entry from index '0'"),
-                HasSubstr("failed to read EntityId from field 'id'"),
-                HasSubstr("expected JSON number (unsigned 64-bit integer)"))));
-}
-
-// ============================================================================
-// JSONFormat::entityIdTableFromJSON() Error Tests
-// ============================================================================
-
-TEST_P(TUSummaryTest, IDTableNotArray) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": {},
-    "linkage_table": [],
-    "data": []
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(
-          AllOf(HasSubstr("reading TUSummary from file"),
-                HasSubstr("failed to read IdTable from field 'id_table'"),
-                HasSubstr("expected JSON array"))));
-}
-
-TEST_P(TUSummaryTest, IDTableElementNotObject) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [123],
-    "linkage_table": [],
-    "data": []
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(
-          AllOf(HasSubstr("reading TUSummary from file"),
-                HasSubstr("reading IdTable from field 'id_table'"),
-                HasSubstr("failed to read EntityIdTable entry from index '0'"),
-                HasSubstr("expected JSON object"))));
-}
-
-TEST_P(TUSummaryTest, DuplicateEntity) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [
-      {
-        "id": 0,
-        "name": {
-          "usr": "c:@F at foo",
-          "suffix": "",
-          "namespace": [
-            {
-              "kind": "CompilationUnit",
-              "name": "test.cpp"
-            }
-          ]
-        }
-      },
-      {
-        "id": 1,
-        "name": {
-          "usr": "c:@F at foo",
-          "suffix": "",
-          "namespace": [
-            {
-              "kind": "CompilationUnit",
-              "name": "test.cpp"
-            }
-          ]
-        }
-      }
-    ],
-    "linkage_table": [
-      {
-        "id": 0,
-        "linkage": { "type": "Internal" }
-      },
-      {
-        "id": 1,
-        "linkage": { "type": "External" }
-      }
-    ],
-    "data": []
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(
-          AllOf(HasSubstr("reading TUSummary from file"),
-                HasSubstr("reading IdTable from field 'id_table'"),
-                HasSubstr("failed to insert EntityIdTable entry at index '1'"),
-                HasSubstr("encountered duplicate 'EntityId(0)'"))));
-}
-
-// ============================================================================
-// JSONFormat::entitySummaryFromJSON() / encodingDataMapEntryFromJSON() Tests
-// ============================================================================
-
-TEST_F(JSONFormatTUSummaryTest, ReadEntitySummaryNoFormatInfo) {
-  auto Result = readTUSummaryFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": [],
-    "data": [
-      {
-        "summary_name": "UnregisteredEntitySummaryForJSONFormatTest",
-        "summary_data": [
-          {
-            "entity_id": 0,
-            "entity_summary": {}
-          }
-        ]
-      }
-    ]
-  })");
-
-  EXPECT_THAT_EXPECTED(
-      Result,
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("reading SummaryData entries from field 'data'"),
-          HasSubstr("reading SummaryData entry from index '0'"),
-          HasSubstr("reading EntitySummary entries from field 'summary_data'"),
-          HasSubstr("reading EntitySummary entry from index '0'"),
-          HasSubstr("reading EntitySummary from field 'entity_summary'"),
-          HasSubstr("failed to deserialize EntitySummary"),
-          HasSubstr(
-              "no FormatInfo registered for "
-              "'SummaryName(UnregisteredEntitySummaryForJSONFormatTest)'"))));
-}
-
-// ============================================================================
-// PairsEntitySummaryForJSONFormatTest Deserialization Error Tests
-// ============================================================================
-
-TEST_F(JSONFormatTUSummaryTest,
-       PairsEntitySummaryForJSONFormatTestMissingPairsField) {
-  auto Result = readTUSummaryFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": [],
-    "data": [
-      {
-        "summary_name": "PairsEntitySummaryForJSONFormatTest",
-        "summary_data": [
-          {
-            "entity_id": 0,
-            "entity_summary": {}
-          }
-        ]
-      }
-    ]
-  })");
-
-  EXPECT_THAT_EXPECTED(
-      Result,
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("reading SummaryData entries from field 'data'"),
-          HasSubstr("reading SummaryData entry from index '0'"),
-          HasSubstr("reading EntitySummary entries from field 'summary_data'"),
-          HasSubstr("reading EntitySummary entry from index '0'"),
-          HasSubstr("reading EntitySummary from field 'entity_summary'"),
-          HasSubstr("missing or invalid field 'pairs'"))));
-}
-
-TEST_F(JSONFormatTUSummaryTest,
-       PairsEntitySummaryForJSONFormatTestInvalidPairsFieldType) {
-  auto Result = readTUSummaryFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": [],
-    "data": [
-      {
-        "summary_name": "PairsEntitySummaryForJSONFormatTest",
-        "summary_data": [
-          {
-            "entity_id": 0,
-            "entity_summary": {
-              "pairs": "not_an_array"
-            }
-          }
-        ]
-      }
-    ]
-  })");
-
-  EXPECT_THAT_EXPECTED(
-      Result,
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("reading SummaryData entries from field 'data'"),
-          HasSubstr("reading SummaryData entry from index '0'"),
-          HasSubstr("reading EntitySummary entries from field 'summary_data'"),
-          HasSubstr("reading EntitySummary entry from index '0'"),
-          HasSubstr("reading EntitySummary from field 'entity_summary'"),
-          HasSubstr("missing or invalid field 'pairs'"))));
-}
-
-TEST_F(JSONFormatTUSummaryTest,
-       PairsEntitySummaryForJSONFormatTestPairsElementNotObject) {
-  auto Result = readTUSummaryFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": [],
-    "data": [
-      {
-        "summary_name": "PairsEntitySummaryForJSONFormatTest",
-        "summary_data": [
-          {
-            "entity_id": 0,
-            "entity_summary": {
-              "pairs": ["not_an_object"]
-            }
-          }
-        ]
-      }
-    ]
-  })");
-
-  EXPECT_THAT_EXPECTED(
-      Result,
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("reading SummaryData entries from field 'data'"),
-          HasSubstr("reading SummaryData entry from index '0'"),
-          HasSubstr("reading EntitySummary entries from field 'summary_data'"),
-          HasSubstr("reading EntitySummary entry from index '0'"),
-          HasSubstr("reading EntitySummary from field 'entity_summary'"),
-          HasSubstr("pairs element at index 0 is not a JSON object"))));
-}
-
-TEST_F(JSONFormatTUSummaryTest,
-       PairsEntitySummaryForJSONFormatTestMissingFirstField) {
-  auto Result = readTUSummaryFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": [],
-    "data": [
-      {
-        "summary_name": "PairsEntitySummaryForJSONFormatTest",
-        "summary_data": [
-          {
-            "entity_id": 0,
-            "entity_summary": {
-              "pairs": [
-                {
-                  "second": {"@": 1}
-                }
-              ]
-            }
-          }
-        ]
-      }
-    ]
-  })");
-
-  EXPECT_THAT_EXPECTED(
-      Result,
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("reading SummaryData entries from field 'data'"),
-          HasSubstr("reading SummaryData entry from index '0'"),
-          HasSubstr("reading EntitySummary entries from field 'summary_data'"),
-          HasSubstr("reading EntitySummary entry from index '0'"),
-          HasSubstr("reading EntitySummary from field 'entity_summary'"),
-          HasSubstr("missing or invalid 'first' field at index '0'"))));
-}
-
-TEST_F(JSONFormatTUSummaryTest,
-       PairsEntitySummaryForJSONFormatTestInvalidFirstField) {
-  auto Result = readTUSummaryFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": [],
-    "data": [
-      {
-        "summary_name": "PairsEntitySummaryForJSONFormatTest",
-        "summary_data": [
-          {
-            "entity_id": 0,
-            "entity_summary": {
-              "pairs": [
-                {
-                  "first": "not_a_number",
-                  "second": {"@": 1}
-                }
-              ]
-            }
-          }
-        ]
-      }
-    ]
-  })");
-
-  EXPECT_THAT_EXPECTED(
-      Result,
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("reading SummaryData entries from field 'data'"),
-          HasSubstr("reading SummaryData entry from index '0'"),
-          HasSubstr("reading EntitySummary entries from field 'summary_data'"),
-          HasSubstr("reading EntitySummary entry from index '0'"),
-          HasSubstr("reading EntitySummary from field 'entity_summary'"),
-          HasSubstr("missing or invalid 'first' field at index '0'"))));
-}
-
-TEST_F(JSONFormatTUSummaryTest,
-       PairsEntitySummaryForJSONFormatTestMissingSecondField) {
-  auto Result = readTUSummaryFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": [],
-    "data": [
-      {
-        "summary_name": "PairsEntitySummaryForJSONFormatTest",
-        "summary_data": [
-          {
-            "entity_id": 0,
-            "entity_summary": {
-              "pairs": [
-                {
-                  "first": {"@": 0}
-                }
-              ]
-            }
-          }
-        ]
-      }
-    ]
-  })");
-
-  EXPECT_THAT_EXPECTED(
-      Result,
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("reading SummaryData entries from field 'data'"),
-          HasSubstr("reading SummaryData entry from index '0'"),
-          HasSubstr("reading EntitySummary entries from field 'summary_data'"),
-          HasSubstr("reading EntitySummary entry from index '0'"),
-          HasSubstr("reading EntitySummary from field 'entity_summary'"),
-          HasSubstr("missing or invalid 'second' field at index '0'"))));
-}
-
-TEST_F(JSONFormatTUSummaryTest,
-       PairsEntitySummaryForJSONFormatTestInvalidSecondField) {
-  auto Result = readTUSummaryFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": [],
-    "data": [
-      {
-        "summary_name": "PairsEntitySummaryForJSONFormatTest",
-        "summary_data": [
-          {
-            "entity_id": 0,
-            "entity_summary": {
-              "pairs": [
-                {
-                  "first": {"@": 0},
-                  "second": "not_a_number"
-                }
-              ]
-            }
-          }
-        ]
-      }
-    ]
-  })");
-
-  EXPECT_THAT_EXPECTED(
-      Result,
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("reading SummaryData entries from field 'data'"),
-          HasSubstr("reading SummaryData entry from index '0'"),
-          HasSubstr("reading EntitySummary entries from field 'summary_data'"),
-          HasSubstr("reading EntitySummary entry from index '0'"),
-          HasSubstr("reading EntitySummary from field 'entity_summary'"),
-          HasSubstr("missing or invalid 'second' field at index '0'"))));
-}
-
-// ============================================================================
-// JSONFormat::entityDataMapEntryFromJSON() Error Tests
-// ============================================================================
-
-TEST_P(TUSummaryTest, EntityDataMissingEntityID) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": [],
-    "data": [
-      {
-        "summary_name": "PairsEntitySummaryForJSONFormatTest",
-        "summary_data": [
-          {
-            "entity_summary": {}
-          }
-        ]
-      }
-    ]
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("reading SummaryData entries from field 'data'"),
-          HasSubstr("reading SummaryData entry from index '0'"),
-          HasSubstr("reading EntitySummary entries from field 'summary_data'"),
-          HasSubstr("reading EntitySummary entry from index '0'"),
-          HasSubstr("failed to read EntityId from field 'entity_id'"),
-          HasSubstr("expected JSON number (unsigned 64-bit integer)"))));
-}
-
-TEST_P(TUSummaryTest, EntityDataMissingEntitySummary) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": [],
-    "data": [
-      {
-        "summary_name": "PairsEntitySummaryForJSONFormatTest",
-        "summary_data": [
-          {
-            "entity_id": 0
-          }
-        ]
-      }
-    ]
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("reading SummaryData entries from field 'data'"),
-          HasSubstr("reading SummaryData entry from index '0'"),
-          HasSubstr("reading EntitySummary entries from field 'summary_data'"),
-          HasSubstr("reading EntitySummary entry from index '0'"),
-          HasSubstr("failed to read EntitySummary from field 'entity_summary'"),
-          HasSubstr("expected JSON object"))));
-}
-
-TEST_P(TUSummaryTest, EntityIDNotUInt64) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": [],
-    "data": [
-      {
-        "summary_name": "PairsEntitySummaryForJSONFormatTest",
-        "summary_data": [
-          {
-            "entity_id": "not_a_number",
-            "entity_summary": {}
-          }
-        ]
-      }
-    ]
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("reading SummaryData entries from field 'data'"),
-          HasSubstr("reading SummaryData entry from index '0'"),
-          HasSubstr("reading EntitySummary entries from field 'summary_data'"),
-          HasSubstr("reading EntitySummary entry from index '0'"),
-          HasSubstr("failed to read EntityId from field 'entity_id'"),
-          HasSubstr("expected JSON number (unsigned 64-bit integer)"))));
-}
-
-TEST_F(JSONFormatTUSummaryTest, ReadEntitySummaryMissingData) {
-  auto Result = readTUSummaryFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": [],
-    "data": [
-      {
-        "summary_name": "NullEntitySummaryForJSONFormatTest",
-        "summary_data": [
-          {
-            "entity_id": 0,
-            "entity_summary": {}
-          }
-        ]
-      }
-    ]
-  })");
-
-  EXPECT_THAT_EXPECTED(
-      Result,
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("reading SummaryData entries from field 'data'"),
-          HasSubstr("reading SummaryData entry from index '0'"),
-          HasSubstr("reading EntitySummary entries from field 'summary_data'"),
-          HasSubstr("reading EntitySummary entry from index '0'"),
-          HasSubstr("failed to deserialize EntitySummary"),
-          HasSubstr("null EntitySummary data for "
-                    "'SummaryName(NullEntitySummaryForJSONFormatTest)'"))));
-}
-
-TEST_F(JSONFormatTUSummaryTest, ReadEntitySummaryMismatchedSummaryName) {
-  auto Result = readTUSummaryFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": [],
-    "data": [
-      {
-        "summary_name": "MismatchedEntitySummaryForJSONFormatTest",
-        "summary_data": [
-          {
-            "entity_id": 0,
-            "entity_summary": {}
-          }
-        ]
-      }
-    ]
-  })");
-
-  EXPECT_THAT_EXPECTED(
-      Result,
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("reading SummaryData entries from field 'data'"),
-          HasSubstr("reading SummaryData entry from index '0'"),
-          HasSubstr("reading EntitySummary entries from field 'summary_data'"),
-          HasSubstr("reading EntitySummary entry from index '0'"),
-          HasSubstr("failed to deserialize EntitySummary"),
-          HasSubstr(
-              "EntitySummary data for "
-              "'SummaryName(MismatchedEntitySummaryForJSONFormatTest)' reports "
-              "mismatched "
-              "'SummaryName(MismatchedEntitySummaryForJSONFormatTest_WrongName)"
-              "'"))));
-}
-
-// ============================================================================
-// JSONFormat::entityDataMapEntryToJSON() Fatal Tests
-// ============================================================================
-
-TEST_F(JSONFormatTUSummaryTest, WriteEntitySummaryMissingData) {
-  TUSummary Summary(
-      BuildNamespace(BuildNamespaceKind::CompilationUnit, "test.cpp"));
-
-  NestedBuildNamespace Namespace =
-      NestedBuildNamespace::makeCompilationUnit("test.cpp");
-  EntityId EI = getIdTable(Summary).getId(
-      EntityName{"c:@F at foo", "", std::move(Namespace)});
-
-  SummaryName SN("NullEntitySummaryForJSONFormatTest");
-  getData(Summary)[SN][EI] = nullptr;
-
-  EXPECT_DEATH(
-      { (void)writeTUSummary(Summary, "output.json"); },
-      "JSONFormat - null EntitySummary data for "
-      "'SummaryName\\(NullEntitySummaryForJSONFormatTest\\)'");
-}
-
-TEST_F(JSONFormatTUSummaryTest, WriteEntitySummaryMismatchedSummaryName) {
-  TUSummary Summary(
-      BuildNamespace(BuildNamespaceKind::CompilationUnit, "test.cpp"));
-
-  NestedBuildNamespace Namespace =
-      NestedBuildNamespace::makeCompilationUnit("test.cpp");
-  EntityId EI = getIdTable(Summary).getId(
-      EntityName{"c:@F at foo", "", std::move(Namespace)});
-
-  SummaryName SN("MismatchedEntitySummaryForJSONFormatTest");
-  getData(Summary)[SN][EI] =
-      std::make_unique<MismatchedEntitySummaryForJSONFormatTest>();
-
-  EXPECT_DEATH(
-      { (void)writeTUSummary(Summary, "output.json"); },
-      "JSONFormat - EntitySummary data for "
-      "'SummaryName\\(MismatchedEntitySummaryForJSONFormatTest\\)' reports "
-      "mismatched "
-      "'SummaryName\\(MismatchedEntitySummaryForJSONFormatTest_WrongName\\)'");
-}
-
-// ============================================================================
-// JSONFormat::entityDataMapFromJSON() Error Tests
-// ============================================================================
-
-TEST_P(TUSummaryTest, EntityDataElementNotObject) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": [],
-    "data": [
-      {
-        "summary_name": "PairsEntitySummaryForJSONFormatTest",
-        "summary_data": ["invalid"]
-      }
-    ]
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("reading SummaryData entries from field 'data'"),
-          HasSubstr("reading SummaryData entry from index '0'"),
-          HasSubstr("reading EntitySummary entries from field 'summary_data'"),
-          HasSubstr("failed to read EntitySummary entry from index '0'"),
-          HasSubstr("expected JSON object"))));
-}
-
-TEST_P(TUSummaryTest, DuplicateEntityIdInDataMap) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [
-      {
-        "id": 0,
-        "name": {
-          "usr": "c:@F at foo",
-          "suffix": "",
-          "namespace": []
-        }
-      }
-    ],
-    "linkage_table": [
-      {
-        "id": 0,
-        "linkage": {
-          "type": "None"
-        }
-      }
-    ],
-    "data": [
-      {
-        "summary_name": "PairsEntitySummaryForJSONFormatTest",
-        "summary_data": [
-          {
-            "entity_id": 0,
-            "entity_summary": {
-              "pairs": []
-            }
-          },
-          {
-            "entity_id": 0,
-            "entity_summary": {
-              "pairs": []
-            }
-          }
-        ]
-      }
-    ]
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("reading SummaryData entries from field 'data'"),
-          HasSubstr("reading SummaryData entry from index '0'"),
-          HasSubstr("reading EntitySummary entries from field 'summary_data'"),
-          HasSubstr("failed to insert EntitySummary entry at index '1'"),
-          HasSubstr("encountered duplicate 'EntityId(0)'"))));
-}
-
-// ============================================================================
-// JSONFormat::summaryDataMapEntryFromJSON() Error Tests
-// ============================================================================
-
-TEST_P(TUSummaryTest, DataEntryMissingSummaryName) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": [],
-    "data": [
-      {
-        "summary_data": []
-      }
-    ]
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("reading SummaryData entries from field 'data'"),
-          HasSubstr("reading SummaryData entry from index '0'"),
-          HasSubstr("failed to read SummaryName from field 'summary_name'"),
-          HasSubstr("expected JSON string"))));
-}
-
-TEST_P(TUSummaryTest, DataEntryMissingData) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": [],
-    "data": [
-      {
-        "summary_name": "PairsEntitySummaryForJSONFormatTest"
-      }
-    ]
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("reading SummaryData entries from field 'data'"),
-          HasSubstr("reading SummaryData entry from index '0'"),
-          HasSubstr(
-              "failed to read EntitySummary entries from field 'summary_data'"),
-          HasSubstr("expected JSON array"))));
-}
-
 // ============================================================================
-// JSONFormat::summaryDataMapFromJSON() / encodingSummaryDataMapFromJSON() Tests
+// JSONFormat::entityDataMapEntryFromJSON() Error Tests
 // ============================================================================
 
-TEST_P(TUSummaryTest, DataNotArray) {
-  auto Result = readFromString(R"({
+TEST_F(JSONFormatTUSummaryTest, ReadEntitySummaryMissingData) {
+  auto Result = readTUSummaryFromString(R"({
     "tu_namespace": {
       "kind": "CompilationUnit",
       "name": "test.cpp"
     },
     "id_table": [],
     "linkage_table": [],
-    "data": {}
+    "data": [
+      {
+        "summary_name": "NullEntitySummaryForJSONFormatTest",
+        "summary_data": [
+          {
+            "entity_id": 0,
+            "entity_summary": {}
+          }
+        ]
+      }
+    ]
   })");
 
-  EXPECT_THAT_ERROR(
-      std::move(Result),
+  EXPECT_THAT_EXPECTED(
+      Result,
       FailedWithMessage(AllOf(
           HasSubstr("reading TUSummary from file"),
-          HasSubstr("failed to read SummaryData entries from field 'data'"),
-          HasSubstr("expected JSON array"))));
-}
-
-TEST_P(TUSummaryTest, DataElementNotObject) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": [],
-    "data": ["invalid"]
-  })");
-
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(
-          AllOf(HasSubstr("reading TUSummary from file"),
-                HasSubstr("reading SummaryData entries from field 'data'"),
-                HasSubstr("failed to read SummaryData entry from index '0'"),
-                HasSubstr("expected JSON object"))));
+          HasSubstr("reading SummaryData entries from field 'data'"),
+          HasSubstr("reading SummaryData entry from index '0'"),
+          HasSubstr("reading EntitySummary entries from field 'summary_data'"),
+          HasSubstr("reading EntitySummary entry from index '0'"),
+          HasSubstr("failed to deserialize EntitySummary"),
+          HasSubstr("null EntitySummary data for "
+                    "'SummaryName(NullEntitySummaryForJSONFormatTest)'"))));
 }
 
-TEST_P(TUSummaryTest, DuplicateSummaryName) {
-  auto Result = readFromString(R"({
+TEST_F(JSONFormatTUSummaryTest, ReadEntitySummaryMismatchedSummaryName) {
+  auto Result = readTUSummaryFromString(R"({
     "tu_namespace": {
       "kind": "CompilationUnit",
       "name": "test.cpp"
@@ -1771,96 +257,77 @@ TEST_P(TUSummaryTest, DuplicateSummaryName) {
     "linkage_table": [],
     "data": [
       {
-        "summary_name": "PairsEntitySummaryForJSONFormatTest",
-        "summary_data": []
-      },
-      {
-        "summary_name": "PairsEntitySummaryForJSONFormatTest",
-        "summary_data": []
+        "summary_name": "MismatchedEntitySummaryForJSONFormatTest",
+        "summary_data": [
+          {
+            "entity_id": 0,
+            "entity_summary": {}
+          }
+        ]
       }
     ]
   })");
 
-  EXPECT_THAT_ERROR(
-      std::move(Result),
+  EXPECT_THAT_EXPECTED(
+      Result,
       FailedWithMessage(AllOf(
           HasSubstr("reading TUSummary from file"),
           HasSubstr("reading SummaryData entries from field 'data'"),
-          HasSubstr("failed to insert SummaryData entry at index '1'"),
-          HasSubstr("encountered duplicate "
-                    "'SummaryName(PairsEntitySummaryForJSONFormatTest)'"))));
+          HasSubstr("reading SummaryData entry from index '0'"),
+          HasSubstr("reading EntitySummary entries from field 'summary_data'"),
+          HasSubstr("reading EntitySummary entry from index '0'"),
+          HasSubstr("failed to deserialize EntitySummary"),
+          HasSubstr("EntitySummary data for "
+                    "'SummaryName(MismatchedEntitySummaryForJSONFormatTest)' "
+                    "reports "
+                    "mismatched "
+                    "'SummaryName(MismatchedEntitySummaryForJSONFormatTest_"
+                    "WrongName)"
+                    "'"))));
 }
 
 // ============================================================================
-// JSONFormat::readTUSummary() / readTUSummaryEncoding() Error Tests
+// JSONFormat::entityDataMapEntryToJSON() Fatal Tests
 // ============================================================================
 
-TEST_P(TUSummaryTest, MissingTUNamespace) {
-  auto Result = readFromString(R"({
-    "id_table": [],
-    "linkage_table": [],
-    "data": []
-  })");
+TEST_F(JSONFormatTUSummaryTest, WriteEntitySummaryMissingData) {
+  TUSummary Summary(
+      BuildNamespace(BuildNamespaceKind::CompilationUnit, "test.cpp"));
 
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("failed to read BuildNamespace from field 'tu_namespace'"),
-          HasSubstr("expected JSON object"))));
-}
+  NestedBuildNamespace Namespace =
+      NestedBuildNamespace::makeCompilationUnit("test.cpp");
+  EntityId EI = getIdTable(Summary).getId(
+      EntityName{"c:@F at foo", "", std::move(Namespace)});
 
-TEST_P(TUSummaryTest, MissingIDTable) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "data": []
-  })");
+  SummaryName SN("NullEntitySummaryForJSONFormatTest");
+  getData(Summary)[SN][EI] = nullptr;
 
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(
-          AllOf(HasSubstr("reading TUSummary from file"),
-                HasSubstr("failed to read IdTable from field 'id_table'"),
-                HasSubstr("expected JSON array"))));
+  EXPECT_DEATH(
+      { (void)writeTUSummary(Summary, "output.json"); },
+      "JSONFormat - null EntitySummary data for "
+      "'SummaryName\\(NullEntitySummaryForJSONFormatTest\\)'");
 }
 
-TEST_P(TUSummaryTest, MissingLinkageTable) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "data": []
-  })");
+TEST_F(JSONFormatTUSummaryTest, WriteEntitySummaryMismatchedSummaryName) {
+  TUSummary Summary(
+      BuildNamespace(BuildNamespaceKind::CompilationUnit, "test.cpp"));
 
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("failed to read LinkageTable from field 'linkage_table'"),
-          HasSubstr("expected JSON array"))));
-}
+  NestedBuildNamespace Namespace =
+      NestedBuildNamespace::makeCompilationUnit("test.cpp");
+  EntityId EI = getIdTable(Summary).getId(
+      EntityName{"c:@F at foo", "", std::move(Namespace)});
 
-TEST_P(TUSummaryTest, MissingData) {
-  auto Result = readFromString(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": []
-  })");
+  SummaryName SN("MismatchedEntitySummaryForJSONFormatTest");
+  getData(Summary)[SN][EI] =
+      std::make_unique<MismatchedEntitySummaryForJSONFormatTest>();
 
-  EXPECT_THAT_ERROR(
-      std::move(Result),
-      FailedWithMessage(AllOf(
-          HasSubstr("reading TUSummary from file"),
-          HasSubstr("failed to read SummaryData entries from field 'data'"),
-          HasSubstr("expected JSON array"))));
+  EXPECT_DEATH(
+      { (void)writeTUSummary(Summary, "output.json"); },
+      "JSONFormat - EntitySummary data for "
+      "'SummaryName\\(MismatchedEntitySummaryForJSONFormatTest\\)' reports "
+      "mismatched "
+      "'SummaryName\\(MismatchedEntitySummaryForJSONFormatTest_WrongName\\)"
+      "'");
 }
 
 // ============================================================================
@@ -1968,377 +435,4 @@ TEST_F(JSONFormatTUSummaryTest, WriteEntitySummaryNoFormatInfo) {
               "'SummaryName(UnregisteredEntitySummaryForJSONFormatTest)'"))));
 }
 
-// ============================================================================
-// Round-Trip Tests - Serialization Verification
-// ============================================================================
-
-TEST_P(TUSummaryTest, RoundTripEmpty) {
-  readWriteCompare(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": [],
-    "data": []
-  })");
-}
-
-TEST_P(TUSummaryTest, RoundTripWithTwoSummaryTypes) {
-  readWriteCompare(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [
-      {
-        "id": 3,
-        "name": {
-          "usr": "c:@F at qux",
-          "suffix": "",
-          "namespace": [
-            {
-              "kind": "CompilationUnit",
-              "name": "test.cpp"
-            }
-          ]
-        }
-      },
-      {
-        "id": 1,
-        "name": {
-          "usr": "c:@F at bar",
-          "suffix": "",
-          "namespace": [
-            {
-              "kind": "CompilationUnit",
-              "name": "test.cpp"
-            }
-          ]
-        }
-      },
-      {
-        "id": 4,
-        "name": {
-          "usr": "c:@F at quux",
-          "suffix": "",
-          "namespace": [
-            {
-              "kind": "CompilationUnit",
-              "name": "test.cpp"
-            }
-          ]
-        }
-      },
-      {
-        "id": 0,
-        "name": {
-          "usr": "c:@F at foo",
-          "suffix": "",
-          "namespace": [
-            {
-              "kind": "CompilationUnit",
-              "name": "test.cpp"
-            }
-          ]
-        }
-      },
-      {
-        "id": 2,
-        "name": {
-          "usr": "c:@F at baz",
-          "suffix": "",
-          "namespace": [
-            {
-              "kind": "CompilationUnit",
-              "name": "test.cpp"
-            }
-          ]
-        }
-      }
-    ],
-    "linkage_table": [
-      {
-        "id": 3,
-        "linkage": { "type": "Internal" }
-      },
-      {
-        "id": 1,
-        "linkage": { "type": "None" }
-      },
-      {
-        "id": 4,
-        "linkage": { "type": "External" }
-      },
-      {
-        "id": 0,
-        "linkage": { "type": "None" }
-      },
-      {
-        "id": 2,
-        "linkage": { "type": "Internal" }
-      }
-    ],
-    "data": [
-      {
-        "summary_name": "TagsEntitySummaryForJSONFormatTest",
-        "summary_data": [
-          {
-            "entity_id": 4,
-            "entity_summary": { "tags": ["exported", "hot"] }
-          },
-          {
-            "entity_id": 1,
-            "entity_summary": { "tags": ["internal-only"] }
-          },
-          {
-            "entity_id": 3,
-            "entity_summary": { "tags": ["internal-only"] }
-          },
-          {
-            "entity_id": 0,
-            "entity_summary": { "tags": ["entry-point"] }
-          },
-          {
-            "entity_id": 2,
-            "entity_summary": { "tags": [] }
-          }
-        ]
-      },
-      {
-        "summary_name": "PairsEntitySummaryForJSONFormatTest",
-        "summary_data": [
-          {
-            "entity_id": 1,
-            "entity_summary": {
-              "pairs": [
-                { "first": {"@": 1}, "second": {"@": 3} }
-              ]
-            }
-          },
-          {
-            "entity_id": 4,
-            "entity_summary": {
-              "pairs": [
-                { "first": {"@": 4}, "second": {"@": 0} },
-                { "first": {"@": 4}, "second": {"@": 2} }
-              ]
-            }
-          },
-          {
-            "entity_id": 0,
-            "entity_summary": {
-              "pairs": []
-            }
-          },
-          {
-            "entity_id": 3,
-            "entity_summary": {
-              "pairs": [
-                { "first": {"@": 3}, "second": {"@": 1} }
-              ]
-            }
-          },
-          {
-            "entity_id": 2,
-            "entity_summary": {
-              "pairs": [
-                { "first": {"@": 2}, "second": {"@": 4} },
-                { "first": {"@": 2}, "second": {"@": 3} }
-              ]
-            }
-          }
-        ]
-      }
-    ]
-  })");
-}
-
-TEST_P(TUSummaryTest, RoundTripLinkUnit) {
-  readWriteCompare(R"({
-    "tu_namespace": {
-      "kind": "LinkUnit",
-      "name": "libtest.so"
-    },
-    "id_table": [],
-    "linkage_table": [],
-    "data": []
-  })");
-}
-
-TEST_P(TUSummaryTest, RoundTripWithEmptyDataEntry) {
-  readWriteCompare(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [],
-    "linkage_table": [],
-    "data": [
-      {
-        "summary_name": "PairsEntitySummaryForJSONFormatTest",
-        "summary_data": []
-      }
-    ]
-  })");
-}
-
-TEST_P(TUSummaryTest, RoundTripLinkageTableWithNoneLinkage) {
-  readWriteCompare(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [
-      {
-        "id": 0,
-        "name": {
-          "usr": "c:@F at foo",
-          "suffix": "",
-          "namespace": [
-            {
-              "kind": "CompilationUnit",
-              "name": "test.cpp"
-            }
-          ]
-        }
-      }
-    ],
-    "linkage_table": [
-      {
-        "id": 0,
-        "linkage": { "type": "None" }
-      }
-    ],
-    "data": []
-  })");
-}
-
-TEST_P(TUSummaryTest, RoundTripLinkageTableWithInternalLinkage) {
-  readWriteCompare(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [
-      {
-        "id": 0,
-        "name": {
-          "usr": "c:@F at bar",
-          "suffix": "",
-          "namespace": [
-            {
-              "kind": "CompilationUnit",
-              "name": "test.cpp"
-            }
-          ]
-        }
-      }
-    ],
-    "linkage_table": [
-      {
-        "id": 0,
-        "linkage": { "type": "Internal" }
-      }
-    ],
-    "data": []
-  })");
-}
-
-TEST_P(TUSummaryTest, RoundTripLinkageTableWithExternalLinkage) {
-  readWriteCompare(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [
-      {
-        "id": 0,
-        "name": {
-          "usr": "c:@F at baz",
-          "suffix": "",
-          "namespace": [
-            {
-              "kind": "CompilationUnit",
-              "name": "test.cpp"
-            }
-          ]
-        }
-      }
-    ],
-    "linkage_table": [
-      {
-        "id": 0,
-        "linkage": { "type": "External" }
-      }
-    ],
-    "data": []
-  })");
-}
-
-TEST_P(TUSummaryTest, RoundTripLinkageTableWithMultipleEntries) {
-  readWriteCompare(R"({
-    "tu_namespace": {
-      "kind": "CompilationUnit",
-      "name": "test.cpp"
-    },
-    "id_table": [
-      {
-        "id": 0,
-        "name": {
-          "usr": "c:@F at foo",
-          "suffix": "",
-          "namespace": [
-            {
-              "kind": "CompilationUnit",
-              "name": "test.cpp"
-            }
-          ]
-        }
-      },
-      {
-        "id": 1,
-        "name": {
-          "usr": "c:@F at bar",
-          "suffix": "",
-          "namespace": [
-            {
-              "kind": "CompilationUnit",
-              "name": "test.cpp"
-            }
-          ]
-        }
-      },
-      {
-        "id": 2,
-        "name": {
-          "usr": "c:@F at baz",
-          "suffix": "",
-          "namespace": [
-            {
-              "kind": "CompilationUnit",
-              "name": "test.cpp"
-            }
-          ]
-        }
-      }
-    ],
-    "linkage_table": [
-      {
-        "id": 0,
-        "linkage": { "type": "None" }
-      },
-      {
-        "id": 1,
-        "linkage": { "type": "Internal" }
-      },
-      {
-        "id": 2,
-        "linkage": { "type": "External" }
-      }
-    ],
-    "data": []
-  })");
-}
-
 } // anonymous namespace



More information about the cfe-commits mailing list