better tests

This commit is contained in:
Robin Appelman 2021-02-20 18:12:28 +01:00
commit 90af4a497c
9 changed files with 568 additions and 163 deletions

71
.github/workflows/rust.yml vendored Normal file
View file

@ -0,0 +1,71 @@
on: [push, pull_request]
name: CI
jobs:
check:
name: Check
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- uses: Swatinem/rust-cache@v1
- uses: actions-rs/cargo@v1
with:
command: check
build:
name: Build
runs-on: ubuntu-20.04
strategy:
matrix:
target:
- x86_64-unknown-linux-musl
- armv7-unknown-linux-musleabihf
- aarch64-unknown-linux-musl
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
target: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v1
- uses: actions-rs/cargo@v1
with:
use-cross: true
command: build
args: --release --target ${{ matrix.target }}
- uses: actions/upload-artifact@v2
with:
name: notify-redis-${{ matrix.target }}
path: target/${{ matrix.target }}/release/notify-redis
test:
name: Tests
runs-on: ubuntu-20.04
services:
redis:
image: redis
ports:
- 6379:6379
options: --entrypoint redis-server
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
- uses: Swatinem/rust-cache@v1
- uses: actions-rs/cargo@v1
with:
command: test

View file

@ -1,18 +0,0 @@
sudo: required
services:
- docker
- redis-server
install:
- docker pull ekidd/rust-musl-builder:latest
script:
- "./travis-build.sh notify-redis ${TRAVIS_OS_NAME}"
- "./test.sh ./notify-redis-${TRAVIS_OS_NAME}"
deploy:
provider: releases
api_key:
secure: GBWfTW08qT3K9W9zeVteUJ9RTDnaIguH5Zoe7N+GOTPBGUDscL/3vfCOj5PyfhLgHuPXzlMAGCM42MZFknL5PXFO4ZMFuiTtcSbiSEQNPArQjDrWbCPxU9IrXN7W/94EEcdWmSSpkT/Fc3XRgx9z2BjUSfXCv4R4owwtRWhWlNNnmqVIe7OEfCQEKBCsuGB9PmLTShUd33ztVqVt2+WMC8kT9Wrk9KmeeEANx/K1rz18sMOphd7Ruqx9z1tLF0nFaRGBSRPV4EhpBGLahdglRtq9uvHFA4iMmpf1HWWE/TDVLI/tykE1B39gYfzEircLAJw+D6orO3kPsjqH8gzwwUyZKQr5OmEZUExtdHp/K606Doo3fwdWty3CqZYzDI+p4YwXDL8cI2TShGaQpXVwKXpJK+5RXrsYNY+tBvsQ2BpW4Z5US+uqESUBX4dbdrjZyXKfIJFHRpzn8COa02Hc0nFYf/hqakzTqTylVYTTQaZ4VdgPuU7zUFPHg7rBEPbuv0ncxDOgLjjYLHl00CSSsqiRezJzW2TUEFW6xXI8qz9QTnOAyVM4aBStxF1oAtOia4jcMu7gjIJeeAOZnYSqryW/Xy8YuzRE07axXNk90Uma+Gxn3zcgvGAdwct2jq+iPQ8yvqirCJFNMxfu8pDTYbLZcFNZEvOfLVQv9OmPNqE=
file: notify-redis-linux
skip_cleanup: true
on:
repo: icewind1991/notify-redis
tags: true

246
Cargo.lock generated
View file

@ -194,12 +194,32 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
[[package]]
name = "getrandom"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
dependencies = [
"cfg-if 1.0.0",
"libc",
"wasi",
]
[[package]] [[package]]
name = "gimli" name = "gimli"
version = "0.23.0" version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce"
[[package]]
name = "hermit-abi"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "idna" name = "idna"
version = "0.2.2" version = "0.2.2"
@ -237,6 +257,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "instant"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
dependencies = [
"cfg-if 1.0.0",
]
[[package]] [[package]]
name = "iovec" name = "iovec"
version = "0.1.4" version = "0.1.4"
@ -280,6 +309,15 @@ version = "0.2.86"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c" checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c"
[[package]]
name = "lock_api"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312"
dependencies = [
"scopeguard",
]
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.14" version = "0.4.14"
@ -324,12 +362,25 @@ dependencies = [
"kernel32-sys", "kernel32-sys",
"libc", "libc",
"log", "log",
"miow", "miow 0.2.2",
"net2", "net2",
"slab", "slab",
"winapi 0.2.8", "winapi 0.2.8",
] ]
[[package]]
name = "mio"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc250d6848c90d719ea2ce34546fb5df7af1d3fd189d10bf7bad80bfcebecd95"
dependencies = [
"libc",
"log",
"miow 0.3.6",
"ntapi",
"winapi 0.3.9",
]
[[package]] [[package]]
name = "mio-extras" name = "mio-extras"
version = "2.0.6" version = "2.0.6"
@ -338,7 +389,7 @@ checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19"
dependencies = [ dependencies = [
"lazycell", "lazycell",
"log", "log",
"mio", "mio 0.6.23",
"slab", "slab",
] ]
@ -354,6 +405,16 @@ dependencies = [
"ws2_32-sys", "ws2_32-sys",
] ]
[[package]]
name = "miow"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897"
dependencies = [
"socket2",
"winapi 0.3.9",
]
[[package]] [[package]]
name = "net2" name = "net2"
version = "0.2.37" version = "0.2.37"
@ -377,7 +438,7 @@ dependencies = [
"fsevent-sys", "fsevent-sys",
"inotify", "inotify",
"libc", "libc",
"mio", "mio 0.6.23",
"mio-extras", "mio-extras",
"walkdir", "walkdir",
"winapi 0.3.9", "winapi 0.3.9",
@ -390,9 +451,21 @@ dependencies = [
"chrono", "chrono",
"color-eyre", "color-eyre",
"notify", "notify",
"rand",
"redis", "redis",
"serde", "serde",
"serde_json", "serde_json",
"tempfile",
"tokio",
]
[[package]]
name = "ntapi"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
dependencies = [
"winapi 0.3.9",
] ]
[[package]] [[package]]
@ -414,6 +487,16 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "num_cpus"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
dependencies = [
"hermit-abi",
"libc",
]
[[package]] [[package]]
name = "object" name = "object"
version = "0.23.0" version = "0.23.0"
@ -432,6 +515,31 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2386b4ebe91c2f7f51082d4cefa145d030e33a1842a96b12e4885cc3c01f7a55" checksum = "2386b4ebe91c2f7f51082d4cefa145d030e33a1842a96b12e4885cc3c01f7a55"
[[package]]
name = "parking_lot"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
dependencies = [
"instant",
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
dependencies = [
"cfg-if 1.0.0",
"instant",
"libc",
"redox_syscall",
"smallvec",
"winapi 0.3.9",
]
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.1.0" version = "2.1.0"
@ -444,6 +552,12 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827" checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827"
[[package]]
name = "ppv-lite86"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.24" version = "1.0.24"
@ -462,6 +576,46 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "rand"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
name = "rand_chacha"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_hc"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
dependencies = [
"rand_core",
]
[[package]] [[package]]
name = "redis" name = "redis"
version = "0.20.0" version = "0.20.0"
@ -486,6 +640,15 @@ dependencies = [
"bitflags", "bitflags",
] ]
[[package]]
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi 0.3.9",
]
[[package]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.18" version = "0.1.18"
@ -507,6 +670,12 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.123" version = "1.0.123"
@ -553,12 +722,38 @@ dependencies = [
"lazy_static", "lazy_static",
] ]
[[package]]
name = "signal-hook-registry"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "slab" name = "slab"
version = "0.4.2" version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
[[package]]
name = "smallvec"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
[[package]]
name = "socket2"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e"
dependencies = [
"cfg-if 1.0.0",
"libc",
"winapi 0.3.9",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.60" version = "1.0.60"
@ -570,6 +765,20 @@ dependencies = [
"unicode-xid", "unicode-xid",
] ]
[[package]]
name = "tempfile"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
dependencies = [
"cfg-if 1.0.0",
"libc",
"rand",
"redox_syscall",
"remove_dir_all",
"winapi 0.3.9",
]
[[package]] [[package]]
name = "thread_local" name = "thread_local"
version = "1.1.3" version = "1.1.3"
@ -605,6 +814,37 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8190d04c665ea9e6b6a0dc45523ade572c088d2e6566244c1122671dbf4ae3a"
dependencies = [
"autocfg",
"bytes",
"libc",
"memchr",
"mio 0.7.8",
"num_cpus",
"once_cell",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"tokio-macros",
"winapi 0.3.9",
]
[[package]]
name = "tokio-macros"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "tracing" name = "tracing"
version = "0.1.24" version = "0.1.24"

View file

@ -11,3 +11,8 @@ chrono = { version = "0.4", features = ["serde"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
color-eyre = "0.5" color-eyre = "0.5"
[dev-dependencies]
rand = "0.8.3"
tokio = { version = "1.2", features = ["full"] }
tempfile = "3"

83
src/lib.rs Normal file
View file

@ -0,0 +1,83 @@
use chrono::{DateTime, Timelike, Utc};
use color_eyre::{eyre::WrapErr, Result};
use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher};
use redis::{Client, Commands, Connection, IntoConnectionInfo};
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use std::sync::mpsc::channel;
use std::time::Duration;
#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "event")]
#[serde(rename_all = "snake_case")]
pub enum Event {
Modify {
path: PathBuf,
time: DateTime<Utc>,
},
Move {
from: PathBuf,
to: PathBuf,
time: DateTime<Utc>,
},
Delete {
path: PathBuf,
time: DateTime<Utc>,
},
None,
}
impl From<DebouncedEvent> for Event {
fn from(event: DebouncedEvent) -> Self {
let time = Utc::now().with_nanosecond(0).unwrap();
match event {
DebouncedEvent::Write(path)
| DebouncedEvent::Create(path)
| DebouncedEvent::Chmod(path) => Event::Modify { path, time },
DebouncedEvent::Rename(from, to) => Event::Move { from, to, time },
DebouncedEvent::Remove(path) => Event::Delete { path, time },
_ => Event::None,
}
}
}
pub fn watch(
path: impl AsRef<Path>,
redis_connect: impl IntoConnectionInfo,
redis_list: &str,
debounce: Duration,
) -> Result<()> {
let (tx, rx) = channel();
let mut watcher: RecommendedWatcher = Watcher::new(tx, debounce)?;
let client = Client::open(redis_connect).wrap_err("Invalid redis connection")?;
let mut con = client
.get_connection()
.wrap_err("Failed to open redis connection")?;
watcher.watch(path, RecursiveMode::Recursive)?;
while let Ok(event) = rx.recv() {
push_event(event, &mut con, redis_list).wrap_err("Failed to send event to redis")?;
}
Ok(())
}
fn push_event(event: DebouncedEvent, con: &mut Connection, list: &str) -> Result<()> {
match format_event(event) {
Some(formatted_event) => {
println!("{}", formatted_event);
Ok(con.lpush(list, formatted_event)?)
}
None => Ok(()),
}
}
fn format_event(event: DebouncedEvent) -> Option<String> {
let event: Event = event.into();
match &event {
Event::None => None,
_ => serde_json::to_string(&event).ok(),
}
}

View file

@ -1,87 +1,12 @@
use chrono::{DateTime, Timelike, Utc}; use color_eyre::Result;
use color_eyre::{eyre::WrapErr, Result}; use notify_redis::watch;
use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher};
use redis::{Client, Commands, Connection};
use serde::Serialize;
use std::env; use std::env;
use std::path::PathBuf;
use std::sync::mpsc::channel;
use std::time::Duration; use std::time::Duration;
#[derive(Serialize, Debug)]
#[serde(tag = "event")]
#[serde(rename_all = "snake_case")]
enum Event {
Modify {
path: PathBuf,
time: DateTime<Utc>,
},
Move {
from: PathBuf,
to: PathBuf,
time: DateTime<Utc>,
},
Delete {
path: PathBuf,
time: DateTime<Utc>,
},
None,
}
impl From<DebouncedEvent> for Event {
fn from(event: DebouncedEvent) -> Self {
let time = Utc::now().with_nanosecond(0).unwrap();
match event {
DebouncedEvent::Write(path)
| DebouncedEvent::Create(path)
| DebouncedEvent::Chmod(path) => Event::Modify { path, time },
DebouncedEvent::Rename(from, to) => Event::Move { from, to, time },
DebouncedEvent::Remove(path) => Event::Delete { path, time },
_ => Event::None,
}
}
}
fn watch(path: &str, redis_connect: &str, redis_list: &str) -> Result<()> {
let (tx, rx) = channel();
let mut watcher: RecommendedWatcher = Watcher::new(tx, Duration::from_secs(2))?;
let client = Client::open(redis_connect).wrap_err("Invalid redis connection")?;
let mut con = client
.get_connection()
.wrap_err("Failed to open redis connection")?;
watcher.watch(path, RecursiveMode::Recursive)?;
while let Ok(event) = rx.recv() {
push_event(event, &mut con, redis_list).wrap_err("Failed to send event to redis")?;
}
Ok(())
}
fn push_event(event: DebouncedEvent, con: &mut Connection, list: &str) -> Result<()> {
match format_event(event) {
Some(formatted_event) => {
println!("{}", formatted_event);
Ok(con.lpush(list, formatted_event)?)
}
None => Ok(()),
}
}
fn format_event(event: DebouncedEvent) -> Option<String> {
let event: Event = event.into();
match &event {
Event::None => None,
_ => serde_json::to_string(&event).ok(),
}
}
fn main() -> Result<()> { fn main() -> Result<()> {
let args: Vec<_> = env::args().collect(); let args: Vec<_> = env::args().collect();
if let [_, path, redis, list] = args.as_slice() { if let [_, path, redis, list] = args.as_slice() {
watch(path, redis, list)?; watch(path, redis.as_str(), list, Duration::from_secs(2))?;
} else { } else {
println!("usage: {} <path> <redis_connect> <redis_list>", args[0]) println!("usage: {} <path> <redis_connect> <redis_list>", args[0])
} }

56
test.sh
View file

@ -1,56 +0,0 @@
#!/bin/bash
function assert_notify {
actual="$(echo $(redis-cli rpop notify) | sed -e 's/,\"time\":[^Z]*Z\"//g')" # strip time
if [ "$actual" != "$1" ]
then
echo "'$actual' not equal to expected '$1'"
killall $(basename $bin)
exit 1
fi
}
mkdir -p test
rm test/*
redis-cli del notify
bin=$1;
: ${bin:="target/x86_64-unknown-linux-musl/release/notify-redis"}
echo "running tests with $bin"
$bin "$PWD/test" redis://localhost notify &
sleep 1
echo foo > test/foo.txt
sleep 2
assert_notify "{\"event\":\"modify\",\"path\":\"$(pwd)/test/foo.txt\"}"
assert_notify ''
mv test/foo.txt test/bar.txt
sleep 2
assert_notify "{\"event\":\"move\",\"from\":\"$(pwd)/test/foo.txt\",\"to\":\"$(pwd)/test/bar.txt\"}"
assert_notify ""
rm test/bar.txt
echo asd > test/bar.txt
sleep 2
assert_notify "{\"event\":\"modify\",\"path\":\"$(pwd)/test/bar.txt\"}"
assert_notify ""
rm test/bar.txt
sleep 2
echo asd > test/bar.txt
sleep 2
assert_notify "{\"event\":\"delete\",\"path\":\"$(pwd)/test/bar.txt\"}"
assert_notify "{\"event\":\"modify\",\"path\":\"$(pwd)/test/bar.txt\"}"
assert_notify ""
killall $(basename $bin)

162
tests/tests.rs Normal file
View file

@ -0,0 +1,162 @@
use notify_redis::{watch, Event};
use redis::{Client, Commands, Connection, ConnectionInfo};
use std::fs::{remove_file, rename, write};
use std::path::Path;
use std::thread::sleep;
use std::time::Duration;
use tempfile::tempdir;
fn cleanup(redis: ConnectionInfo, list: &str) {
let client = Client::open(redis).unwrap();
let mut con = client.get_connection().unwrap();
con.del::<_, ()>(list).unwrap();
}
struct EventList {
redis: Connection,
list: String,
}
impl EventList {
fn new(redis: ConnectionInfo, list: &str) -> Self {
let client = Client::open(redis).unwrap();
let redis = client.get_connection().unwrap();
EventList {
redis,
list: list.into(),
}
}
fn next(&mut self) -> Option<Event> {
let raw: Option<String> = self.redis.rpop(&self.list).unwrap();
raw.map(|raw| serde_json::from_str(&raw).unwrap())
}
}
fn spawn_watch(
path: &Path,
redis_connect: ConnectionInfo,
list: &str,
) -> std::thread::JoinHandle<()> {
let path = path.to_path_buf();
let list = list.to_string();
std::thread::spawn(move || {
if let Err(e) = watch(path, redis_connect, &list, Duration::from_millis(1)) {
eprintln!("watch error {:#}", e);
}
})
}
#[test]
fn test_basic() {
let list = format!("notify_redis_test_{}", rand::random::<u16>());
let redis_connect: ConnectionInfo = "redis://localhost".parse().unwrap();
cleanup(redis_connect.clone(), &list);
let dir = tempdir().unwrap();
let mut event_list = EventList::new(redis_connect.clone(), &list);
spawn_watch(dir.path(), redis_connect.clone(), &list);
sleep(Duration::from_millis(10));
write(dir.path().join("foo.txt"), "foo").unwrap();
sleep(Duration::from_millis(10));
assert!(
matches!(event_list.next(), Some(Event::Modify {path ,..}) if path.ends_with("foo.txt"))
);
assert!(matches!(event_list.next(), None));
}
#[test]
fn test_rename_debounce() {
let list = format!("notify_redis_test_{}", rand::random::<u16>());
let redis_connect: ConnectionInfo = "redis://localhost".parse().unwrap();
cleanup(redis_connect.clone(), &list);
let dir = tempdir().unwrap();
let mut event_list = EventList::new(redis_connect.clone(), &list);
spawn_watch(dir.path(), redis_connect.clone(), &list);
sleep(Duration::from_millis(10));
write(dir.path().join("foo.txt"), "foo").unwrap();
rename(dir.path().join("foo.txt"), dir.path().join("bar.txt")).unwrap();
sleep(Duration::from_millis(10));
assert!(
matches!(event_list.next(), Some(Event::Modify {path ,..}) if path.ends_with("bar.txt"))
);
assert!(matches!(event_list.next(), None));
}
#[test]
fn test_rename() {
let list = format!("notify_redis_test_{}", rand::random::<u16>());
let redis_connect: ConnectionInfo = "redis://localhost".parse().unwrap();
cleanup(redis_connect.clone(), &list);
let dir = tempdir().unwrap();
let mut event_list = EventList::new(redis_connect.clone(), &list);
spawn_watch(dir.path(), redis_connect.clone(), &list);
sleep(Duration::from_millis(10));
write(dir.path().join("foo.txt"), "foo").unwrap();
sleep(Duration::from_millis(10));
rename(dir.path().join("foo.txt"), dir.path().join("bar.txt")).unwrap();
sleep(Duration::from_millis(10));
assert!(
matches!(event_list.next(), Some(Event::Modify {path ,..}) if path.ends_with("foo.txt"))
);
assert!(
matches!(event_list.next(), Some(Event::Move {from, to ,..}) if from.ends_with("foo.txt") && to.ends_with("bar.txt"))
);
assert!(matches!(dbg!(event_list.next()), None));
}
#[test]
fn test_delete() {
let list = format!("notify_redis_test_{}", rand::random::<u16>());
let redis_connect: ConnectionInfo = "redis://localhost".parse().unwrap();
cleanup(redis_connect.clone(), &list);
let dir = tempdir().unwrap();
let mut event_list = EventList::new(redis_connect.clone(), &list);
spawn_watch(dir.path(), redis_connect.clone(), &list);
sleep(Duration::from_millis(10));
write(dir.path().join("foo.txt"), "foo").unwrap();
sleep(Duration::from_millis(10));
remove_file(dir.path().join("foo.txt")).unwrap();
sleep(Duration::from_millis(10));
assert!(
matches!(event_list.next(), Some(Event::Modify {path ,..}) if path.ends_with("foo.txt"))
);
assert!(
matches!(event_list.next(), Some(Event::Delete {path ,..}) if path.ends_with("foo.txt"))
);
assert!(matches!(dbg!(event_list.next()), None));
}
#[test]
fn test_delete_debounce() {
let list = format!("notify_redis_test_{}", rand::random::<u16>());
let redis_connect: ConnectionInfo = "redis://localhost".parse().unwrap();
cleanup(redis_connect.clone(), &list);
let dir = tempdir().unwrap();
let mut event_list = EventList::new(redis_connect.clone(), &list);
spawn_watch(dir.path(), redis_connect.clone(), &list);
sleep(Duration::from_millis(10));
write(dir.path().join("foo.txt"), "foo").unwrap();
remove_file(dir.path().join("foo.txt")).unwrap();
sleep(Duration::from_millis(10));
assert!(matches!(dbg!(event_list.next()), None));
}

View file

@ -1,7 +0,0 @@
#!/bin/sh
echo "Building static binaries using ekidd/rust-musl-builder"
docker build -t build-"$1"-image .
docker run -it --name build-"$1" build-"$1"-image
docker cp build-"$1":/home/rust/src/target/x86_64-unknown-linux-musl/release/"$1" "$1-$2"
docker rm build-"$1"
docker rmi build-"$1"-image