first pass of merging in warp (doesn't build)
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
use std::io::Read;
|
||||
|
||||
use super::*;
|
||||
|
||||
fn read(path: &Path) -> String {
|
||||
let mut s = String::new();
|
||||
File::open(path).unwrap().read_to_string(&mut s).unwrap();
|
||||
s
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn writes_below_threshold_do_not_rotate() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let mut w = RotatingFileWriter::open(tmp.path(), "warp.log", 1024, 3).unwrap();
|
||||
w.write_all(b"hello world\n").unwrap();
|
||||
w.flush().unwrap();
|
||||
assert_eq!(read(&tmp.path().join("warp.log")), "hello world\n");
|
||||
assert!(!tmp.path().join("warp.log.in_session.0").exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn crossing_threshold_rotates_to_in_session_zero() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let mut w = RotatingFileWriter::open(tmp.path(), "warp.log", 16, 3).unwrap();
|
||||
w.write_all(b"first batch ").unwrap(); // 12 bytes
|
||||
w.write_all(b"more content").unwrap(); // crosses 16 → rotate before write
|
||||
w.flush().unwrap();
|
||||
assert_eq!(
|
||||
read(&tmp.path().join("warp.log.in_session.0")),
|
||||
"first batch "
|
||||
);
|
||||
assert_eq!(read(&tmp.path().join("warp.log")), "more content");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repeated_rotations_shift_slots_up() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let mut w = RotatingFileWriter::open(tmp.path(), "warp.log", 8, 3).unwrap();
|
||||
// Each write of ~10 bytes crosses the 8-byte threshold and triggers
|
||||
// a rotation before the write lands. So the *previous* batch becomes
|
||||
// .in_session.0 each time, shifting older slots up.
|
||||
w.write_all(b"aaaaaaaaa\n").unwrap(); // first write — no prior content, becomes active
|
||||
w.write_all(b"bbbbbbbbb\n").unwrap(); // rotates "aaa..." -> .0
|
||||
w.write_all(b"ccccccccc\n").unwrap(); // rotates "bbb..." -> .0, "aaa..." -> .1
|
||||
w.write_all(b"ddddddddd\n").unwrap(); // rotates "ccc..." -> .0, "bbb..." -> .1, "aaa..." -> .2
|
||||
w.flush().unwrap();
|
||||
assert_eq!(read(&tmp.path().join("warp.log")), "ddddddddd\n");
|
||||
assert_eq!(
|
||||
read(&tmp.path().join("warp.log.in_session.0")),
|
||||
"ccccccccc\n"
|
||||
);
|
||||
assert_eq!(
|
||||
read(&tmp.path().join("warp.log.in_session.1")),
|
||||
"bbbbbbbbb\n"
|
||||
);
|
||||
assert_eq!(
|
||||
read(&tmp.path().join("warp.log.in_session.2")),
|
||||
"aaaaaaaaa\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overflow_drops_the_oldest_slot() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let mut w = RotatingFileWriter::open(tmp.path(), "warp.log", 8, 2).unwrap();
|
||||
w.write_all(b"aaaaaaaaa\n").unwrap();
|
||||
w.write_all(b"bbbbbbbbb\n").unwrap(); // rotates -> .0 = aaa
|
||||
w.write_all(b"ccccccccc\n").unwrap(); // rotates -> .0 = bbb, .1 = aaa
|
||||
w.write_all(b"ddddddddd\n").unwrap(); // rotates -> .0 = ccc, .1 = bbb, aaa dropped
|
||||
w.flush().unwrap();
|
||||
assert_eq!(read(&tmp.path().join("warp.log")), "ddddddddd\n");
|
||||
assert_eq!(
|
||||
read(&tmp.path().join("warp.log.in_session.0")),
|
||||
"ccccccccc\n"
|
||||
);
|
||||
assert_eq!(
|
||||
read(&tmp.path().join("warp.log.in_session.1")),
|
||||
"bbbbbbbbb\n"
|
||||
);
|
||||
assert!(!tmp.path().join("warp.log.in_session.2").exists());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn oversized_first_write_does_not_promote_empty_file_to_in_session_zero() {
|
||||
// Regression for the Oz nit on #11000: when the very first write
|
||||
// exceeds `max_bytes`, the rotator must NOT rename an empty active
|
||||
// file into `.in_session.0` — that would burn a retention slot
|
||||
// before any useful data exists. The oversized payload stays in the
|
||||
// active file and is promoted on the next real rotation.
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let mut w = RotatingFileWriter::open(tmp.path(), "warp.log", 8, 3).unwrap();
|
||||
w.write_all(b"oversized first payload\n").unwrap(); // 24 bytes, > 8
|
||||
w.flush().unwrap();
|
||||
assert!(!tmp.path().join("warp.log.in_session.0").exists());
|
||||
assert_eq!(
|
||||
read(&tmp.path().join("warp.log")),
|
||||
"oversized first payload\n"
|
||||
);
|
||||
|
||||
// On the next write the (now-populated) active file rotates
|
||||
// normally and the oversized payload becomes `.in_session.0`.
|
||||
w.write_all(b"next\n").unwrap();
|
||||
w.flush().unwrap();
|
||||
assert_eq!(
|
||||
read(&tmp.path().join("warp.log.in_session.0")),
|
||||
"oversized first payload\n"
|
||||
);
|
||||
assert_eq!(read(&tmp.path().join("warp.log")), "next\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zero_max_rotation_truncates_in_place_without_sidecar() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let mut w = RotatingFileWriter::open(tmp.path(), "warp.log", 8, 0).unwrap();
|
||||
// First write skips rotation since the active file is still empty;
|
||||
// both batches land sequentially, and the second crosses the
|
||||
// threshold so the truncate-in-place branch fires.
|
||||
w.write_all(b"first batch\n").unwrap();
|
||||
w.write_all(b"second batch\n").unwrap();
|
||||
w.flush().unwrap();
|
||||
// With max_rotation=0, no .in_session.N file should ever exist.
|
||||
assert!(!tmp.path().join("warp.log.in_session.0").exists());
|
||||
// The active file holds only the most recent batch (older content
|
||||
// truncated since slot 0 is not retained).
|
||||
assert_eq!(read(&tmp.path().join("warp.log")), "second batch\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zero_max_bytes_disables_rotation_entirely() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let mut w = RotatingFileWriter::open(tmp.path(), "warp.log", 0, 3).unwrap();
|
||||
for _ in 0..100 {
|
||||
w.write_all(b"line\n").unwrap();
|
||||
}
|
||||
w.flush().unwrap();
|
||||
assert!(!tmp.path().join("warp.log.in_session.0").exists());
|
||||
assert_eq!(read(&tmp.path().join("warp.log")).len(), 500);
|
||||
}
|
||||
Reference in New Issue
Block a user