1/*
2 * Copyright (c) 2019 ARM Limited
3 * All rights reserved
4 *
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder.  You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
13 *
14 * Copyright 2018 Google, Inc.
15 *
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions are
18 * met: redistributions of source code must retain the above copyright
19 * notice, this list of conditions and the following disclaimer;
20 * redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in the
22 * documentation and/or other materials provided with the distribution;
23 * neither the name of the copyright holders nor the names of its
24 * contributors may be used to endorse or promote products derived from
25 * this software without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 *
39 * Authors: Gabe Black
40 *          Giacomo Travaglini
41 */
42
43#include <gtest/gtest.h>
44
45#include <initializer_list>
46#include <iostream>
47#include <vector>
48
49#include "base/fiber.hh"
50
51/** This test is checking if the "started" member has its expected
52 * value before and after the fiber runs. In the test an empty fiber
53 * is used since we are just interested on the _started member and
54 * nothing more.
55 */
56TEST(Fiber, Starting)
57{
58    class StartingFiber : public Fiber
59    {
60      public:
61        StartingFiber(Fiber *link) : Fiber(link) {}
62        void main() { /** Do nothing */ }
63    };
64
65    StartingFiber fiber(Fiber::primaryFiber());
66
67    ASSERT_FALSE(fiber.started());
68
69    fiber.run();
70
71    ASSERT_TRUE(fiber.started());
72}
73
74class SwitchingFiber : public Fiber
75{
76  public:
77    const char *name;
78    std::vector<Fiber *> next;
79
80    SwitchingFiber(const char *name, std::initializer_list<Fiber *> l);
81
82    void checkExpected();
83    void main();
84};
85
86extern SwitchingFiber a;
87extern SwitchingFiber b;
88extern SwitchingFiber c;
89
90SwitchingFiber a("A", { &b, &a, Fiber::primaryFiber(), &b, &c });
91SwitchingFiber b("B", { &a, &c });
92SwitchingFiber c("C", { &a, Fiber::primaryFiber(), Fiber::primaryFiber() });
93
94std::vector<SwitchingFiber *>::iterator expectedIt;
95std::vector<SwitchingFiber *> expected({
96    &a, &b, &a, &a, /* main Fiber, */
97    &a, &b, &c, &a, &c,
98    /* main Fiber, */ &c, &c
99});
100
101SwitchingFiber::SwitchingFiber(
102        const char *name, std::initializer_list<Fiber *> l) :
103    name(name), next(l)
104{}
105
106void
107SwitchingFiber::checkExpected()
108{
109    ASSERT_NE(expectedIt, expected.end());
110    SwitchingFiber *e = *expectedIt++;
111    EXPECT_EQ(e, this) << "Expected " << e->name << ", got " << name;
112}
113
114void
115SwitchingFiber::main()
116{
117    checkExpected();
118    for (auto &n : next) {
119        n->run();
120        checkExpected();
121    }
122}
123
124TEST(Fiber, Switching)
125{
126    expectedIt = expected.begin();
127
128    a.run();
129    EXPECT_EQ(expectedIt - expected.begin(), 4);
130
131    a.run();
132    EXPECT_EQ(expectedIt - expected.begin(), 9);
133
134    c.run();
135    EXPECT_EQ(expectedIt - expected.begin(), 10);
136
137    EXPECT_FALSE(a.finished());
138    EXPECT_FALSE(b.finished());
139    EXPECT_FALSE(c.finished());
140
141    c.run();
142    EXPECT_EQ(expected.end(), expectedIt) <<
143        "Didn't exactly use up the expected Fiber sequence";
144
145    EXPECT_TRUE(c.finished());
146}
147
148int currentIndex = 0;
149
150class LinkedFiber : public Fiber
151{
152  public:
153    const int index;
154    LinkedFiber(Fiber *link, int index) : Fiber(link), index(index) {}
155
156    void
157    main()
158    {
159        EXPECT_EQ(currentIndex, index);
160        currentIndex++;
161    }
162};
163
164TEST(Fiber, Linked)
165{
166    currentIndex = 0;
167
168    LinkedFiber lf3(Fiber::primaryFiber(), 3);
169    LinkedFiber lf2(&lf3, 2);
170    LinkedFiber lf1(&lf2, 1);
171    LinkedFiber lf0(&lf1, 0);
172
173    lf0.run();
174
175    EXPECT_EQ(currentIndex, 4);
176}
177