diff --git a/Makefile.am b/Makefile.am
index e79d023..48177e9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3,14 +3,16 @@ AM_MAKEFLAGS = --no-print-directory
 
 GIT_SHA1 ?= `git --work-tree=$(top_srcdir) --git-dir=$(top_srcdir)/.git describe --always --long --dirty 2>/dev/null || echo unknown`
 
+libpdbg_tests = libpdbg_target_test
+
 bin_PROGRAMS = pdbg
-check_PROGRAMS = optcmd_test
+check_PROGRAMS = $(libpdbg_tests) optcmd_test
 
 PDBG_TESTS = \
 	tests/test_selection.sh 	\
 	tests/test_hw_bmc.sh
 
-TESTS = optcmd_test $(PDBG_TESTS)
+TESTS = $(libpdbg_tests) optcmd_test $(PDBG_TESTS)
 
 TEST_EXTENSIONS = .sh
 SH_LOG_DRIVER = $(SHELL) $(srcdir)/tests/run_test.sh
@@ -146,6 +148,17 @@ libccan_a_SOURCES = \
 	ccan/str/str.c \
 	ccan/str/str.h
 
+libpdbg_test_cflags = -I$(top_srcdir)/libpdbg -I$(top_srcdir)/libfdt
+libpdbg_test_ldflags = -Wl,--whole-archive,-lpdbg,--no-whole-archive
+libpdbg_test_ldadd = -L.libs libpdbg.la libfdt.la
+
+libpdbg_target_test_SOURCES = src/tests/libpdbg_target_test.c
+libpdbg_target_test_CFLAGS = $(libpdbg_test_cflags)
+libpdbg_target_test_LDFLAGS = $(libpdbg_test_ldflags)
+libpdbg_target_test_LDADD = fake.dtb.o $(libpdbg_test_ldadd)
+
+src/tests/libpdbg_target_test.c: fake.dt.h
+
 M4_V = $(M4_V_$(V))
 M4_V_ = $(M4_V_$(AM_DEFAULT_VERBOSITY))
 M4_V_0 = @echo "  M4      " $@;
diff --git a/src/tests/libpdbg_target_test.c b/src/tests/libpdbg_target_test.c
new file mode 100644
index 0000000..6b64326
--- /dev/null
+++ b/src/tests/libpdbg_target_test.c
@@ -0,0 +1,256 @@
+/* Copyright 2018 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <assert.h>
+
+#include <libpdbg.h>
+
+#include "fake.dt.h"
+
+static int count_target(struct pdbg_target *parent, const char *classname)
+{
+	struct pdbg_target *target;
+	int n = 0;
+
+	pdbg_for_each_target(classname, parent, target) {
+		n++;
+	}
+
+	return n;
+}
+
+static int count_class_target(const char *classname)
+{
+	struct pdbg_target *target;
+	int n = 0;
+
+	pdbg_for_each_class_target(classname, target) {
+		n++;
+	}
+
+	return n;
+}
+
+static int count_child_target(struct pdbg_target *parent)
+{
+	struct pdbg_target *child;
+	int n = 0;
+
+	pdbg_for_each_child_target(parent, child) {
+		n++;
+	}
+
+	return n;
+}
+
+int main(void)
+{
+	struct pdbg_target *root, *target, *parent, *parent2;
+	const char *name;
+	int count;
+
+	pdbg_targets_init(&_binary_fake_dtb_o_start);
+
+	root = pdbg_target_root();
+	assert(root);
+
+	count = count_class_target("fsi");
+	assert(count == 1);
+
+	count = count_class_target("pib");
+	assert(count == 8);
+
+	count = count_class_target("core");
+	assert(count == 32);
+
+	count = count_class_target("thread");
+	assert(count == 64);
+
+	pdbg_for_each_class_target("fsi", target) {
+		parent = pdbg_target_parent("fsi", target);
+		assert(parent == NULL);
+
+		parent = pdbg_target_parent("pib", target);
+		assert(parent == NULL);
+
+		parent = pdbg_target_parent("core", target);
+		assert(parent == NULL);
+
+		parent = pdbg_target_parent("thread", target);
+		assert(parent == NULL);
+
+		count = count_child_target(target);
+		assert(count == 8);
+
+		count = count_target(target, "fsi");
+		assert(count == 1);
+
+		count = count_target(target, "pib");
+		assert(count == 8);
+
+		count = count_target(target, "core");
+		assert(count == 32);
+
+		count = count_target(target, "thread");
+		assert(count == 64);
+
+		name = pdbg_target_name(target);
+		assert(!strcmp(name, "Fake FSI"));
+
+		name = pdbg_target_class_name(target);
+		assert(!strcmp(name, "fsi"));
+
+		name = pdbg_target_dn_name(target);
+		assert(!strncmp(name, "fsi", 3));
+	}
+
+	pdbg_for_each_class_target("pib", target) {
+		parent = pdbg_target_parent("fsi", target);
+		assert(parent);
+
+		parent2 = pdbg_target_require_parent("fsi", target);
+		assert(parent == parent2);
+
+		parent = pdbg_target_parent("pib", target);
+		assert(parent == NULL);
+
+		parent = pdbg_target_parent("core", target);
+		assert(parent == NULL);
+
+		parent = pdbg_target_parent("thread", target);
+		assert(parent == NULL);
+
+		count = count_child_target(target);
+		assert(count == 4);
+
+		count = count_target(target, "fsi");
+		assert(count == 0);
+
+		count = count_target(target, "pib");
+		assert(count == 1);
+
+		count = count_target(target, "core");
+		assert(count == 4);
+
+		count = count_target(target, "thread");
+		assert(count == 8);
+
+		name = pdbg_target_name(target);
+		assert(!strcmp(name, "Fake PIB"));
+
+		name = pdbg_target_class_name(target);
+		assert(!strcmp(name, "pib"));
+
+		name = pdbg_target_dn_name(target);
+		assert(!strncmp(name, "pib", 3));
+	}
+
+	pdbg_for_each_class_target("core", target) {
+		parent = pdbg_target_parent("fsi", target);
+		assert(parent);
+
+		parent2 = pdbg_target_require_parent("fsi", target);
+		assert(parent == parent2);
+
+		parent = pdbg_target_parent("pib", target);
+		assert(parent);
+
+		parent2 = pdbg_target_require_parent("pib", target);
+		assert(parent == parent2);
+
+		parent = pdbg_target_parent("core", target);
+		assert(parent == NULL);
+
+		parent = pdbg_target_parent("thread", target);
+		assert(parent == NULL);
+
+		count = count_child_target(target);
+		assert(count == 2);
+
+		count = count_target(target, "fsi");
+		assert(count == 0);
+
+		count = count_target(target, "pib");
+		assert(count == 0);
+
+		count = count_target(target, "core");
+		assert(count == 1);
+
+		count = count_target(target, "thread");
+		assert(count == 2);
+
+		name = pdbg_target_name(target);
+		assert(!strcmp(name, "Fake Core"));
+
+		name = pdbg_target_class_name(target);
+		assert(!strcmp(name, "core"));
+
+		name = pdbg_target_dn_name(target);
+		assert(!strncmp(name, "core", 4));
+	}
+
+	pdbg_for_each_class_target("thread", target) {
+		parent = pdbg_target_parent("fsi", target);
+		assert(parent);
+
+		parent2 = pdbg_target_require_parent("fsi", target);
+		assert(parent == parent2);
+
+		parent = pdbg_target_parent("pib", target);
+		assert(parent);
+
+		parent2 = pdbg_target_require_parent("pib", target);
+		assert(parent == parent2);
+
+		parent = pdbg_target_parent("core", target);
+		assert(parent);
+
+		parent2 = pdbg_target_require_parent("core", target);
+		assert(parent == parent2);
+
+		parent = pdbg_target_parent("thread", target);
+		assert(parent == NULL);
+
+		count = count_child_target(target);
+		assert(count == 0);
+
+		count = count_target(target, "fsi");
+		assert(count == 0);
+
+		count = count_target(target, "pib");
+		assert(count == 0);
+
+		count = count_target(target, "core");
+		assert(count == 0);
+
+		count = count_target(target, "thread");
+		assert(count == 1);
+
+		name = pdbg_target_name(target);
+		assert(!strcmp(name, "Fake Thread"));
+
+		name = pdbg_target_class_name(target);
+		assert(!strcmp(name, "thread"));
+
+		name = pdbg_target_dn_name(target);
+		assert(!strncmp(name, "thread", 6));
+	}
+
+	return 0;
+}
