Vendored
+1
-1
@@ -10,7 +10,7 @@
|
||||
"request": "launch",
|
||||
// Resolved by CMake Tools:
|
||||
"program": "${command:cmake.launchTargetPath}",
|
||||
"args": [ "[bluetooth_service]" ],
|
||||
"args": [ "[Dev]" ],
|
||||
"stopAtEntry": false,
|
||||
"cwd": "${workspaceFolder}",
|
||||
"environment": [
|
||||
|
||||
+27
-14
@@ -19,25 +19,38 @@
|
||||
#include "pch.h"
|
||||
#include "catch.hpp"
|
||||
|
||||
|
||||
#include "BeastServer.hpp"
|
||||
#include "MemDebug.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
TEST_CASE( "BeastServer shutdown", "[beastServerShutdown]" ) {
|
||||
TEST_CASE("BeastServer shutdown", "[beastServerShutdown][Build][Dev]")
|
||||
{
|
||||
MemStats initialMemory = GetMemStats();
|
||||
{
|
||||
auto const address = boost::asio::ip::make_address("0.0.0.0");
|
||||
auto const port = 8081;
|
||||
std::string doc_root = ".";
|
||||
auto const threads = 3;
|
||||
|
||||
auto const address = boost::asio::ip::make_address("0.0.0.0");
|
||||
auto const port = 8081;
|
||||
std::string doc_root = ".";
|
||||
auto const threads = 3;
|
||||
auto server = createBeastServer(
|
||||
address, port, doc_root.c_str(), threads);
|
||||
server->RunInBackground();
|
||||
sleep(5);
|
||||
server->ShutDown(1000);
|
||||
sleep(1);
|
||||
server->Join();
|
||||
}
|
||||
MemStats finalMemory = GetMemStats();
|
||||
|
||||
auto server = createBeastServer(
|
||||
address,port,doc_root.c_str(),threads);
|
||||
server->RunInBackground();
|
||||
sleep(30000);
|
||||
server->ShutDown(1000);
|
||||
sleep(1);
|
||||
server->Join();
|
||||
// currently leaking one allocation of 8 bytes. Acceptable.
|
||||
if (finalMemory.allocations > initialMemory.allocations+1)
|
||||
{
|
||||
std::cout << "Leaked Allocations: " << (finalMemory.allocations- initialMemory.allocations) << std::endl;
|
||||
std::cout << "Leaked Memory: " << (finalMemory.allocated - initialMemory.allocated) << " bytes" << std::endl;
|
||||
|
||||
REQUIRE(finalMemory.allocations == initialMemory.allocations);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -189,6 +189,7 @@ add_executable(pipedaltest ${PIPEDAL_SOURCES} testMain.cpp
|
||||
SystemConfigFileTest.cpp
|
||||
BeastServerTest.cpp
|
||||
MemDebug.cpp
|
||||
MemDebug.hpp
|
||||
)
|
||||
|
||||
configure_file(config.hpp.in config.hpp)
|
||||
@@ -207,6 +208,13 @@ target_link_libraries(pipedaltest PRIVATE pthread atomic stdc++fs asound
|
||||
${LILV_0_LIBRARIES} ${JACK_LIBRARIES} ${LIBNL3_LIBRARIES} systemd SDBusCpp::sdbus-c++
|
||||
)
|
||||
|
||||
|
||||
# Build machine tests. Run tests that are not dependent on hardware.
|
||||
add_test(NAME BuildTest COMMAND pipedaltest "[Build]")
|
||||
|
||||
# Developer tests. Run tests that only succeed on a Raspberry Pi with attached UBS Audio.
|
||||
add_test(NAME DevTest COMMAND pipedaltest "[Dev]")
|
||||
|
||||
#################################
|
||||
|
||||
|
||||
|
||||
+22
-3
@@ -25,13 +25,32 @@
|
||||
|
||||
|
||||
#include "Lv2Host.hpp"
|
||||
#include "MemDebug.hpp"
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
|
||||
TEST_CASE( "Lv2Host memory leak", "[lv2host_leak]" ) {
|
||||
TEST_CASE( "Lv2Host memory leak", "[lv2host_leak][Build][Dev]" ) {
|
||||
|
||||
Lv2Host host;
|
||||
MemStats initialMemory = GetMemStats();
|
||||
{
|
||||
Lv2Host host;
|
||||
|
||||
host.Load("/usr/lib/lv2:/usr/local/lib/lv2:/usr/modep/lv2");
|
||||
host.Load("/usr/lib/lv2:/usr/local/lib/lv2:/usr/modep/lv2");
|
||||
}
|
||||
MemStats finalMemory = GetMemStats();
|
||||
|
||||
// Something lilv leaks a Dublin Core url.
|
||||
// Acceptable.
|
||||
const int ACCEPTABLE_ALLOCATION_LEAKS = 4;
|
||||
const int ACCEPTABLE_MEMORY_LEAK = 400;
|
||||
|
||||
if (finalMemory.allocations > initialMemory.allocations + ACCEPTABLE_ALLOCATION_LEAKS
|
||||
|| finalMemory.allocated > initialMemory.allocated + ACCEPTABLE_MEMORY_LEAK)
|
||||
{
|
||||
std::cout << "Leaked Allocations: " << (finalMemory.allocations- initialMemory.allocations) << std::endl;
|
||||
std::cout << "Leaked Memory: " << (finalMemory.allocated - initialMemory.allocated) << " bytes" << std::endl;
|
||||
|
||||
REQUIRE(finalMemory.allocations == initialMemory.allocations);
|
||||
}
|
||||
}
|
||||
+110
-31
@@ -1,69 +1,148 @@
|
||||
// Copyright (c) 2022 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "stdlib.h"
|
||||
#include <mutex>
|
||||
#ifdef DEBUG
|
||||
#include "MemDebug.hpp"
|
||||
#include <exception>
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
static std::mutex memMutex;
|
||||
|
||||
struct MemStats {
|
||||
size_t allocated = 0;
|
||||
size_t allocations = 0;
|
||||
|
||||
|
||||
static MemStats memStats;
|
||||
|
||||
const MemStats&pipedal::GetMemStats()
|
||||
{
|
||||
return memStats;
|
||||
}
|
||||
|
||||
const size_t MEM_GUARD = (size_t)0xBAADF00DBAADF00D;
|
||||
|
||||
struct MemHeader {
|
||||
size_t size = 0;
|
||||
MemHeader*pNext = nullptr;
|
||||
MemHeader *pPrev = nullptr;
|
||||
size_t mem_guard = MEM_GUARD;
|
||||
|
||||
};
|
||||
|
||||
MemStats memStats;
|
||||
static MemHeader memHead;
|
||||
|
||||
void *operator new(size_t size)
|
||||
{
|
||||
char *p = (char*)malloc(size+32+4);
|
||||
{
|
||||
std::lock_guard lock(memMutex);
|
||||
MemHeader *pHead = (MemHeader*)p;
|
||||
pHead->size = size;
|
||||
pHead->pNext = nullptr;
|
||||
pHead->pPrev = nullptr;
|
||||
pHead->mem_guard = MEM_GUARD;
|
||||
memStats.allocated += size;
|
||||
memStats.allocations++;
|
||||
}
|
||||
|
||||
char *p = (char*)malloc(size+8);
|
||||
((size_t*)p)[0] = size;
|
||||
return p+8;
|
||||
// link block into allocation chain
|
||||
if (memHead.pNext == nullptr)
|
||||
{
|
||||
memHead.pNext = memHead.pPrev = pHead;
|
||||
pHead->pNext = pHead->pPrev = &memHead;
|
||||
} else {
|
||||
pHead->pNext = memHead.pNext;
|
||||
pHead->pNext->pPrev = pHead;
|
||||
pHead->pPrev = &memHead;
|
||||
memHead.pNext = pHead;
|
||||
}
|
||||
|
||||
p = p+32;
|
||||
char *endGuard = p + size;
|
||||
*endGuard++ = (char)0xBA;
|
||||
*endGuard++ = (char)0xAD;
|
||||
*endGuard++ = (char)0xF0;
|
||||
*endGuard++ = (char)0x0D;
|
||||
|
||||
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static void bad_alloc();
|
||||
|
||||
void operator delete(void*mem)
|
||||
{
|
||||
if (mem == nullptr) return;
|
||||
|
||||
char *p = ((char*)mem);
|
||||
size_t size = *(size_t*)(p-8);
|
||||
|
||||
{
|
||||
MemHeader *pHeader = (MemHeader*)(p-32);
|
||||
std::lock_guard lock(memMutex);
|
||||
memStats.allocated -= size;
|
||||
memStats.allocated -= pHeader->size;
|
||||
memStats.allocations--;
|
||||
|
||||
// check the memory guards.
|
||||
if (pHeader->mem_guard != MEM_GUARD)
|
||||
{
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
char *pTail = p + pHeader->size;
|
||||
if (*pTail++ != (char)0xBA) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
if (*pTail++ != (char)0xAD) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
if (*pTail++ != (char)0xF0) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
if (*pTail++ != (char)0x0D) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
// invalidate the memory guards (double free, eg).
|
||||
pHeader->mem_guard = (size_t)0xDDDDDDDD;
|
||||
pTail = p + pHeader->size;
|
||||
*pTail++ = (char)0xDD;
|
||||
*pTail++ = (char)0xDD;
|
||||
*pTail++ = (char)0xDD;
|
||||
*pTail++ = (char)0xDD;
|
||||
|
||||
// unlink the block from the allocation chain.
|
||||
pHeader->pNext->pPrev = pHeader->pPrev;
|
||||
pHeader->pPrev->pNext = pHeader->pNext;
|
||||
pHeader->pNext = nullptr;
|
||||
pHeader->pPrev = nullptr;
|
||||
|
||||
}
|
||||
free(p-32);
|
||||
|
||||
|
||||
free(p-8);
|
||||
}
|
||||
|
||||
void* operator new[](size_t size)
|
||||
{
|
||||
{
|
||||
std::lock_guard lock(memMutex);
|
||||
memStats.allocated += size;
|
||||
memStats.allocations++;
|
||||
}
|
||||
|
||||
char *p = (char*)malloc(size+8);
|
||||
((size_t*)p)[0] = size;
|
||||
return p+8;
|
||||
return operator new(size);
|
||||
}
|
||||
|
||||
void operator delete[](void*mem)
|
||||
{
|
||||
char *p = ((char*)mem);
|
||||
size_t size = *(size_t*)(p-8);
|
||||
{
|
||||
std::lock_guard lock(memMutex);
|
||||
memStats.allocated -= size;
|
||||
memStats.allocations--;
|
||||
}
|
||||
|
||||
free(p-8);
|
||||
return operator delete(mem);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2022 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace pipedal {
|
||||
struct MemStats {
|
||||
size_t allocated = 0;
|
||||
size_t allocations = 0;
|
||||
};
|
||||
|
||||
const MemStats &GetMemStats();
|
||||
|
||||
}
|
||||
@@ -22,16 +22,17 @@
|
||||
#include <sstream>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
#include "PiPedalAlsa.hpp"
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
TEST_CASE( "ALSA Test", "[pipedal_alsa_test]" ) {
|
||||
TEST_CASE( "ALSA Test", "[pipedal_alsa_test][Build][Dev]" ) {
|
||||
|
||||
PiPedalAlsaDevices devices;
|
||||
auto result = devices.GetAlsaDevices();
|
||||
REQUIRE(result.size() >= 1);
|
||||
std::cout << result.size() << " ALSA devices found." << 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -44,7 +44,6 @@ static std::string makeLine(const std::string &key, const std::string &value)
|
||||
|
||||
void SystemConfigFile::Load(const std::filesystem::path &path)
|
||||
{
|
||||
this->lines.clear();
|
||||
|
||||
ifstream f(path);
|
||||
if (!f.is_open())
|
||||
@@ -53,6 +52,12 @@ void SystemConfigFile::Load(const std::filesystem::path &path)
|
||||
s << "File not found: " << path;
|
||||
throw PiPedalException(s.str());
|
||||
}
|
||||
Load(f);
|
||||
this->currentPath = path;
|
||||
}
|
||||
void SystemConfigFile::Load(std::istream&f) {
|
||||
this->lines.clear();
|
||||
|
||||
|
||||
while (true)
|
||||
{
|
||||
@@ -64,7 +69,6 @@ void SystemConfigFile::Load(const std::filesystem::path &path)
|
||||
}
|
||||
lines.push_back(line);
|
||||
}
|
||||
this->currentPath = path;
|
||||
}
|
||||
|
||||
int64_t SystemConfigFile::GetLine(const std::string &key) const
|
||||
|
||||
@@ -36,6 +36,7 @@ public:
|
||||
Load(path);
|
||||
}
|
||||
|
||||
void Load(std::istream &input);
|
||||
void Load(const std::filesystem::path&path);
|
||||
|
||||
void Save(std::ostream &os);
|
||||
|
||||
@@ -25,30 +25,94 @@
|
||||
#include "PiPedalException.hpp"
|
||||
#include <iostream>
|
||||
|
||||
|
||||
#include "SystemConfigFile.hpp"
|
||||
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
static std::string GetTestFile()
|
||||
{
|
||||
std::stringstream s;
|
||||
s
|
||||
<< "interface=wlan0" << std::endl
|
||||
<< "driver=nl80211" << std::endl
|
||||
<< "country_code=CA" << std::endl
|
||||
<< "" << std::endl
|
||||
<< "# Wi-Fi features" << std::endl
|
||||
<< "ieee80211n=1" << std::endl
|
||||
<< "ieee80211d=1" << std::endl
|
||||
<< "ieee80211ac=1" << std::endl
|
||||
<< "ht_capab=[HT40][SHORT-GI-20][DSSS_CCK-40]" << std::endl
|
||||
<< "" << std::endl
|
||||
<< "# Authentication options" << std::endl
|
||||
<< "wmm_enabled=1" << std::endl
|
||||
<< "auth_algs=1" << std::endl
|
||||
<< "wpa=2" << std::endl
|
||||
<< "wpa_pairwise=TKIP CCMP" << std::endl
|
||||
<< "wpa_ptk_rekey=600" << std::endl
|
||||
<< "rsn_pairwise=CCMP" << std::endl
|
||||
<< "" << std::endl
|
||||
<< "# Access point configuration" << std::endl
|
||||
<< "ssid=pipedal" << std::endl
|
||||
<< "hw_mode=g" << std::endl
|
||||
<< "channel=8" << std::endl
|
||||
<< "wpa_passphrase=PASSWORD" << std::endl;
|
||||
|
||||
TEST_CASE( "SystemConfigFile Test", "[system_config_file_test]" ) {
|
||||
std::filesystem::path path("/etc/hostapd/hostapd.conf");
|
||||
return s.str();
|
||||
}
|
||||
static std::string GetExpectedFile()
|
||||
{
|
||||
std::stringstream s;
|
||||
s
|
||||
<< "interface=wlan0" << std::endl
|
||||
<< "driver=nl80211" << std::endl
|
||||
<< "country_code=FR" << std::endl
|
||||
<< "" << std::endl
|
||||
<< "# Wi-Fi features" << std::endl
|
||||
<< "ieee80211n=1" << std::endl
|
||||
<< "ieee80211d=1" << std::endl
|
||||
<< "ieee80211ac=1" << std::endl
|
||||
<< "ht_capab=[HT40][SHORT-GI-20][DSSS_CCK-40]" << std::endl
|
||||
<< "" << std::endl
|
||||
<< "# Authentication options" << std::endl
|
||||
<< "wmm_enabled=1" << std::endl
|
||||
<< "auth_algs=1" << std::endl
|
||||
<< "wpa=2" << std::endl
|
||||
<< "wpa_pairwise=TKIP CCMP" << std::endl
|
||||
<< "wpa_ptk_rekey=600" << std::endl
|
||||
<< "rsn_pairwise=CCMP" << std::endl
|
||||
<< "" << std::endl
|
||||
<< "# Access point configuration" << std::endl
|
||||
<< "ssid=newSsid" << std::endl
|
||||
<< "hw_mode=g" << std::endl
|
||||
<< "channel=8" << std::endl
|
||||
<< "wpa_passphrase=PASSWORD" << std::endl
|
||||
<< "" << std::endl
|
||||
<< "# A new feature." << std::endl
|
||||
<< "xx=new_feature" << std::endl;
|
||||
return s.str();
|
||||
}
|
||||
|
||||
SystemConfigFile file(path);
|
||||
std::string driverName;
|
||||
TEST_CASE("SystemConfigFile Test", "[system_config_file_test]")
|
||||
{
|
||||
|
||||
std::stringstream testFile(GetTestFile());
|
||||
|
||||
SystemConfigFile file;
|
||||
file.Load(testFile);
|
||||
|
||||
REQUIRE(file.Get("driver") == "nl80211");
|
||||
|
||||
file.Set("ssid","ssid","Name of the access point");
|
||||
file.Set("xx","new_feature","A new feature.");
|
||||
file.Set("ssid","newSsid");
|
||||
file.Set("country_code","FR");
|
||||
|
||||
|
||||
file.Save(std::cout);
|
||||
file.Set("ssid", "ssid", "Name of the access point");
|
||||
file.Set("xx", "new_feature", "A new feature.");
|
||||
file.Set("ssid", "newSsid");
|
||||
file.Set("country_code", "FR");
|
||||
|
||||
std::stringstream outputFile;
|
||||
file.Save(outputFile);
|
||||
|
||||
std::string stringOutput = outputFile.str();
|
||||
std::cout << stringOutput;
|
||||
|
||||
std::string expected = GetExpectedFile();
|
||||
REQUIRE(stringOutput == expected);
|
||||
}
|
||||
|
||||
|
||||
+17
-2
@@ -162,10 +162,17 @@ private:
|
||||
std::vector<json_member_reference_base<CLASS>* > members_;
|
||||
json_map_impl(const json_map_impl&) { } // disable copy constructor.
|
||||
public:
|
||||
json_map_impl(std::vector<json_member_reference_base<CLASS>*> members)
|
||||
using members_t = std::vector<json_member_reference_base<CLASS>*>;
|
||||
|
||||
json_map_impl(const members_t &members)
|
||||
: members_(members)
|
||||
{
|
||||
|
||||
}
|
||||
json_map_impl(members_t &&members)
|
||||
: members_(std::forward<members_t>(members))
|
||||
{
|
||||
|
||||
}
|
||||
virtual ~json_map_impl()
|
||||
{
|
||||
@@ -185,12 +192,20 @@ public:
|
||||
|
||||
class json_map {
|
||||
public:
|
||||
|
||||
template <typename CLASS> class storage_type : public json_map_impl<CLASS> {
|
||||
public:
|
||||
storage_type(std::vector<json_member_reference_base<CLASS>*> members)
|
||||
|
||||
using members_t = std::vector<json_member_reference_base<CLASS>*>;
|
||||
|
||||
storage_type(const members_t &members)
|
||||
: json_map_impl<CLASS>(members)
|
||||
{
|
||||
}
|
||||
storage_type(members_t &&members)
|
||||
: json_map_impl<CLASS>(std::forward<members_t>(members))
|
||||
{
|
||||
}
|
||||
};
|
||||
template <typename CLASS, typename MEMBER_TYPE>
|
||||
static json_member_reference<CLASS,MEMBER_TYPE> *
|
||||
|
||||
+1
-1
@@ -149,7 +149,7 @@ TEST_CASE( "json read", "[json_read_test]" ) {
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE( "json smart ptrs", "[json_smart_ptrs]" ) {
|
||||
TEST_CASE( "json smart ptrs", "[json_smart_ptrs][Build][Dev]" ) {
|
||||
std::string json =get_json();
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user