-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Python testing framework to support Integration testing of the bl…
…oom module + Add basic sanity tests Signed-off-by: Karthik Subbarao <[email protected]>
- Loading branch information
1 parent
0d2ad8a
commit 85e20fd
Showing
7 changed files
with
695 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,6 @@ | ||
Cargo.lock | ||
target | ||
tests/.build | ||
__pycache__ | ||
test-data | ||
.attach_pid* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
#!/usr/bin/env sh | ||
|
||
# Script to run format checks valkey-bloom module, build it and generate .so files, run unit and integration tests. | ||
|
||
# Exit the script if any command fails | ||
set -e | ||
|
||
SCRIPT_DIR=$(pwd) | ||
echo "Script Directory: $SCRIPT_DIR" | ||
|
||
echo "Running cargo and clippy format checks..." | ||
cargo fmt --check | ||
cargo clippy --profile release --all-targets -- -D clippy::all | ||
|
||
echo "Running cargo build release..." | ||
cargo build --all --all-targets --release | ||
|
||
# We have waiting on a new feature in the valkey-module-rs to be released which will allow unit testing of Valkey Rust Modules. | ||
# echo "Running unit tests..." | ||
# cargo test | ||
|
||
# Ensure VERSION environment variable is set | ||
if [ -z "$VERSION" ]; then | ||
This comment has been minimized.
Sorry, something went wrong. |
||
echo "ERROR: VERSION environment variable is not set." | ||
exit 1 | ||
fi | ||
|
||
if [ "$VERSION" != "unstable" ] && [ "$VERSION" != "7.2.6" ] && [ "$VERSION" != "7.2.5" ] ; then | ||
echo "ERROR: Unsupported version - $VERSIOn" | ||
exit 1 | ||
fi | ||
|
||
REPO_URL="https://github.com/valkey-io/valkey.git" | ||
BINARY_PATH="tests/.build/binaries/$VERSION/valkey-server" | ||
|
||
if [ -f "$BINARY_PATH" ] && [ -x "$BINARY_PATH" ]; then | ||
echo "valkey-server binary '$BINARY_PATH' found." | ||
else | ||
echo "valkey-server binary '$BINARY_PATH' not found." | ||
mkdir -p "tests/.build/binaries/$VERSION" | ||
cd tests/.build | ||
rm -rf valkey | ||
git clone "$REPO_URL" | ||
cd valkey | ||
git checkout "$VERSION" | ||
make | ||
cp src/valkey-server ../binaries/$VERSION/ | ||
fi | ||
|
||
REQUIREMENTS_FILE="requirements.txt" | ||
|
||
# Check if pip is available | ||
if command -v pip > /dev/null 2>&1; then | ||
echo "Using pip to install packages..." | ||
pip install -r "$SCRIPT_DIR/$REQUIREMENTS_FILE" | ||
# Check if pip3 is available | ||
elif command -v pip3 > /dev/null 2>&1; then | ||
echo "Using pip3 to install packages..." | ||
pip3 install -r "$SCRIPT_DIR/$REQUIREMENTS_FILE" | ||
else | ||
echo "Error: Neither pip nor pip3 is available. Please install Python package installer." | ||
exit 1 | ||
fi | ||
|
||
export MODULE_PATH="$SCRIPT_DIR/target/release/libvalkey_bloom.so" | ||
|
||
echo "Running the integration tests..." | ||
python3 -m pytest --cache-clear -v "$SCRIPT_DIR/tests/" | ||
|
||
echo "Build and Integration Tests succeeded" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
valkey | ||
pytest==4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import time | ||
import pytest | ||
from valkey import ResponseError | ||
from valkey_test_case import ValkeyTestCase, ValkeyServerVersion | ||
import logging | ||
import os | ||
|
||
class TestBloomBasic(ValkeyTestCase): | ||
|
||
def get_custom_args(self): | ||
self.set_server_version(ValkeyServerVersion.LATEST) | ||
return { | ||
'loadmodule': os.getenv('MODULE_PATH'), | ||
} | ||
|
||
def test_basic(self): | ||
client = self.server.get_new_client() | ||
module_list_data = client.execute_command('MODULE LIST') | ||
module_list_count = len(module_list_data) | ||
assert module_list_count == 1 | ||
module_loaded = False | ||
for module in module_list_data: | ||
if (module[b'name'] == b'bf'): | ||
module_loaded = True | ||
break | ||
assert(module_loaded) | ||
bf_add_result = client.execute_command('BF.ADD filter1 item1') | ||
assert bf_add_result == 1 | ||
bf_exists_result = client.execute_command('BF.EXISTS filter1 item1') | ||
assert bf_exists_result == 1 | ||
bf_exists_result = client.execute_command('BF.EXISTS filter1 item2') | ||
assert bf_exists_result == 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
""" | ||
This module is loaded for all tests, and attaches a global port tracker to every test. This solves | ||
the case where there are parallel parameters. | ||
""" | ||
|
||
import pytest | ||
import fcntl | ||
import socket | ||
import os | ||
import tempfile | ||
import random | ||
import subprocess | ||
import threading | ||
from pathlib import Path | ||
|
||
class PortTracker(object): | ||
""" Provides "safe" to bind ports to valkey-server | ||
Ports allocation is file base is protected. A port that was obtained via | ||
`get_unused_port` will not be allocated to any other process. Ports are | ||
de-allocated (protection removed) upon PortTracker exit, even if the socket | ||
of the port in question was closed. | ||
""" | ||
|
||
CLUSTER_BUS_PORT_OFFSET = 10000 | ||
MIN_PORT = 5000 | ||
MAX_PORT = 32768 # this is the lower ephemeral port range | ||
MAX_BASE_PORT = MAX_PORT - CLUSTER_BUS_PORT_OFFSET - MIN_PORT | ||
MAX_RETRIES = 100 | ||
LOCKS_DIR = os.path.join(tempfile.gettempdir(), "port_tracker") | ||
|
||
|
||
def __init__(self, node_id): | ||
self._hash = hash(str(node_id)) | ||
if not os.path.exists(Path(PortTracker.LOCKS_DIR)): | ||
Path(PortTracker.LOCKS_DIR).mkdir(parents=True, exist_ok=True) | ||
|
||
def __enter__(self): | ||
self.open_and_locked_files = {} | ||
return self | ||
|
||
def __exit__(self, type, value, tb): | ||
for lockfile in self.open_and_locked_files.values(): | ||
self._try_remove(lockfile) | ||
|
||
def _try_remove(self, lockfile): | ||
lockfile.close() | ||
try: | ||
os.remove(lockfile.name) | ||
except: | ||
pass | ||
|
||
def _next_port(self): | ||
self._hash = hash(str(self._hash)) | ||
return (self._hash % PortTracker.MAX_BASE_PORT) + PortTracker.MIN_PORT | ||
|
||
def _try_lock_port(self, port): | ||
# get a lock on a file | ||
lockfilename = os.path.join(self.LOCKS_DIR, "port%d.lock" % port) | ||
lockfile = open(lockfilename, "w") | ||
try: | ||
fcntl.flock(lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB) | ||
except OSError: | ||
self._try_remove(lockfile) | ||
return False | ||
# test that the valkey server would be able to bind to this port | ||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: | ||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | ||
try: | ||
sock.bind(('0.0.0.0', port)) | ||
except OSError: | ||
lockfile.close() | ||
return False | ||
|
||
self.open_and_locked_files[port] = lockfile | ||
return True | ||
|
||
def _unlock_port(self, port): | ||
lockfile = self.open_and_locked_files.get(port) | ||
if lockfile: | ||
lockfile.close() | ||
del self.open_and_locked_files[port] | ||
|
||
def get_unused_port(self): | ||
for r in range(PortTracker.MAX_RETRIES): | ||
port = self._next_port() | ||
if not self._try_lock_port(port): | ||
continue | ||
if not self._try_lock_port(port + PortTracker.CLUSTER_BUS_PORT_OFFSET): | ||
self._unlock_port(port) | ||
continue | ||
return port | ||
assert False , "Failed to find port after %d tries" % PortTracker.MAX_RETRIES | ||
|
||
@pytest.fixture(scope='function', autouse=True) | ||
def resource_port_tracker(request): | ||
''' | ||
Create port tracker for each pytest worker. | ||
''' | ||
with PortTracker(request.node.nodeid) as p: | ||
yield p |
Oops, something went wrong.
How did the VERSION get detected? Do we need to pass any parameter to this script? Or do I need to have a valkey-server running on the background?