diff --git a/zircon/system/ulib/trace-reader/BUILD.gn b/zircon/system/ulib/trace-reader/BUILD.gn
index 20dfd5d0a726ef43d4ac378e87b162d0a3f54965..1a1bdbe336c8073230a078026a7691921404c166 100644
--- a/zircon/system/ulib/trace-reader/BUILD.gn
+++ b/zircon/system/ulib/trace-reader/BUILD.gn
@@ -27,3 +27,30 @@ library("trace-reader") {
     "$zx/system/ulib/fbl",
   ]
 }
+
+test("trace-reader-test") {
+  sources = [
+    "reader_tests.cpp",
+    "records_tests.cpp",
+  ]
+  deps = [
+    "$zx/system/ulib/fbl",
+    "$zx/system/ulib/trace-engine",
+    "$zx/system/ulib/trace-reader",
+    "$zx/system/ulib/zxtest",
+  ]
+  if (is_fuchsia) {
+    deps += [
+      "$zx/system/ulib/fdio",
+      "$zx/system/ulib/zircon",
+      "$zx/system/ulib/zx",
+    ]
+  }
+}
+
+group("tests") {
+  testonly = true
+  deps = [
+    ":trace-reader-test",
+  ]
+}
diff --git a/zircon/system/utest/trace-reader/reader_tests.cpp b/zircon/system/ulib/trace-reader/reader_tests.cpp
similarity index 91%
rename from zircon/system/utest/trace-reader/reader_tests.cpp
rename to zircon/system/ulib/trace-reader/reader_tests.cpp
index d18af5df233086382e81d709806afd37c88189cf..f9af88b3728498e401c4297f015e0f5b2ffc4fd1 100644
--- a/zircon/system/utest/trace-reader/reader_tests.cpp
+++ b/zircon/system/ulib/trace-reader/reader_tests.cpp
@@ -8,10 +8,11 @@
 
 #include <fbl/algorithm.h>
 #include <fbl/vector.h>
-#include <unittest/unittest.h>
+#include <zxtest/zxtest.h>
 
 #include <utility>
 
+namespace trace {
 namespace {
 
 template <typename T>
@@ -32,9 +33,7 @@ trace::TraceReader::ErrorHandler MakeErrorHandler(fbl::String* out_error) {
     };
 }
 
-bool empty_chunk_test() {
-    BEGIN_TEST;
-
+TEST(TraceReader, EmptyChunk) {
     uint64_t value;
     int64_t int64_value;
     double double_value;
@@ -57,13 +56,9 @@ bool empty_chunk_test() {
     EXPECT_TRUE(empty.ReadChunk(0u, &subchunk));
     EXPECT_EQ(0u, subchunk.remaining_words());
     EXPECT_FALSE(empty.ReadChunk(1u, &subchunk));
-
-    END_TEST;
 }
 
-bool non_empty_chunk_test() {
-    BEGIN_TEST;
-
+TEST(TraceReader, NonEmptyChunk) {
     uint64_t value;
     int64_t int64_value;
     double double_value;
@@ -145,13 +140,9 @@ bool non_empty_chunk_test() {
 
     EXPECT_FALSE(subchunk.ReadUint64(&value));
     EXPECT_FALSE(chunk.ReadUint64(&value));
-
-    END_TEST;
 }
 
-bool initial_state_test() {
-    BEGIN_TEST;
-
+TEST(TraceReader, InitialState) {
     fbl::Vector<trace::Record> records;
     fbl::String error;
     trace::TraceReader reader(MakeRecordConsumer(&records), MakeErrorHandler(&error));
@@ -161,13 +152,9 @@ bool initial_state_test() {
     EXPECT_TRUE(reader.GetProviderName(0) == "");
     EXPECT_EQ(0, records.size());
     EXPECT_TRUE(error.empty());
-
-    END_TEST;
 }
 
-bool empty_buffer_test() {
-    BEGIN_TEST;
-
+TEST(TraceReader, EmptyBuffer) {
     fbl::Vector<trace::Record> records;
     fbl::String error;
     trace::TraceReader reader(MakeRecordConsumer(&records), MakeErrorHandler(&error));
@@ -176,17 +163,9 @@ bool empty_buffer_test() {
     EXPECT_TRUE(reader.ReadRecords(empty));
     EXPECT_EQ(0, records.size());
     EXPECT_TRUE(error.empty());
-
-    END_TEST;
 }
 
 // NOTE: Most of the reader is covered by the libtrace tests.
 
 } // namespace
-
-BEGIN_TEST_CASE(reader_tests)
-RUN_TEST(empty_chunk_test)
-RUN_TEST(non_empty_chunk_test)
-RUN_TEST(initial_state_test)
-RUN_TEST(empty_buffer_test)
-END_TEST_CASE(reader_tests)
+} // namespace trace
diff --git a/zircon/system/utest/trace-reader/records_tests.cpp b/zircon/system/ulib/trace-reader/records_tests.cpp
similarity index 97%
rename from zircon/system/utest/trace-reader/records_tests.cpp
rename to zircon/system/ulib/trace-reader/records_tests.cpp
index 1f8d5aad125482c9ab59af0129047f862a71bbc0..30953c66d355f421c64603b045699ef3703ac12a 100644
--- a/zircon/system/utest/trace-reader/records_tests.cpp
+++ b/zircon/system/ulib/trace-reader/records_tests.cpp
@@ -8,10 +8,11 @@
 
 #include <fbl/algorithm.h>
 #include <fbl/string_printf.h>
-#include <unittest/unittest.h>
+#include <zxtest/zxtest.h>
 
 #include <utility>
 
+namespace trace {
 namespace {
 
 template <typename T>
@@ -19,9 +20,7 @@ uint64_t ToWord(const T& value) {
     return *reinterpret_cast<const uint64_t*>(&value);
 }
 
-bool process_thread_test() {
-    BEGIN_TEST;
-
+TEST(TraceRecords, ProcessThread) {
     trace::ProcessThread pt;
     EXPECT_EQ(ZX_KOID_INVALID, pt.process_koid());
     EXPECT_EQ(ZX_KOID_INVALID, pt.thread_koid());
@@ -63,13 +62,9 @@ bool process_thread_test() {
     EXPECT_FALSE(trace::ProcessThread(1, 2) < trace::ProcessThread());
 
     EXPECT_STR_EQ("1/2", trace::ProcessThread(1, 2).ToString().c_str());
-
-    END_TEST;
 }
 
-bool argument_value_test() {
-    BEGIN_TEST;
-
+TEST(TraceRecords, ArgumentValue) {
     // null
 
     trace::ArgumentValue av = trace::ArgumentValue::MakeNull();
@@ -294,13 +289,9 @@ bool argument_value_test() {
     }
 
     EXPECT_STR_EQ("koid(18446744073709551615)", av.ToString().c_str());
-
-    END_TEST;
 }
 
-bool argument_test() {
-    BEGIN_TEST;
-
+TEST(TraceRecords, Argument) {
     trace::Argument a("name", trace::ArgumentValue::MakeInt32(123));
     EXPECT_TRUE(a.name() == "name");
     EXPECT_EQ(123, a.value().GetInt32());
@@ -318,13 +309,9 @@ bool argument_test() {
     EXPECT_EQ(123, a.value().GetInt32());
 
     EXPECT_STR_EQ("name: int32(123)", a.ToString().c_str());
-
-    END_TEST;
 }
 
-bool metadata_data_test() {
-    BEGIN_TEST;
-
+TEST(TraceRecords, MetadataData) {
     // provider info
 
     {
@@ -363,13 +350,9 @@ bool metadata_data_test() {
 
         EXPECT_STR_EQ("ProviderSection(id: 1)", d.ToString().c_str());
     }
-
-    END_TEST;
 }
 
-bool event_data_test() {
-    BEGIN_TEST;
-
+TEST(TraceRecords, EventData) {
     // instant
 
     {
@@ -411,15 +394,15 @@ bool event_data_test() {
     {
         trace::EventData d(trace::EventData::DurationBegin{});
         EXPECT_EQ(trace::EventType::kDurationBegin, d.type());
-        EXPECT_NONNULL(&d.GetDurationBegin());
+        EXPECT_NOT_NULL(&d.GetDurationBegin());
 
         trace::EventData m(std::move(d));
         EXPECT_EQ(trace::EventType::kDurationBegin, m.type());
-        EXPECT_NONNULL(&m.GetDurationBegin());
+        EXPECT_NOT_NULL(&m.GetDurationBegin());
 
         d = std::move(m);
         EXPECT_EQ(trace::EventType::kDurationBegin, d.type());
-        EXPECT_NONNULL(&d.GetDurationBegin());
+        EXPECT_NOT_NULL(&d.GetDurationBegin());
 
         EXPECT_STR_EQ("DurationBegin", d.ToString().c_str());
     }
@@ -429,15 +412,15 @@ bool event_data_test() {
     {
         trace::EventData d(trace::EventData::DurationEnd{});
         EXPECT_EQ(trace::EventType::kDurationEnd, d.type());
-        EXPECT_NONNULL(&d.GetDurationEnd());
+        EXPECT_NOT_NULL(&d.GetDurationEnd());
 
         trace::EventData m(std::move(d));
         EXPECT_EQ(trace::EventType::kDurationEnd, m.type());
-        EXPECT_NONNULL(&m.GetDurationEnd());
+        EXPECT_NOT_NULL(&m.GetDurationEnd());
 
         d = std::move(m);
         EXPECT_EQ(trace::EventType::kDurationEnd, d.type());
-        EXPECT_NONNULL(&d.GetDurationEnd());
+        EXPECT_NOT_NULL(&d.GetDurationEnd());
 
         EXPECT_STR_EQ("DurationEnd", d.ToString().c_str());
     }
@@ -567,13 +550,9 @@ bool event_data_test() {
 
         EXPECT_STR_EQ("FlowEnd(id: 123)", d.ToString().c_str());
     }
-
-    END_TEST;
 }
 
-bool record_test() {
-    BEGIN_TEST;
-
+TEST(TraceRecords, Record) {
     // metadata
 
     {
@@ -866,17 +845,7 @@ bool record_test() {
 
         EXPECT_STR_EQ("Log(ts: 123, pt: 4/5, \"log message\")", r.ToString().c_str());
     }
-
-    END_TEST;
 }
 
 } // namespace
-
-BEGIN_TEST_CASE(types_tests)
-RUN_TEST(process_thread_test)
-RUN_TEST(argument_value_test)
-RUN_TEST(argument_test)
-RUN_TEST(metadata_data_test)
-RUN_TEST(event_data_test)
-RUN_TEST(record_test)
-END_TEST_CASE(types_tests)
+} // namespace trace
diff --git a/zircon/system/utest/BUILD.gn b/zircon/system/utest/BUILD.gn
index 76ff7af09c6a3aaac43bfa8b117627db5d126ff3..ecda379256ba5e232ab243586b6505626d411285 100644
--- a/zircon/system/utest/BUILD.gn
+++ b/zircon/system/utest/BUILD.gn
@@ -74,6 +74,7 @@ if (current_cpu != "") {
       "$zx/system/ulib/simplehid/test",
       "$zx/system/ulib/storage-metrics/test",
       "$zx/system/ulib/tftp:tftp-test",
+      "$zx/system/ulib/trace-reader:tests",
       "$zx/system/ulib/trace-vthread:tests",
       "$zx/system/ulib/zbi-bootfs/test:test",
       "$zx/system/ulib/zxtest/test",
@@ -175,7 +176,6 @@ if (current_cpu != "") {
       "time",
       "timers",
       "trace",
-      "trace-reader",
       "usb",
       "usb-virtual-bus",
       "utf_conversion",
@@ -218,6 +218,7 @@ if (current_cpu != "") {
     deps = [
       "$zx/system/ulib/fvm/test",
       "$zx/system/ulib/zxtest/test",
+      "$zx/system/ulib/trace-reader:tests",
       "cmdline",
       "fbl",
       "ffl",
@@ -226,7 +227,6 @@ if (current_cpu != "") {
       "fs-host",
       "fvm-host",
       "runtests-utils",
-      "trace-reader",
       "util",
       "zbi",
     ]
diff --git a/zircon/system/utest/trace-reader/BUILD.gn b/zircon/system/utest/trace-reader/BUILD.gn
deleted file mode 100644
index f2b3be0dc4b6eed928748990c60624dcd14fe543..0000000000000000000000000000000000000000
--- a/zircon/system/utest/trace-reader/BUILD.gn
+++ /dev/null
@@ -1,23 +0,0 @@
-# Copyright 2019 The Fuchsia Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-test("trace-reader") {
-  sources = [
-    "reader_tests.cpp",
-    "records_tests.cpp",
-  ]
-  deps = [
-    "$zx/system/ulib/fbl",
-    "$zx/system/ulib/trace-engine",
-    "$zx/system/ulib/trace-reader",
-    "$zx/system/ulib/unittest",
-  ]
-  if (is_fuchsia) {
-    deps += [
-      "$zx/system/ulib/fdio",
-      "$zx/system/ulib/zircon",
-      "$zx/system/ulib/zx",
-    ]
-  }
-}
diff --git a/zircon/system/utest/trace-reader/OWNERS b/zircon/system/utest/trace-reader/OWNERS
deleted file mode 100644
index 5669548895b88878c1f2025281a376ca4c78f729..0000000000000000000000000000000000000000
--- a/zircon/system/utest/trace-reader/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-dje@google.com
-fmeawad@google.com
-jeffbrown@google.com
-*