1 /* Copyright 2016, Ableton AG, Berlin. All rights reserved.
2  *
3  *  This program is free software: you can redistribute it and/or modify
4  *  it under the terms of the GNU General Public License as published by
5  *  the Free Software Foundation, either version 2 of the License, or
6  *  (at your option) any later version.
7  *
8  *  This program is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *  GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License
14  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
15  *
16  *  If you would like to incorporate Link into a proprietary software application,
17  *  please contact <link-devs@ableton.com>.
18  */
19 
20 #include <ableton/discovery/PeerGateway.hpp>
21 #include <ableton/test/CatchWrapper.hpp>
22 #include <ableton/test/serial_io/Fixture.hpp>
23 #include <ableton/util/Log.hpp>
24 
25 namespace ableton
26 {
27 namespace discovery
28 {
29 namespace
30 {
31 
32 struct TestNodeState : std::tuple<std::string, double>
33 {
TestNodeStateableton::discovery::__anon12e1acf50111::TestNodeState34   TestNodeState(std::string ident, double tempo)
35     : std::tuple<std::string, double>(std::move(ident), std::move(tempo))
36   {
37   }
38 
identableton::discovery::__anon12e1acf50111::TestNodeState39   std::string ident() const
40   {
41     return std::get<0>(*this);
42   }
43 };
44 
45 struct TestMessenger
46 {
47   template <typename Handler>
receiveableton::discovery::__anon12e1acf50111::TestMessenger48   void receive(Handler handler)
49   {
50     receivePeerState = [handler](const PeerState<TestNodeState>& msg) { handler(msg); };
51 
52     receiveByeBye = [handler](const ByeBye<std::string>& msg) { handler(msg); };
53   }
54 
55   std::function<void(const PeerState<TestNodeState>&)> receivePeerState;
56   std::function<void(const ByeBye<std::string>&)> receiveByeBye;
57 };
58 
59 struct TestObserver
60 {
61   using GatewayObserverNodeState = TestNodeState;
62   using GatewayObserverNodeId = std::string;
63 
sawPeer(TestObserver & observer,const GatewayObserverNodeState & peer)64   friend void sawPeer(TestObserver& observer, const GatewayObserverNodeState& peer)
65   {
66     observer.mPeersSeen.push_back(peer);
67   }
68 
peerLeft(TestObserver & observer,const GatewayObserverNodeId & id)69   friend void peerLeft(TestObserver& observer, const GatewayObserverNodeId& id)
70   {
71     observer.mPeersLeft.push_back(id);
72   }
73 
peerTimedOut(TestObserver & observer,const GatewayObserverNodeId & id)74   friend void peerTimedOut(TestObserver& observer, const GatewayObserverNodeId& id)
75   {
76     observer.mPeersTimedOut.push_back(id);
77   }
78 
79   std::vector<GatewayObserverNodeState> mPeersSeen;
80   std::vector<GatewayObserverNodeId> mPeersLeft;
81   std::vector<GatewayObserverNodeId> mPeersTimedOut;
82 };
83 
expectPeersSeen(std::vector<TestNodeState> expected,const TestObserver & observer)84 void expectPeersSeen(std::vector<TestNodeState> expected, const TestObserver& observer)
85 {
86   CHECK(expected == observer.mPeersSeen);
87 }
88 
expectPeersLeft(std::vector<std::string> expected,const TestObserver & observer)89 void expectPeersLeft(std::vector<std::string> expected, const TestObserver& observer)
90 {
91   CHECK(expected == observer.mPeersLeft);
92 }
93 
expectPeersTimedOut(std::vector<std::string> expected,const TestObserver & observer)94 void expectPeersTimedOut(std::vector<std::string> expected, const TestObserver& observer)
95 {
96   CHECK(expected == observer.mPeersTimedOut);
97 }
98 
99 // Test peers
100 const auto peerA = TestNodeState{"peerA", 120};
101 const auto peerB = TestNodeState{"peerB", 150};
102 
103 } // anonymous namespace
104 
105 TEST_CASE("PeerGateway | NoActivity", "[PeerGateway]")
106 {
107   test::serial_io::Fixture io;
108   TestObserver observer;
109   auto listener = makePeerGateway(util::injectVal(TestMessenger{}),
110     util::injectRef(observer), util::injectVal(io.makeIoContext()));
111   io.advanceTime(std::chrono::seconds(10));
112 
113   // Without any outside interaction but the passage of time, our
114   // listener should not have seen any peers.
115   CHECK(observer.mPeersSeen.empty());
116   CHECK(observer.mPeersLeft.empty());
117   CHECK(observer.mPeersTimedOut.empty());
118 }
119 
120 TEST_CASE("PeerGateway | ReceivedPeerState", "[PeerGateway]")
121 {
122   test::serial_io::Fixture io;
123   TestObserver observer;
124   TestMessenger messenger;
125   auto listener = makePeerGateway(util::injectRef(messenger), util::injectRef(observer),
126     util::injectVal(io.makeIoContext()));
127 
128   messenger.receivePeerState({peerA, 5});
129   io.flush();
130 
131   expectPeersSeen({peerA}, observer);
132 }
133 
134 TEST_CASE("PeerGateway | TwoPeersOneLeaves", "[PeerGateway]")
135 {
136   test::serial_io::Fixture io;
137   TestObserver observer;
138   TestMessenger messenger;
139   auto listener = makePeerGateway(util::injectRef(messenger), util::injectRef(observer),
140     util::injectVal(io.makeIoContext()));
141 
142   messenger.receivePeerState({peerA, 5});
143   messenger.receivePeerState({peerB, 5});
144   messenger.receiveByeBye({peerA.ident()});
145   io.flush();
146 
147   expectPeersSeen({peerA, peerB}, observer);
148   expectPeersLeft({peerA.ident()}, observer);
149 }
150 
151 TEST_CASE("PeerGateway | TwoPeersOneTimesOut", "[PeerGateway]")
152 {
153   test::serial_io::Fixture io;
154   TestObserver observer;
155   TestMessenger messenger;
156   auto listener = makePeerGateway(util::injectRef(messenger), util::injectRef(observer),
157     util::injectVal(io.makeIoContext()));
158 
159   messenger.receivePeerState({peerA, 5});
160   messenger.receivePeerState({peerB, 10});
161 
162   io.advanceTime(std::chrono::seconds(3));
163   expectPeersTimedOut({}, observer);
164   io.advanceTime(std::chrono::seconds(4));
165   expectPeersTimedOut({peerA.ident()}, observer);
166 }
167 
168 TEST_CASE("PeerGateway | PeerTimesOutAndIsSeenAgain", "[PeerGateway]")
169 {
170   test::serial_io::Fixture io;
171   TestObserver observer;
172   TestMessenger messenger;
173   auto listener = makePeerGateway(util::injectRef(messenger), util::injectRef(observer),
174     util::injectVal(io.makeIoContext()));
175 
176   messenger.receivePeerState({peerA, 5});
177   io.advanceTime(std::chrono::seconds(7));
178 
179   expectPeersTimedOut({peerA.ident()}, observer);
180 
181   messenger.receivePeerState({peerA, 5});
182   io.advanceTime(std::chrono::seconds(3));
183 
184   expectPeersSeen({peerA, peerA}, observer);
185   expectPeersTimedOut({peerA.ident()}, observer);
186 }
187 
188 } // namespace discovery
189 } // namespace ableton
190