Added tests for C API

This commit is contained in:
Paul-Louis Ageneau
2020-03-08 20:06:56 +01:00
parent 56198372fd
commit be04d8037e
9 changed files with 317 additions and 49 deletions

View File

@ -39,6 +39,7 @@ set(LIBDATACHANNEL_SOURCES
set(TESTS_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/test/main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/connectivity.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test/capi.cpp
)
set(TESTS_OFFERER_SOURCES

View File

@ -6,7 +6,7 @@ AR=$(CROSS)ar
RM=rm -f
CXXFLAGS=-std=c++17
CPPFLAGS=-O2 -pthread -fPIC -Wall -Wno-address-of-packed-member
LDFLAGS=-pthread
LDFLAGS=-pthread -g
LIBS=
LOCALLIBS=libusrsctp.a
USRSCTP_DIR=deps/usrsctp
@ -44,6 +44,9 @@ LDLIBS+=$(LOCALLIBS) $(shell pkg-config --libs $(LIBS))
SRCS=$(shell printf "%s " src/*.cpp)
OBJS=$(subst .cpp,.o,$(SRCS))
TEST_SRCS=$(shell printf "%s " test/*.cpp)
TEST_OBJS=$(subst .cpp,.o,$(TEST_SRCS))
all: $(NAME).a $(NAME).so tests
src/%.o: src/%.cpp
@ -60,8 +63,8 @@ $(NAME).a: $(OBJS)
$(NAME).so: $(LOCALLIBS) $(OBJS)
$(CXX) $(LDFLAGS) -shared -o $@ $(OBJS) $(LDLIBS)
tests: $(NAME).a test/main.o
$(CXX) $(LDFLAGS) -o $@ test/main.o $(NAME).a $(LDLIBS)
tests: $(NAME).a $(TEST_OBJS)
$(CXX) $(LDFLAGS) -o $@ $(TEST_OBJS) $(NAME).a $(LDLIBS)
clean:
-$(RM) include/rtc/*.d *.d

View File

@ -64,6 +64,7 @@ template <class... Ts> overloaded(Ts...)->overloaded<Ts...>;
template <typename... P> class synchronized_callback {
public:
synchronized_callback() = default;
synchronized_callback(std::function<void(P...)> func) { *this = std::move(func); };
~synchronized_callback() { *this = nullptr; }
synchronized_callback &operator=(std::function<void(P...)> func) {

View File

@ -52,12 +52,18 @@ typedef enum {
RTC_LOG_VERBOSE = 6
} rtcLogLevel;
typedef struct {
const char **iceServers;
int iceServersCount;
} rtcConfiguration;
typedef void (*dataChannelCallbackFunc)(int dc, void *ptr);
typedef void (*descriptionCallbackFunc)(const char *sdp, const char *type, void *ptr);
typedef void (*candidateCallbackFunc)(const char *cand, const char *mid, void *ptr);
typedef void (*stateChangeCallbackFunc)(rtcState state, void *ptr);
typedef void (*gatheringStateCallbackFunc)(rtcGatheringState state, void *ptr);
typedef void (*openCallbackFunc)(void *ptr);
typedef void (*closedCallbackFunc)(void *ptr);
typedef void (*errorCallbackFunc)(const char *error, void *ptr);
typedef void (*messageCallbackFunc)(const char *message, int size, void *ptr);
typedef void (*bufferedAmountLowCallbackFunc)(void *ptr);
@ -70,7 +76,7 @@ void rtcInitLogger(rtcLogLevel level);
void rtcSetUserPointer(int i, void *ptr);
// PeerConnection
int rtcCreatePeerConnection(const char **iceServers, int iceServersCount);
int rtcCreatePeerConnection(const rtcConfiguration *config);
int rtcDeletePeerConnection(int pc);
int rtcSetDataChannelCallback(int pc, dataChannelCallbackFunc cb);
@ -88,6 +94,7 @@ int rtcDeleteDataChannel(int dc);
int rtcGetDataChannelLabel(int dc, char *buffer, int size);
int rtcSetOpenCallback(int dc, openCallbackFunc cb);
int rtcSetClosedCallback(int dc, closedCallbackFunc cb);
int rtcSetErrorCallback(int dc, errorCallbackFunc cb);
int rtcSetMessageCallback(int dc, messageCallbackFunc cb);
int rtcSendMessage(int dc, const char *data, int size);

View File

@ -18,8 +18,6 @@
#include "channel.hpp"
namespace {}
namespace rtc {
size_t Channel::maxMessageSize() const { return DEFAULT_MAX_MESSAGE_SIZE; }

View File

@ -106,15 +106,28 @@ void rtcSetUserPointer(int i, void *ptr) {
userPointerMap.erase(i);
}
int rtcCreatePeerConnection(const char **iceServers, int iceServersCount) {
Configuration config;
for (int i = 0; i < iceServersCount; ++i)
config.iceServers.emplace_back(IceServer(string(iceServers[i])));
int rtcCreatePeerConnection(const rtcConfiguration *config) {
Configuration c;
for (int i = 0; i < config->iceServersCount; ++i)
c.iceServers.emplace_back(string(config->iceServers[i]));
return emplacePeerConnection(std::make_shared<PeerConnection>(config));
return emplacePeerConnection(std::make_shared<PeerConnection>(c));
}
int rtcDeletePeerConnection(int pc) { return erasePeerConnection(pc) ? 0 : -1; }
int rtcDeletePeerConnection(int pc) {
auto peerConnection = getPeerConnection(pc);
if (!peerConnection)
return -1;
peerConnection->onDataChannel(nullptr);
peerConnection->onLocalDescription(nullptr);
peerConnection->onLocalCandidate(nullptr);
peerConnection->onStateChange(nullptr);
peerConnection->onGatheringStateChange(nullptr);
erasePeerConnection(pc);
return 0;
}
int rtcCreateDataChannel(int pc, const char *label) {
auto peerConnection = getPeerConnection(pc);
@ -127,19 +140,36 @@ int rtcCreateDataChannel(int pc, const char *label) {
return dc;
}
int rtcDeleteDataChannel(int dc) { return eraseDataChannel(dc) ? 0 : -1; }
int rtcDeleteDataChannel(int dc) {
auto dataChannel = getDataChannel(dc);
if (!dataChannel)
return -1;
dataChannel->onOpen(nullptr);
dataChannel->onClosed(nullptr);
dataChannel->onError(nullptr);
dataChannel->onMessage(nullptr);
dataChannel->onBufferedAmountLow(nullptr);
dataChannel->onAvailable(nullptr);
eraseDataChannel(dc);
return 0;
}
int rtcSetDataChannelCallback(int pc, dataChannelCallbackFunc cb) {
auto peerConnection = getPeerConnection(pc);
if (!peerConnection)
return -1;
if (cb)
peerConnection->onDataChannel([pc, cb](std::shared_ptr<DataChannel> dataChannel) {
int dc = emplaceDataChannel(dataChannel);
void *ptr = getUserPointer(pc);
rtcSetUserPointer(dc, ptr);
cb(dc, ptr);
});
else
peerConnection->onDataChannel(nullptr);
return 0;
}
@ -148,9 +178,12 @@ int rtcSetLocalDescriptionCallback(int pc, descriptionCallbackFunc cb) {
if (!peerConnection)
return -1;
if (cb)
peerConnection->onLocalDescription([pc, cb](const Description &desc) {
cb(string(desc).c_str(), desc.typeString().c_str(), getUserPointer(pc));
});
else
peerConnection->onLocalDescription(nullptr);
return 0;
}
@ -159,9 +192,12 @@ int rtcSetLocalCandidateCallback(int pc, candidateCallbackFunc cb) {
if (!peerConnection)
return -1;
if (cb)
peerConnection->onLocalCandidate([pc, cb](const Candidate &cand) {
cb(cand.candidate().c_str(), cand.mid().c_str(), getUserPointer(pc));
});
else
peerConnection->onLocalCandidate(nullptr);
return 0;
}
@ -170,9 +206,12 @@ int rtcSetStateChangeCallback(int pc, stateChangeCallbackFunc cb) {
if (!peerConnection)
return -1;
if (cb)
peerConnection->onStateChange([pc, cb](PeerConnection::State state) {
cb(static_cast<rtcState>(state), getUserPointer(pc));
});
else
peerConnection->onStateChange(nullptr);
return 0;
}
@ -181,9 +220,12 @@ int rtcSetGatheringStateChangeCallback(int pc, gatheringStateCallbackFunc cb) {
if (!peerConnection)
return -1;
if (cb)
peerConnection->onGatheringStateChange([pc, cb](PeerConnection::GatheringState state) {
cb(static_cast<rtcGatheringState>(state), getUserPointer(pc));
});
else
peerConnection->onGatheringStateChange(nullptr);
return 0;
}
@ -225,7 +267,22 @@ int rtcSetOpenCallback(int dc, openCallbackFunc cb) {
if (!dataChannel)
return -1;
if (cb)
dataChannel->onOpen([dc, cb]() { cb(getUserPointer(dc)); });
else
dataChannel->onOpen(nullptr);
return 0;
}
int rtcSetClosedCallback(int dc, closedCallbackFunc cb) {
auto dataChannel = getDataChannel(dc);
if (!dataChannel)
return -1;
if (cb)
dataChannel->onClosed([dc, cb]() { cb(getUserPointer(dc)); });
else
dataChannel->onClosed(nullptr);
return 0;
}
@ -234,7 +291,11 @@ int rtcSetErrorCallback(int dc, errorCallbackFunc cb) {
if (!dataChannel)
return -1;
dataChannel->onError([dc, cb](const string &error) { cb(error.c_str(), getUserPointer(dc)); });
if (cb)
dataChannel->onError(
[dc, cb](const string &error) { cb(error.c_str(), getUserPointer(dc)); });
else
dataChannel->onError(nullptr);
return 0;
}
@ -243,11 +304,15 @@ int rtcSetMessageCallback(int dc, messageCallbackFunc cb) {
if (!dataChannel)
return -1;
if (cb)
dataChannel->onMessage(
[dc, cb](const binary &b) {
cb(reinterpret_cast<const char *>(b.data()), b.size(), getUserPointer(dc));
},
[dc, cb](const string &s) { cb(s.c_str(), -1, getUserPointer(dc)); });
else
dataChannel->onMessage(nullptr);
return 0;
}
@ -289,7 +354,10 @@ int rtcSetBufferedAmountLowCallback(int dc, bufferedAmountLowCallbackFunc cb) {
if (!dataChannel)
return -1;
dataChannel->onOpen([dc, cb]() { cb(getUserPointer(dc)); });
if (cb)
dataChannel->onBufferedAmountLow([dc, cb]() { cb(getUserPointer(dc)); });
else
dataChannel->onBufferedAmountLow(nullptr);
return 0;
}
@ -306,7 +374,10 @@ int rtcSetAvailableCallback(int dc, availableCallbackFunc cb) {
if (!dataChannel)
return -1;
if (cb)
dataChannel->onOpen([dc, cb]() { cb(getUserPointer(dc)); });
else
dataChannel->onOpen(nullptr);
return 0;
}
@ -333,11 +404,11 @@ int rtcReceiveMessage(int dc, char *buffer, int *size) {
},
[&](const string &s) {
int len = std::min(*size - 1, int(s.size()));
*size = -1;
if (len >= 0) {
std::copy(s.data(), s.data() + len, buffer);
buffer[len] = '\0';
}
*size = -(len + 1);
return len + 1;
}},
*message);

178
test/capi.cpp Normal file
View File

@ -0,0 +1,178 @@
/**
* Copyright (c) 2020 Paul-Louis Ageneau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <rtc/rtc.h>
#include <cstdbool>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h> // for sleep
using namespace std;
typedef struct {
rtcState state;
rtcGatheringState gatheringState;
int pc;
int dc;
bool connected;
} Peer;
Peer *peer1 = NULL;
Peer *peer2 = NULL;
static void descriptionCallback(const char *sdp, const char *type, void *ptr) {
Peer *peer = (Peer *)ptr;
printf("Description %d:\n%s\n", peer == peer1 ? 1 : 2, sdp);
Peer *other = peer == peer1 ? peer2 : peer1;
rtcSetRemoteDescription(other->pc, sdp, type);
}
static void candidateCallback(const char *cand, const char *mid, void *ptr) {
Peer *peer = (Peer *)ptr;
printf("Candidate %d: %s\n", peer == peer1 ? 1 : 2, cand);
Peer *other = peer == peer1 ? peer2 : peer1;
rtcAddRemoteCandidate(other->pc, cand, mid);
}
static void stateChangeCallback(rtcState state, void *ptr) {
Peer *peer = (Peer *)ptr;
peer->state = state;
printf("State %d: %d\n", peer == peer1 ? 1 : 2, (int)state);
}
static void gatheringStateCallback(rtcGatheringState state, void *ptr) {
Peer *peer = (Peer *)ptr;
peer->gatheringState = state;
printf("Gathering state %d: %d\n", peer == peer1 ? 1 : 2, (int)state);
}
static void openCallback(void *ptr) {
Peer *peer = (Peer *)ptr;
peer->connected = true;
printf("DataChannel %d: Open\n", peer == peer1 ? 1 : 2);
const char *message = peer == peer1 ? "Hello from 1" : "Hello from 2";
rtcSendMessage(peer->dc, message, -1); // negative size indicates a null-terminated string
}
static void closedCallback(void *ptr) {
Peer *peer = (Peer *)ptr;
peer->connected = false;
}
static void messageCallback(const char *message, int size, void *ptr) {
Peer *peer = (Peer *)ptr;
if (size < 0) { // negative size indicates a null-terminated string
printf("Message %d: %s\n", peer == peer1 ? 1 : 2, message);
} else {
printf("Message %d: [binary of size %d]\n", peer == peer1 ? 1 : 2, size);
}
}
static void dataChannelCallback(int dc, void *ptr) {
Peer *peer = (Peer *)ptr;
peer->dc = dc;
peer->connected = true;
rtcSetClosedCallback(dc, closedCallback);
rtcSetMessageCallback(dc, messageCallback);
char buffer[256];
if (rtcGetDataChannelLabel(dc, buffer, 256) >= 0)
printf("DataChannel %d: Received with label \"%s\"\n", peer == peer1 ? 1 : 2, buffer);
const char *message = peer == peer1 ? "Hello from 1" : "Hello from 2";
rtcSendMessage(peer->dc, message, -1); // negative size indicates a null-terminated string
}
static Peer *createPeer(const rtcConfiguration *config) {
Peer *peer = (Peer *)malloc(sizeof(Peer));
if (!peer)
return nullptr;
memset(peer, 0, sizeof(Peer));
// Create peer connection
peer->pc = rtcCreatePeerConnection(config);
rtcSetUserPointer(peer->pc, peer);
rtcSetDataChannelCallback(peer->pc, dataChannelCallback);
rtcSetLocalDescriptionCallback(peer->pc, descriptionCallback);
rtcSetLocalCandidateCallback(peer->pc, candidateCallback);
rtcSetStateChangeCallback(peer->pc, stateChangeCallback);
rtcSetGatheringStateChangeCallback(peer->pc, gatheringStateCallback);
return peer;
}
static void deletePeer(Peer *peer) {
if (peer) {
if (peer->dc)
rtcDeleteDataChannel(peer->dc);
if (peer->pc)
rtcDeletePeerConnection(peer->pc);
}
}
int test_capi_main() {
rtcInitLogger(RTC_LOG_DEBUG);
rtcConfiguration config;
memset(&config, 0, sizeof(config));
// const char *iceServers[1] = {"stun:stun.l.google.com:19302"};
// config.iceServers = iceServers;
// config.iceServersCount = 1;
// Create peer 1
peer1 = createPeer(&config);
if (!peer1)
goto error;
// Create peer 2
peer2 = createPeer(&config);
if (!peer2)
goto error;
// Peer 1: Create data channel
peer1->dc = rtcCreateDataChannel(peer1->pc, "test");
rtcSetOpenCallback(peer1->dc, openCallback);
rtcSetClosedCallback(peer1->dc, closedCallback);
rtcSetMessageCallback(peer1->dc, messageCallback);
sleep(3);
if (peer1->connected && peer2->connected) {
deletePeer(peer1);
deletePeer(peer2);
sleep(1);
printf("Success\n");
return 0;
}
error:
deletePeer(peer1);
deletePeer(peer2);
return -1;
}
#include <stdexcept>
void test_capi() {
if (test_capi_main())
throw std::runtime_error("C API test failed");
}

View File

@ -33,10 +33,9 @@ void test_connectivity() {
Configuration config;
// config.iceServers.emplace_back("stun:stun.l.google.com:19302");
// config.iceServers.emplace_back("turn:USER@PASSWORD:turn.example.net:3478?transport=udp"); //
// libnice only config.enableIceTcp = true; // libnice only
auto pc1 = std::make_shared<PeerConnection>(config);
auto pc2 = std::make_shared<PeerConnection>(config);
pc1->onLocalDescription([wpc2 = make_weak_ptr(pc2)](const Description &sdp) {
@ -83,11 +82,11 @@ void test_connectivity() {
shared_ptr<DataChannel> dc2;
pc2->onDataChannel([&dc2](shared_ptr<DataChannel> dc) {
cout << "Got a DataChannel with label: " << dc->label() << endl;
cout << "DataChannel 2: Received with label \"" << dc->label() << "\"" << endl;
dc2 = dc;
dc2->onMessage([](const variant<binary, string> &message) {
if (holds_alternative<string>(message)) {
cout << "Received 2: " << get<string>(message) << endl;
cout << "Message 2: " << get<string>(message) << endl;
}
});
dc2->send("Hello from 2");
@ -98,12 +97,12 @@ void test_connectivity() {
auto dc1 = wdc1.lock();
if (!dc1)
return;
cout << "DataChannel open: " << dc1->label() << endl;
cout << "DataChannel 1: Open" << endl;
dc1->send("Hello from 1");
});
dc1->onMessage([](const variant<binary, string> &message) {
if (holds_alternative<string>(message)) {
cout << "Received 1: " << get<string>(message) << endl;
cout << "Message 1: " << get<string>(message) << endl;
}
});

View File

@ -21,14 +21,24 @@
using namespace std;
void test_connectivity();
void test_capi();
int main(int argc, char **argv) {
try {
std::cout << "*** Running connectivity test..." << std::endl;
test_connectivity();
std::cout << "*** Finished connectivity test" << std::endl;
} catch (const exception &e) {
std::cerr << "Connectivity check failed: " << e.what() << endl;
std::cerr << "Connectivity test failed: " << e.what() << endl;
return -1;
}
try {
std::cout << "*** Running C API test..." << std::endl;
test_capi();
std::cout << "*** Finished C API test" << std::endl;
} catch (const exception &e) {
std::cerr << "C API test failed: " << e.what() << endl;
return -1;
}
return 0;
}