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