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/Payload.hpp>
21 #include <ableton/discovery/test/Socket.hpp>
22 #include <ableton/link/GhostXForm.hpp>
23 #include <ableton/link/NodeState.hpp>
24 #include <ableton/link/PayloadEntries.hpp>
25 #include <ableton/link/PingResponder.hpp>
26 #include <ableton/link/SessionId.hpp>
27 #include <ableton/link/v1/Messages.hpp>
28 #include <ableton/platforms/asio/AsioWrapper.hpp>
29 #include <ableton/test/CatchWrapper.hpp>
30 #include <ableton/util/Log.hpp>
31 #include <ableton/util/test/IoService.hpp>
32 #include <array>
33 
34 namespace ableton
35 {
36 namespace link
37 {
38 namespace
39 {
40 
41 struct RpFixture
42 {
43   RpFixture()
44     : mAddress(asio::ip::address_v4::from_string("127.0.0.1"))
45     , mResponder(mAddress,
46         NodeId::random(),
47         GhostXForm{1.0, std::chrono::microseconds{0}},
48         util::injectVal(util::test::IoService{}),
49         MockClock{},
50         util::injectVal(util::NullLog{}))
51   {
52   }
53 
54   discovery::test::Socket responderSocket()
55   {
56     return mResponder.socket();
57   }
58 
59   std::size_t numSentMessages()
60   {
61     return responderSocket().sentMessages.size();
62   }
63 
64   struct MockClock
65   {
66     std::chrono::microseconds micros() const
67     {
68       return std::chrono::microseconds{4};
69     }
70   };
71 
72   asio::ip::address_v4 mAddress = asio::ip::address_v4::from_string("127.0.0.1");
73   PingResponder<util::test::IoService, MockClock, discovery::test::Socket, util::NullLog>
74     mResponder;
75 };
76 
77 } // anonymous namespace
78 
79 TEST_CASE("PingResponder | ReplyToPing", "[PingResponder]")
80 {
81   using std::chrono::microseconds;
82 
83   RpFixture fixture;
84 
85   // Construct and send Ping Message. Check if Responder sent back a Message
86   const auto payload =
87     discovery::makePayload(HostTime{microseconds(2)}, PrevGHostTime{microseconds(1)});
88 
89   v1::MessageBuffer buffer;
90   const auto msgBegin = std::begin(buffer);
91   const auto msgEnd = v1::pingMessage(payload, msgBegin);
92   const auto endpoint = asio::ip::udp::endpoint(fixture.mAddress, 8888);
93 
94   fixture.responderSocket().incomingMessage(endpoint, msgBegin, msgEnd);
95 
96   CHECK(1 == fixture.numSentMessages());
97 
98   // Check Responder's message
99   const auto messageBuffer = fixture.responderSocket().sentMessages[0].first;
100   const auto result = v1::parseMessageHeader(begin(messageBuffer), end(messageBuffer));
101   const auto& hdr = result.first;
102 
103   std::chrono::microseconds ghostTime{0};
104   std::chrono::microseconds prevGHostTime{0};
105   std::chrono::microseconds hostTime{0};
106   discovery::parsePayload<GHostTime, PrevGHostTime, HostTime>(result.second,
107     std::end(messageBuffer),
108     [&ghostTime](GHostTime gt) { ghostTime = std::move(gt.time); },
109     [&prevGHostTime](PrevGHostTime gt) { prevGHostTime = std::move(gt.time); },
110     [&hostTime](HostTime ht) { hostTime = std::move(ht.time); });
111 
112   CHECK(v1::kPong == hdr.messageType);
113   CHECK(std::chrono::microseconds{2} == hostTime);
114   CHECK(std::chrono::microseconds{1} == prevGHostTime);
115   CHECK(std::chrono::microseconds{4} == ghostTime);
116 }
117 
118 TEST_CASE("PingResponder | PingSizeExceeding", "[PingResponder]")
119 {
120   using std::chrono::microseconds;
121 
122   RpFixture fixture;
123 
124   const auto ht = HostTime{microseconds(2)};
125   const auto payload = discovery::makePayload(ht, ht, ht);
126 
127   v1::MessageBuffer buffer;
128   const auto msgBegin = std::begin(buffer);
129   const auto msgEnd = v1::pingMessage(payload, msgBegin);
130 
131   const auto endpoint = asio::ip::udp::endpoint(fixture.mAddress, 8888);
132 
133   fixture.responderSocket().incomingMessage(endpoint, msgBegin, msgEnd);
134 
135   CHECK(0 == fixture.numSentMessages());
136 }
137 
138 } // namespace link
139 } // namespace ableton
140