Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mumble-voip/mumble.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/tests/TestMumbleProtocol/TestMumbleProtocol.cpp')
-rw-r--r--src/tests/TestMumbleProtocol/TestMumbleProtocol.cpp306
1 files changed, 306 insertions, 0 deletions
diff --git a/src/tests/TestMumbleProtocol/TestMumbleProtocol.cpp b/src/tests/TestMumbleProtocol/TestMumbleProtocol.cpp
new file mode 100644
index 000000000..85e0b502a
--- /dev/null
+++ b/src/tests/TestMumbleProtocol/TestMumbleProtocol.cpp
@@ -0,0 +1,306 @@
+// Copyright 2021 The Mumble Developers. All rights reserved.
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file at the root of the
+// Mumble source tree or at <https://www.mumble.info/LICENSE>.
+
+#include "MumbleProtocol.h"
+#include "MumbleUDP.pb.h"
+#include "Version.h"
+
+#include <QObject>
+#include <QtTest>
+
+#include <cstring>
+#include <sstream>
+#include <string>
+
+namespace Mumble {
+namespace Protocol {
+ // Add toString functions for use by QCOMPARE
+
+ char *toString(const Mumble::Protocol::AudioData &data) {
+ std::stringstream stream;
+ stream << "{ payload: {" << static_cast< const void * >(data.payload.data()) << ", " << data.payload.size()
+ << "}, frameNumber: " << data.frameNumber << ", isLastFrame: " << data.isLastFrame
+ << ", senderSession: " << data.senderSession << ", targetOrContext: " << data.targetOrContext
+ << ", usedCodec: " << static_cast< int >(data.usedCodec)
+ << ", containsPositionalData: " << data.containsPositionalData;
+ if (data.containsPositionalData) {
+ stream << ", position: {";
+
+ for (unsigned int i = 0; i < data.position.size(); ++i) {
+ stream << data.position[i];
+
+ if (i + 1 < data.position.size()) {
+ stream << ", ";
+ }
+ }
+
+ stream << "}";
+ }
+ stream << ", volumeAdjustment: " << data.volumeAdjustment.factor << " }";
+
+ std::string str = stream.str();
+
+ char *charArray = new char[str.size() + 1];
+
+ std::strcpy(charArray, str.c_str());
+
+ return charArray;
+ }
+
+ char *toString(const Mumble::Protocol::PingData &data) {
+ std::stringstream stream;
+
+ stream << "{ timestamp: " << data.timestamp
+ << ", requestAdditionalInformation: " << data.requestAdditionalInformation
+ << ", containsAdditionalInformation: " << data.containsAdditionalInformation
+ << ", userCount: " << data.userCount << ", maxUserCount: " << data.maxUserCount
+ << ", maxBandwidthPerUser: " << data.maxBandwidthPerUser << " }";
+
+ std::string str = stream.str();
+
+ char *charArray = new char[str.size() + 1];
+
+ std::strcpy(charArray, str.c_str());
+
+ return charArray;
+ }
+
+
+ template< Role role > class TestAudioEncoder : public UDPAudioEncoder< role > {
+ public:
+ using UDPAudioEncoder< role >::UDPAudioEncoder;
+
+ // Expose these functions publicly for testing-purposes
+ using UDPAudioEncoder< role >::getPreEncodedContext;
+ using UDPAudioEncoder< role >::getPreEncodedVolumeAdjustment;
+ };
+
+}; // namespace Protocol
+}; // namespace Mumble
+
+template< Mumble::Protocol::Role encoderRole, Mumble::Protocol::Role decoderRole > void do_test_ping() {
+ Mumble::Protocol::UDPPingEncoder< encoderRole > encoder;
+ Mumble::Protocol::UDPDecoder< decoderRole > decoder;
+
+ for (Version::mumble_raw_version_t version :
+ { Version::toRaw(1, 3, 0), Mumble::Protocol::PROTOBUF_INTRODUCTION_VERSION }) {
+ int major, minor, patch;
+ Version::fromRaw(version, &major, &minor, &patch);
+ qWarning("Using protocol version %d.%d.%d", major, minor, patch);
+
+ // Note: When the decoder is set to a version < PROTOBUF_INTRODUCTION_VERSION, it can decode pings in
+ // either format
+ encoder.setProtocolVersion(version);
+
+ Mumble::Protocol::PingData data;
+ data.timestamp = 42;
+
+ // Regular connectivity ping
+ auto encodedData = encoder.encodePingPacket(data);
+ QVERIFY(decoder.decode(encodedData));
+
+ QCOMPARE(decoder.getMessageType(), Mumble::Protocol::UDPMessageType::Ping);
+ QCOMPARE(decoder.getPingData(), data);
+
+ // Extended ping (request)
+ if (decoderRole == Mumble::Protocol::Role::Server) {
+ QVERIFY(encoderRole == Mumble::Protocol::Role::Client);
+
+ data.requestAdditionalInformation = true;
+
+ encodedData = encoder.encodePingPacket(data);
+ QVERIFY(decoder.decode(encodedData));
+
+ QCOMPARE(decoder.getMessageType(), Mumble::Protocol::UDPMessageType::Ping);
+ QCOMPARE(decoder.getPingData(), data);
+ } else {
+ QVERIFY(encoderRole == Mumble::Protocol::Role::Server);
+
+ data.containsAdditionalInformation = true;
+ data.userCount = 12;
+ data.maxUserCount = 42;
+ data.maxBandwidthPerUser = 512;
+
+ encodedData = encoder.encodePingPacket(data);
+ QVERIFY(decoder.decode(encodedData));
+
+ QCOMPARE(decoder.getMessageType(), Mumble::Protocol::UDPMessageType::Ping);
+ }
+ }
+}
+
+void printData(gsl::span< const Mumble::Protocol::byte > data) {
+ QString str = "Data is: { ";
+ for (Mumble::Protocol::byte current : data) {
+ str += QString::number(static_cast< int >(current)) + " ";
+ }
+ str += " }";
+
+ qDebug() << str;
+}
+
+template< Mumble::Protocol::Role encoderRole, Mumble::Protocol::Role decoderRole > void do_test_audio() {
+ Mumble::Protocol::UDPAudioEncoder< encoderRole > encoder;
+ Mumble::Protocol::UDPDecoder< decoderRole > decoder;
+
+ std::string payloadData = "I am the payload";
+
+ for (Version::mumble_raw_version_t version :
+ { Version::toRaw(1, 3, 0), Mumble::Protocol::PROTOBUF_INTRODUCTION_VERSION }) {
+ int major, minor, patch;
+ Version::fromRaw(version, &major, &minor, &patch);
+ qWarning("Using protocol version %d.%d.%d", major, minor, patch);
+
+ encoder.setProtocolVersion(version);
+ decoder.setProtocolVersion(version);
+
+ Mumble::Protocol::AudioData data;
+ data.payload = { reinterpret_cast< const Mumble::Protocol::byte * >(payloadData.c_str()), payloadData.size() };
+
+ data.frameNumber = 12;
+ data.containsPositionalData = true;
+ data.position = { 3, 2, 1 };
+ data.isLastFrame = true;
+ data.usedCodec = Mumble::Protocol::AudioCodec::Opus;
+ if (version >= Mumble::Protocol::PROTOBUF_INTRODUCTION_VERSION
+ && decoderRole == Mumble::Protocol::Role::Client) {
+ // Transmitting volume adjustment is only supported in the new packet format
+ // and only in the server->client direction
+ data.volumeAdjustment = VolumeAdjustment::fromFactor(1.4f);
+ }
+
+ if (decoderRole == Mumble::Protocol::Role::Client) {
+ QVERIFY(encoder.getRole() == Mumble::Protocol::Role::Server);
+
+ data.targetOrContext = Mumble::Protocol::AudioContext::Shout;
+ data.senderSession = 42;
+ } else {
+ QVERIFY(encoder.getRole() == Mumble::Protocol::Role::Client);
+
+ data.targetOrContext = Mumble::Protocol::ReservedTargetIDs::ServerLoopback;
+ }
+
+ auto encodedData = encoder.encodeAudioPacket(data);
+ QVERIFY(!encodedData.empty());
+
+ QVERIFY(decoder.decode(encodedData));
+
+ QCOMPARE(decoder.getMessageType(), Mumble::Protocol::UDPMessageType::Audio);
+ QCOMPARE(decoder.getAudioData(), data);
+
+ qWarning() << "Partial re-encoding";
+
+ // Re-encode fields from the "variable" part
+ data.targetOrContext = Mumble::Protocol::AudioContext::Listen;
+ if (version >= Mumble::Protocol::PROTOBUF_INTRODUCTION_VERSION
+ && decoderRole == Mumble::Protocol::Role::Client) {
+ data.volumeAdjustment = VolumeAdjustment::fromFactor(0.9f);
+ }
+
+ encodedData = encoder.updateAudioPacket(data);
+ QVERIFY(!encodedData.empty());
+
+ QVERIFY(decoder.decode(encodedData));
+
+ QCOMPARE(decoder.getMessageType(), Mumble::Protocol::UDPMessageType::Audio);
+ QCOMPARE(decoder.getAudioData(), data);
+
+ qWarning() << "Removing positional data";
+ // Update the audio packet to no longer contain positional data.
+ data.containsPositionalData = false;
+ data.targetOrContext = Mumble::Protocol::AudioContext::Normal;
+
+ encoder.dropPositionalData();
+
+ encodedData = encoder.updateAudioPacket(data);
+ QVERIFY(!encodedData.empty());
+
+ QVERIFY(decoder.decode(encodedData));
+
+ QCOMPARE(decoder.getMessageType(), Mumble::Protocol::UDPMessageType::Audio);
+ QCOMPARE(decoder.getAudioData(), data);
+ }
+}
+
+class TestMumbleProtocol : public QObject {
+ Q_OBJECT;
+private slots:
+ void test_ping_client_to_server() {
+ do_test_ping< Mumble::Protocol::Role::Client, Mumble::Protocol::Role::Server >();
+ }
+
+ void test_ping_server_to_client() {
+ do_test_ping< Mumble::Protocol::Role::Server, Mumble::Protocol::Role::Client >();
+ }
+
+ void test_audio_client_to_server() {
+ do_test_audio< Mumble::Protocol::Role::Client, Mumble::Protocol::Role::Server >();
+ }
+
+ void test_audio_server_to_client() {
+ do_test_audio< Mumble::Protocol::Role::Server, Mumble::Protocol::Role::Client >();
+ }
+
+ void test_preEncode_audio_context() {
+ Mumble::Protocol::TestAudioEncoder< Mumble::Protocol::Role::Server > encoder;
+
+ MumbleUDP::Audio msg;
+ std::vector< Mumble::Protocol::byte > buffer;
+
+ for (Mumble::Protocol::audio_context_t currentContext = Mumble::Protocol::AudioContext::begin;
+ currentContext < Mumble::Protocol::AudioContext::end; currentContext++) {
+ gsl::span< const Mumble::Protocol::byte > snippet = encoder.getPreEncodedContext(currentContext);
+
+ QVERIFY2(!snippet.empty(), "Unable to find pre-encoded snippet for audio context");
+
+ msg.set_context(currentContext);
+
+#if GOOGLE_PROTOBUF_VERSION >= 3002000
+ // ByteSizeLong() was introduced in Protobuf v3.2 as a replacement for ByteSize()
+ buffer.resize(msg.ByteSizeLong());
+#else
+ buffer.resize(msg.ByteSize());
+#endif
+ msg.SerializeWithCachedSizesToArray(buffer.data());
+
+ QCOMPARE(snippet.size(), buffer.size());
+ QVERIFY2(std::equal(snippet.begin(), snippet.end(), buffer.begin()), "Pre-encoded snippet is incorrect");
+ }
+
+ // Ensure that an unknown context yields an empty span
+ QVERIFY(encoder.getPreEncodedContext(Mumble::Protocol::AudioContext::end).empty());
+ }
+
+ void test_preEncode_volume_adjustments() {
+ Mumble::Protocol::TestAudioEncoder< Mumble::Protocol::Role::Server > encoder;
+
+ MumbleUDP::Audio msg;
+
+ constexpr int min = -60;
+ constexpr int max = 30;
+
+ for (int currentAdjustment = min; currentAdjustment <= max; ++currentAdjustment) {
+ msg.Clear();
+
+ gsl::span< const Mumble::Protocol::byte > snippet =
+ encoder.getPreEncodedVolumeAdjustment(VolumeAdjustment::fromDBAdjustment(currentAdjustment));
+
+ QVERIFY2(!snippet.empty(), "Unable to find pre-encoded snippet for volume adjustment");
+
+ msg.ParseFromArray(snippet.data(), snippet.size());
+
+ // This will perform a fuzzy-compare
+ QCOMPARE(msg.volume_adjustment(), std::pow(2.0f, currentAdjustment / 6.0f));
+ }
+
+ // Ensure that an unknown/unexpected volume adjustment yields an empty span
+ QVERIFY(encoder.getPreEncodedVolumeAdjustment(VolumeAdjustment::fromDBAdjustment(min - 1)).empty());
+ // We only expect pre-encoded values for integer dB adjustments
+ QVERIFY(encoder.getPreEncodedVolumeAdjustment(VolumeAdjustment(std::pow(2.0f, (min + 0.5) / 6.0f))).empty());
+ }
+};
+
+QTEST_MAIN(TestMumbleProtocol)
+#include "TestMumbleProtocol.moc"