You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@opendal.apache.org by xu...@apache.org on 2026/06/11 23:33:43 UTC
(opendal) 01/01: Add suffix read behavior tests
This is an automated email from the ASF dual-hosted git repository.
Xuanwo pushed a commit to branch xuanwo/suffix-read-behavior-tests
in repository https://gitbox.apache.org/repos/asf/opendal.git
commit cfd88e32cbafb6e34302b9d9762649aeba9e0e29
Author: Xuanwo <gi...@xuanwo.io>
AuthorDate: Fri Jun 12 07:33:29 2026 +0800
Add suffix read behavior tests
---
core/tests/behavior/async_read.rs | 118 ++++++++++++++++++++++++++++++++++++++
1 file changed, 118 insertions(+)
diff --git a/core/tests/behavior/async_read.rs b/core/tests/behavior/async_read.rs
index 89fedf164..049f95904 100644
--- a/core/tests/behavior/async_read.rs
+++ b/core/tests/behavior/async_read.rs
@@ -58,6 +58,16 @@ pub fn tests(op: &Operator, tests: &mut Vec<Trial>) {
))
}
+ if cap.read && cap.write && cap.read_with_suffix {
+ tests.extend(async_trials!(
+ op,
+ test_read_suffix,
+ test_read_suffix_larger_than_file,
+ test_read_suffix_zero,
+ test_reader_suffix_with_chunk
+ ))
+ }
+
if cap.read && !cap.write {
tests.extend(async_trials!(
op,
@@ -72,6 +82,10 @@ pub fn tests(op: &Operator, tests: &mut Vec<Trial>) {
test_reader_only_read_with_if_none_match
))
}
+
+ if cap.read && !cap.write && cap.read_with_suffix {
+ tests.extend(async_trials!(op, test_read_only_read_with_suffix))
+ }
}
/// Read full content should match.
@@ -113,6 +127,93 @@ pub async fn test_read_range(op: Operator) -> anyhow::Result<()> {
Ok(())
}
+/// Read suffix content should match.
+pub async fn test_read_suffix(op: Operator) -> anyhow::Result<()> {
+ let path = TEST_FIXTURE.new_file_path();
+ let content = gen_fixed_bytes(1024);
+ let suffix_size = 257;
+
+ op.write(&path, content.clone())
+ .await
+ .expect("write must succeed");
+
+ let bs = op
+ .read_with(&path)
+ .range(BytesRange::suffix(suffix_size as u64))
+ .await?
+ .to_bytes();
+ assert_eq!(bs.len(), suffix_size, "read size");
+ assert_eq!(
+ sha256_digest(&bs),
+ sha256_digest(&content[content.len() - suffix_size..]),
+ "read content"
+ );
+
+ Ok(())
+}
+
+/// Read suffix larger than the file should return the full content.
+pub async fn test_read_suffix_larger_than_file(op: Operator) -> anyhow::Result<()> {
+ let path = TEST_FIXTURE.new_file_path();
+ let content = gen_fixed_bytes(1024);
+
+ op.write(&path, content.clone())
+ .await
+ .expect("write must succeed");
+
+ let bs = op
+ .read_with(&path)
+ .range(BytesRange::suffix((content.len() * 2) as u64))
+ .await?
+ .to_bytes();
+ assert_eq!(bs.len(), content.len(), "read size");
+ assert_eq!(sha256_digest(&bs), sha256_digest(&content), "read content");
+
+ Ok(())
+}
+
+/// Read zero suffix should return empty content.
+pub async fn test_read_suffix_zero(op: Operator) -> anyhow::Result<()> {
+ let path = TEST_FIXTURE.new_file_path();
+ let content = gen_fixed_bytes(1024);
+
+ op.write(&path, content).await.expect("write must succeed");
+
+ let bs = op
+ .read_with(&path)
+ .range(BytesRange::suffix(0))
+ .await?
+ .to_bytes();
+ assert!(bs.is_empty(), "read content must be empty");
+
+ Ok(())
+}
+
+/// Reader suffix with chunk should still return the requested suffix content.
+pub async fn test_reader_suffix_with_chunk(op: Operator) -> anyhow::Result<()> {
+ let path = TEST_FIXTURE.new_file_path();
+ let content = gen_fixed_bytes(4096);
+ let suffix_size = 1025;
+
+ op.write(&path, content.clone())
+ .await
+ .expect("write must succeed");
+
+ let reader = op.reader_with(&path).chunk(257).concurrent(3).await?;
+ let bs = reader
+ .read(BytesRange::suffix(suffix_size as u64))
+ .await?
+ .to_bytes();
+ assert_eq!(bs.len(), suffix_size, "read size");
+ assert_eq!(
+ sha256_digest(&bs),
+ sha256_digest(&content[content.len() - suffix_size..]),
+ "read content"
+ );
+
+ Ok(())
+}
+
/// Read full content should match.
pub async fn test_reader(op: Operator) -> anyhow::Result<()> {
let (path, content, size) = TEST_FIXTURE.new_file(op.clone());
@@ -800,6 +901,23 @@ pub async fn test_read_only_read_with_range(op: Operator) -> anyhow::Result<()>
Ok(())
}
+/// Read suffix content should match.
+pub async fn test_read_only_read_with_suffix(op: Operator) -> anyhow::Result<()> {
+ let bs = op
+ .read_with("normal_file.txt")
+ .range(BytesRange::suffix(1024))
+ .await?
+ .to_bytes();
+ assert_eq!(bs.len(), 1024, "read size");
+ assert_eq!(
+ sha256_digest(&bs),
+ "cc9312c869238ea9410b6716e0fc3f48056f2bfb2fe06ccf5f96f2c3bf39e71b",
+ "read content"
+ );
+
+ Ok(())
+}
+
/// Read not exist file should return NotFound
pub async fn test_read_only_read_not_exist(op: Operator) -> anyhow::Result<()> {
let path = uuid::Uuid::new_v4().to_string();