1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <assert.h>
6#include <stdio.h>
7#include <unistd.h>
8
9#include <lib/fzl/time.h>
10#include <lib/fzl/fifo.h>
11#include <fbl/algorithm.h>
12#include <unittest/unittest.h>
13#include <zircon/syscalls.h>
14
15namespace {
16
17template <typename T>
18bool AlmostEqual(T t0, T t1, T e) {
19    BEGIN_HELPER;
20
21    char buf[128];
22    snprintf(buf, sizeof(buf), "%zu != %zu (within error of %zu)", t0, t1, e);
23    ASSERT_TRUE(fbl::min(t0, t1) + e >= fbl::max(t0, t1), buf);
24
25    END_HELPER;
26}
27
28bool TickConverter(zx::ticks ticks, zx::ticks err) {
29    BEGIN_HELPER;
30    ASSERT_TRUE(AlmostEqual(ticks.get(), fzl::NsToTicks(fzl::TicksToNs(ticks)).get(), err.get()));
31    ASSERT_TRUE(AlmostEqual(ticks.get(), ns_to_ticks(ticks_to_ns(ticks.get())), err.get()));
32    END_HELPER;
33}
34
35bool NsConverter(zx::duration ns, zx::duration err) {
36    BEGIN_HELPER;
37    ASSERT_TRUE(AlmostEqual(ns.get(), fzl::TicksToNs(fzl::NsToTicks(ns)).get(), err.get()));
38    ASSERT_TRUE(AlmostEqual(ns.get(), ticks_to_ns(ns_to_ticks(ns.get())), err.get()));
39    END_HELPER;
40}
41
42bool TimeTest() {
43    BEGIN_TEST;
44
45    zx::ticks tps = zx::ticks::per_second();
46    zx::duration nps = zx::sec(1);
47
48    // The following tests check converting from:
49    //  - ticks --> nanoseconds --> ticks
50    //  - nanoseconds --> ticks --> nanoseconds
51    //
52    // This conversion is inherently lossy if the number of ticks/ns (or
53    // ns/tick) is not an exact integer -- which is almost always the case.
54    //
55    // To convert N nanoseconds to ticks, we'd logically multiply by
56    // "ticks/sec" / "ns/second". However, by converting N into the ticks
57    // equivalent T, we may be losing the fractional component of this number: N
58    // may actually be represented by T +/- a partial tick.
59    //
60    // In most situations, where ticks are higher precision than nanoseconds,
61    // there will actually be even more loss in the other direction: when
62    // converting from ticks to nanoseconds, we may potentially lose as many as
63    // "ticks/second / ns/second" ticks.
64    //
65    // To ensure our error margins account for this loss, where we lose
66    // minimally a "partial unit" and maximally an integer ratio of the units,
67    // we calculate acceptable loss as:
68    //
69    // loss = max(1 + ratio, 1)
70    //
71    // Where we add one to the ratio to "round up to the nearest integer ratio" while
72    // doing the conversion.
73    zx::ticks tick_loss = fbl::max(zx::ticks(1 + (tps.get() / nps.get())),
74                                   zx::ticks(1));
75    zx::duration duration_loss = fbl::max(zx::duration(1 + (nps.get() / tps.get())),
76                                          zx::duration(1));
77
78    ASSERT_TRUE(TickConverter(zx::ticks(0), zx::ticks(0)));
79    ASSERT_TRUE(TickConverter(zx::ticks(50), tick_loss));
80    ASSERT_TRUE(TickConverter(zx::ticks(100), tick_loss));
81    ASSERT_TRUE(TickConverter(zx::ticks(100000), tick_loss));
82    ASSERT_TRUE(TickConverter(zx::ticks(1000000000), tick_loss));
83    ASSERT_TRUE(TickConverter(zx::ticks(10000000000000), tick_loss));
84
85    ASSERT_TRUE(NsConverter(zx::duration(0), zx::duration(0)));
86    ASSERT_TRUE(NsConverter(zx::duration(50), duration_loss));
87    ASSERT_TRUE(NsConverter(zx::duration(100), duration_loss));
88    ASSERT_TRUE(NsConverter(zx::duration(100000), duration_loss));
89    ASSERT_TRUE(NsConverter(zx::duration(1000000000), duration_loss));
90    ASSERT_TRUE(NsConverter(zx::duration(10000000000000), duration_loss));
91
92    END_TEST;
93}
94
95bool FifoTest() {
96    BEGIN_TEST;
97
98    // Default constructor
99    {
100        fzl::fifo<int> invalid;
101        ASSERT_EQ(invalid.get_handle(), ZX_HANDLE_INVALID);
102    }
103
104    // Move constructors, reset() and release()
105    {
106        zx::fifo zx_fifo_0, zx_fifo_1;
107        zx_status_t status = zx::fifo::create(4, 4, 0, &zx_fifo_0, &zx_fifo_1);
108        ASSERT_EQ(status, ZX_OK);
109        zx_handle_t handle_0 = zx_fifo_0.get();
110        ASSERT_NE(handle_0, ZX_HANDLE_INVALID);
111
112        fzl::fifo<int> moved_fifo(fbl::move(zx_fifo_0));
113        ASSERT_EQ(moved_fifo.get_handle(), handle_0);
114        ASSERT_EQ(zx_fifo_0.get(), ZX_HANDLE_INVALID);
115
116        fzl::fifo<int> moved_again(fbl::move(moved_fifo));
117        ASSERT_EQ(moved_again.get_handle(), handle_0);
118        ASSERT_EQ(moved_fifo.get_handle(), ZX_HANDLE_INVALID);
119
120        zx::handle opaque_handle(moved_again.release());
121        fzl::fifo<int> from_opaque(fbl::move(opaque_handle));
122        ASSERT_EQ(from_opaque.get_handle(), handle_0);
123        ASSERT_EQ(opaque_handle.get(), ZX_HANDLE_INVALID);
124
125        from_opaque.reset();
126        ASSERT_EQ(from_opaque.get_handle(), ZX_HANDLE_INVALID);
127    }
128
129    // Create, read, write
130
131    fzl::fifo<int64_t, char[8]> fifo_0;
132    fzl::fifo<char[8], int64_t> fifo_1;
133
134    {
135        zx_status_t status = fzl::create_fifo(4, 0, &fifo_0, &fifo_1);
136        ASSERT_EQ(status, ZX_OK);
137    }
138
139    {
140        const int64_t numbers[2] = {10, -20};
141        size_t actual = 0;
142        zx_status_t status = fifo_0.write(numbers, 2, &actual);
143        ASSERT_EQ(status, ZX_OK);
144        ASSERT_EQ(actual, 2);
145    }
146
147    {
148        int64_t numbers[3] = { 0, 0, 0 };
149        size_t actual = 0;
150        zx_status_t status = fifo_1.read(numbers, 3, &actual);
151        ASSERT_EQ(status, ZX_OK);
152        ASSERT_EQ(actual, 2);
153        ASSERT_EQ(numbers[0], 10);
154        ASSERT_EQ(numbers[1], -20);
155    }
156
157    {
158        char str[8] = "hi fifo";
159        zx_status_t status = fifo_1.write_one(str);
160        ASSERT_EQ(status, ZX_OK);
161    }
162
163    {
164        char str[8] = ".......";
165        zx_status_t status = fifo_0.read_one(&str);
166        ASSERT_EQ(status, ZX_OK);
167        ASSERT_STR_EQ("hi fifo", str);
168    }
169
170    // Signal & wait_one
171    {
172        fifo_0.signal(0, ZX_USER_SIGNAL_0);
173        zx_signals_t pending = 0;
174        fifo_0.wait_one(ZX_USER_SIGNAL_0, zx::deadline_after(zx::sec(1)), &pending);
175        ASSERT_TRUE(pending & ZX_USER_SIGNAL_0);
176    }
177
178    // Replace
179    {
180        fzl::fifo<int64_t, char[8]> replaced;
181        fifo_0.replace(0, &replaced);
182        ASSERT_EQ(fifo_0.get_handle(), ZX_HANDLE_INVALID);
183        ASSERT_NE(replaced.get_handle(), ZX_HANDLE_INVALID);
184    }
185
186    END_TEST;
187}
188
189} // namespace
190
191BEGIN_TEST_CASE(libfzl_tests)
192RUN_TEST(TimeTest)
193RUN_TEST(FifoTest)
194END_TEST_CASE(libfzl_tests)
195