Skip to content

Commit

Permalink
Add many more unit tests, a few more integration tests & pubsub tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rpj committed Aug 17, 2023
1 parent 785e501 commit 8a80bed
Show file tree
Hide file tree
Showing 15 changed files with 560 additions and 235 deletions.
16 changes: 11 additions & 5 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,15 @@ jobs:
- uses: actions/checkout@v3
- name: Submodule init
run: git submodule init && git submodule update --recursive
- name: Install redis
run: |
sudo apt -y install redis && \
sudo systemctl start redis
- name: Build & run tests
- name: Add packages.redis.io key
run: curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
- name: Install packages.redis.io key
run: echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/redis.list
- name: Install depedencies
run: sudo apt -y update && sudo apt -y install redis parallel
- name: Redis version check
run: redis-cli info | grep redis_version
- name: Build tests
run: cd test && make
- name: Run tests
run: cd test && make run
4 changes: 2 additions & 2 deletions Redis.h
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ class Redis
/** Returns the index of the first matched `element` when scanning from head to tail
* @param key
* @param element
* @return The index of the first element, or nil when key does not exist.
* @return The index of the first element, or (INT_MAX - 0x0f) when key does not exist.
*/
int lpos(const char *key, const char *element);

Expand Down Expand Up @@ -609,7 +609,7 @@ class Redis
* @param returnString
* @returns true if returnString is the nil return value, false otherwise
*/
bool isNilReturn(String returnString) { return returnString == "(nil)"; }
static bool isNilReturn(String returnString) { return returnString == "(nil)"; }

/**
* Enters subscription mode and subscribes to all channels/patterns setup via `subscribe()`/`psubscribe()`.
Expand Down
8 changes: 8 additions & 0 deletions RedisInternal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,16 @@ String RedisBulkString::RESP()

void RedisArray::init(Client &client)
{
// Null array https://redis.io/docs/reference/protocol-spec/#null-arrays
if (data.toInt() == -1)
{
return;
}

for (int i = 0; i < data.toInt(); i++)
{
add(RedisObject::parseType(client));
}
}

RedisArray::operator std::vector<std::shared_ptr<RedisObject>>() const
Expand Down
5 changes: 5 additions & 0 deletions RedisInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ class RedisArray : public RedisObject

operator std::vector<std::shared_ptr<RedisObject>>() const;

/** Returns false if this is a "Null Array" (https://redis.io/docs/reference/protocol-spec/#null-arrays),
* true otherwise (including if the array is empty!)
*/
bool isNilReturn() const { return data.toInt() == -1; }

virtual void init(Client &client) override;

virtual String RESP() override;
Expand Down
17 changes: 17 additions & 0 deletions test/ArduinoRedisTestBase.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#define ArduinoRedisTestCommonSetupAndLoop \
void setup() \
{ \
const char *include = std::getenv("ARDUINO_REDIS_TEST_INCLUDE"); \
\
if (!include) \
{ \
include = "*"; \
} \
\
TestRunner::include(include); \
} \
\
void loop() \
{ \
TestRunner::run(); \
}
89 changes: 89 additions & 0 deletions test/IntegrationTestBase.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#include <Redis.h>
#include <RedisInternal.h>

#include <AUnitVerbose.h>

#include <memory>

#include "./TestRawClient.h"

const String gKeyPrefix = String("__arduino_redis__test");

#define prefixKey(k) (String(gKeyPrefix + "." + k))
#define prefixKeyCStr(k) (String(gKeyPrefix + "." + k).c_str())

#define defineKey(KEY) \
auto __ks = prefixKey(KEY); \
auto key = __ks.c_str();

std::pair<std::shared_ptr<TestRawClient>, std::shared_ptr<Redis>> NewConnection()
{
std::pair<std::shared_ptr<TestRawClient>, std::shared_ptr<Redis>> ret_val = std::make_pair(nullptr, nullptr);

const char *r_host = std::getenv("ARDUINO_REDIS_TEST_HOST");
if (!r_host)
{
r_host = "localhost";
}

const char *r_port = std::getenv("ARDUINO_REDIS_TEST_PORT");
if (!r_port)
{
r_port = "6379";
}

auto r_auth = std::getenv("ARDUINO_REDIS_TEST_AUTH");

auto client = std::make_shared<TestRawClient>();
if (client->connect(r_host, std::atoi(r_port)) == 0)
{
return ret_val;
}

Client &c_ref = *client.get();
auto r = std::make_shared<Redis>(c_ref);
if (r_auth)
{
auto auth_ret = r->authenticate(r_auth);
if (auth_ret == RedisReturnValue::RedisAuthFailure)
{
return ret_val;
}
}

return std::make_pair(client, r);
}

// Any testF(IntegrationTests, ...) defined will automatically have scope access to `r`, the redis client
class IntegrationTests : public aunit::TestOnce
{
protected:
void setup() override
{
aunit::TestOnce::setup();
auto conn_ret = NewConnection();
assertNotEqual(conn_ret.first.get(), nullptr);
assertNotEqual(conn_ret.second.get(), nullptr);
client = std::move(conn_ret.first);
r = std::move(conn_ret.second);
}

void teardown() override
{
auto keys = RedisCommand("KEYS", ArgList{String(gKeyPrefix + "*").c_str()}).issue(*client);

if (keys->type() == RedisObject::Type::Array)
{
std::vector<String> as_strings = *dynamic_cast<RedisArray *>(keys.get());
for (const auto &key : as_strings)
{
r->del(key.c_str());
}
}

aunit::TestOnce::teardown();
}

std::shared_ptr<TestRawClient> client;
std::shared_ptr<Redis> r;
};
21 changes: 15 additions & 6 deletions test/Makefile
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
TESTS = unit/unit-tests.out integration/integration-tests.out
TESTS = unit/unit-tests.out integration/integration-tests.out pubsub/subscriber/subscriber-tests.out pubsub/publisher/publisher-tests.out

test : $(TESTS)

unit/unit-tests.out:
unit/unit-tests.out: unit/unit-tests.ino ../Redis.h ../Redis.cpp ../RedisInternal.h ../RedisInternal.cpp
cd unit && make

integration/integration-tests.out:
integration/integration-tests.out: integration/integration-tests.ino ../Redis.h ../Redis.cpp ../RedisInternal.h ../RedisInternal.cpp
cd integration && make

run: clean test
pubsub/subscriber/subscriber-tests.out: pubsub/subscriber/subscriber-tests.ino ../Redis.h ../Redis.cpp ../RedisInternal.h ../RedisInternal.cpp
cd pubsub && make

pubsub/publisher/publisher-tests.out: pubsub/publisher/publisher-tests.ino ../Redis.h ../Redis.cpp ../RedisInternal.h ../RedisInternal.cpp
cd pubsub && make

run: test pubsub
cd pubsub && make run
./unit/unit-tests.out
./integration/integration-tests.out

clean:
rm -f unit/unit-tests.out
rm -f integration/integration-tests.out
rm -f ../*.o
cd unit && make clean
cd integration && make clean
cd pubsub && make clean
Loading

0 comments on commit 8a80bed

Please sign in to comment.