[async-testutils] Allow giving a seed in TestLoop's constructor

This allows passing the TEST_LOOP_RANDOM_SEED through other means if the
environment is not usable.

Test: async-testutils-test
Change-Id: I51396388a232546dd1cdea583df2f944390af870
diff --git a/system/ulib/async-testutils/include/lib/async-testutils/test_loop.h b/system/ulib/async-testutils/include/lib/async-testutils/test_loop.h
index 400168b..e10acba 100644
--- a/system/ulib/async-testutils/include/lib/async-testutils/test_loop.h
+++ b/system/ulib/async-testutils/include/lib/async-testutils/test_loop.h
@@ -19,11 +19,15 @@
     virtual async_dispatcher_t* dispatcher() = 0;
 };
 
-
 // A message loop with a fake clock, to be controlled within a test setting.
 class TestLoop {
 public:
+    // Constructs a TestLoop with a seed from the environment, or a random
+    // seed if absent.
     TestLoop();
+    // If state is nonzero, constructs a TestLoop with the given seed.
+    // Otherwise, uses a seed from the environment or a random seed.
+    TestLoop(uint32_t state);
     ~TestLoop();
 
     // Returns the test loop's asynchronous dispatcher.
diff --git a/system/ulib/async-testutils/test_loop.cpp b/system/ulib/async-testutils/test_loop.cpp
index 343086d..4cd32cb 100644
--- a/system/ulib/async-testutils/test_loop.cpp
+++ b/system/ulib/async-testutils/test_loop.cpp
@@ -43,7 +43,6 @@
         zx_cprng_draw(&random_seed, sizeof(uint32_t));
     }
 
-    printf("\nTEST_LOOP_RANDOM_SEED=\"%u\"\n", random_seed);
     return random_seed;
 }
 
@@ -147,10 +146,15 @@
     TestLoopDispatcher* dispatcher_;
 };
 
-TestLoop::TestLoop()
-    : time_keeper_(new TestLoopTimeKeeper()), initial_state_(GetRandomSeed()), state_(initial_state_) {
+TestLoop::TestLoop() : TestLoop(0) {}
+
+TestLoop::TestLoop(uint32_t state)
+    : time_keeper_(new TestLoopTimeKeeper()),
+      initial_state_((state != 0)? state : GetRandomSeed()), state_(initial_state_) {
     dispatchers_.push_back(fbl::make_unique<TestLoopDispatcher>(time_keeper_.get()));
     async_set_default_dispatcher(dispatchers_[0].get());
+
+    printf("\nTEST_LOOP_RANDOM_SEED=\"%u\"\n", initial_state_);
 }
 
 TestLoop::~TestLoop() {
diff --git a/system/utest/async-testutils/test_loop_tests.cpp b/system/utest/async-testutils/test_loop_tests.cpp
index e15c41d..e2721a6 100644
--- a/system/utest/async-testutils/test_loop_tests.cpp
+++ b/system/utest/async-testutils/test_loop_tests.cpp
@@ -14,6 +14,7 @@
 #include <unittest/unittest.h>
 #include <zircon/syscalls.h>
 
+#include <memory>
 #include <utility>
 
 namespace {
@@ -525,15 +526,13 @@
 
 
 // Populates |order| with the order in which two tasks and two waits on four
-// loops were dispatched, given a |random_seed|.
-bool DetermineDispatchOrder(const char* random_seed, int (*order)[4]) {
+// loops were dispatched, given a |loop|.
+    bool DetermineDispatchOrder(std::unique_ptr<async::TestLoop> loop, int (*order)[4]) {
     BEGIN_HELPER;
 
-    ASSERT_EQ(0, setenv("TEST_LOOP_RANDOM_SEED", random_seed, 1));
-    async::TestLoop loop;
-    auto loopA = loop.StartNewLoop();
-    auto loopB = loop.StartNewLoop();
-    auto loopC = loop.StartNewLoop();
+    auto loopA = loop->StartNewLoop();
+    auto loopB = loop->StartNewLoop();
+    auto loopC = loop->StartNewLoop();
     async::Wait wait;
     async::Wait waitB;
     zx::event event;
@@ -546,11 +545,11 @@
     InitWait(&waitB, [&] { (*order)[2] = ++i; }, event, ZX_USER_SIGNAL_0);
     async::PostTask(loopC->dispatcher(), [&] { (*order)[3] = ++i; });
 
-    ASSERT_EQ(ZX_OK, wait.Begin(loop.dispatcher()));
+    ASSERT_EQ(ZX_OK, wait.Begin(loop->dispatcher()));
     ASSERT_EQ(ZX_OK, waitB.Begin(loopB->dispatcher()));
     ASSERT_EQ(ZX_OK, event.signal(0u, ZX_USER_SIGNAL_0));
 
-    loop.RunUntilIdle();
+    loop->RunUntilIdle();
 
     EXPECT_EQ(4, i);
     EXPECT_NE(0, (*order)[0]);
@@ -558,24 +557,44 @@
     EXPECT_NE(0, (*order)[2]);
     EXPECT_NE(0, (*order)[3]);
 
-    ASSERT_EQ(0, unsetenv("TEST_LOOP_RANDOM_SEED"));
+    END_HELPER;
+}
+
+bool SeedTestLoopWithEnv(uint32_t random_seed, std::unique_ptr<async::TestLoop>* loop) {
+    BEGIN_HELPER;
+
+    char buf[12];
+    snprintf(buf, sizeof(buf), "%u", random_seed);
+    EXPECT_EQ(0, setenv("TEST_LOOP_RANDOM_SEED", buf, 1));
+    *loop = std::make_unique<async::TestLoop>();
+    EXPECT_EQ(0, unsetenv("TEST_LOOP_RANDOM_SEED"));
 
     END_HELPER;
 }
 
-bool DispatchOrderIsDeterministicFor(const char* random_seed) {
+bool DispatchOrderIsDeterministicFor(uint32_t random_seed) {
     BEGIN_HELPER;
 
     int expected_order[4] = {0, 0, 0, 0};
-    EXPECT_TRUE(DetermineDispatchOrder(random_seed, &expected_order));
+    std::unique_ptr<async::TestLoop> loop;
+
+    EXPECT_TRUE(SeedTestLoopWithEnv(random_seed, &loop));
+    EXPECT_TRUE(DetermineDispatchOrder(std::move(loop), &expected_order));
 
     for (int i = 0; i < 5; ++i) {
-        int actual_order[4] = {0, 0, 0, 0};
-        EXPECT_TRUE(DetermineDispatchOrder(random_seed, &actual_order));
-        EXPECT_EQ(expected_order[0], actual_order[0]);
-        EXPECT_EQ(expected_order[1], actual_order[1]);
-        EXPECT_EQ(expected_order[2], actual_order[2]);
-        EXPECT_EQ(expected_order[3], actual_order[3]);
+        for (int j = 0; j < 2; j++) {
+            int actual_order[4] = {0, 0, 0, 0};
+            if (j == 0) {
+                EXPECT_TRUE(SeedTestLoopWithEnv(random_seed, &loop));
+            } else {
+                loop = std::make_unique<async::TestLoop>(random_seed);
+            }
+            EXPECT_TRUE(DetermineDispatchOrder(std::move(loop), &actual_order));
+            EXPECT_EQ(expected_order[0], actual_order[0]);
+            EXPECT_EQ(expected_order[1], actual_order[1]);
+            EXPECT_EQ(expected_order[2], actual_order[2]);
+            EXPECT_EQ(expected_order[3], actual_order[3]);
+        }
     }
 
     END_HELPER;
@@ -585,15 +604,15 @@
 bool DispatchOrderIsDeterministic() {
     BEGIN_TEST;
 
-    EXPECT_TRUE(DispatchOrderIsDeterministicFor("1"));
-    EXPECT_TRUE(DispatchOrderIsDeterministicFor("43"));
-    EXPECT_TRUE(DispatchOrderIsDeterministicFor("893"));
-    EXPECT_TRUE(DispatchOrderIsDeterministicFor("39408"));
-    EXPECT_TRUE(DispatchOrderIsDeterministicFor("844018"));
-    EXPECT_TRUE(DispatchOrderIsDeterministicFor("83018299"));
-    EXPECT_TRUE(DispatchOrderIsDeterministicFor("3213"));
-    EXPECT_TRUE(DispatchOrderIsDeterministicFor("139133113"));
-    EXPECT_TRUE(DispatchOrderIsDeterministicFor("1323234373"));
+    EXPECT_TRUE(DispatchOrderIsDeterministicFor(1));
+    EXPECT_TRUE(DispatchOrderIsDeterministicFor(43));
+    EXPECT_TRUE(DispatchOrderIsDeterministicFor(893));
+    EXPECT_TRUE(DispatchOrderIsDeterministicFor(39408));
+    EXPECT_TRUE(DispatchOrderIsDeterministicFor(844018));
+    EXPECT_TRUE(DispatchOrderIsDeterministicFor(83018299));
+    EXPECT_TRUE(DispatchOrderIsDeterministicFor(3213));
+    EXPECT_TRUE(DispatchOrderIsDeterministicFor(139133113));
+    EXPECT_TRUE(DispatchOrderIsDeterministicFor(1323234373));
 
     END_TEST;
 }