This is an automated email from the ASF dual-hosted git repository.

xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-opendal.git


The following commit(s) were added to refs/heads/main by this push:
     new 04d0a1f24 feat: add basic behavior tests for buffer reader (#3862)
04d0a1f24 is described below

commit 04d0a1f24e0275c900159fa94e32f17793bdd5bf
Author: Weny Xu <[email protected]>
AuthorDate: Sun Dec 31 02:05:14 2023 +0900

    feat: add basic behavior tests for buffer reader (#3862)
---
 core/tests/behavior/write.rs | 246 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 246 insertions(+)

diff --git a/core/tests/behavior/write.rs b/core/tests/behavior/write.rs
index 5c3a81458..6c3114820 100644
--- a/core/tests/behavior/write.rs
+++ b/core/tests/behavior/write.rs
@@ -70,14 +70,20 @@ pub fn behavior_write_tests(op: &Operator) -> Vec<Trial> {
         test_read_range,
         test_read_large_range,
         test_reader_range,
+        test_reader_range_with_buffer,
         test_reader_from,
+        test_reader_from_with_buffer,
         test_reader_tail,
+        test_reader_tail_with_buffer,
         test_read_not_exist,
         test_read_with_if_match,
         test_read_with_if_none_match,
         test_fuzz_reader_with_range,
+        test_fuzz_reader_with_range_and_buffer,
         test_fuzz_offset_reader,
+        test_fuzz_offset_reader_with_buffer,
         test_fuzz_part_reader,
+        test_fuzz_part_reader_with_buffer,
         test_read_with_dir_path,
         test_read_with_special_chars,
         test_read_with_override_cache_control,
@@ -756,6 +762,43 @@ pub async fn test_reader_range(op: Operator) -> Result<()> 
{
     Ok(())
 }
 
+/// Read range content should match.
+pub async fn test_reader_range_with_buffer(op: Operator) -> Result<()> {
+    if !op.info().full_capability().read_with_range {
+        return Ok(());
+    }
+
+    let path = uuid::Uuid::new_v4().to_string();
+    debug!("Generate a random file: {}", &path);
+    let (content, size) = gen_bytes(op.info().full_capability());
+    let (offset, length) = gen_offset_length(size);
+
+    op.write(&path, content.clone())
+        .await
+        .expect("write must succeed");
+
+    let mut r = op
+        .reader_with(&path)
+        .range(offset..offset + length)
+        .buffer(4096)
+        .await?;
+
+    let mut bs = Vec::new();
+    r.read_to_end(&mut bs).await?;
+
+    assert_eq!(
+        format!("{:x}", Sha256::digest(&bs)),
+        format!(
+            "{:x}",
+            Sha256::digest(&content[offset as usize..(offset + length) as 
usize])
+        ),
+        "read content"
+    );
+
+    op.delete(&path).await.expect("delete must succeed");
+    Ok(())
+}
+
 /// Read range from should match.
 pub async fn test_reader_from(op: Operator) -> Result<()> {
     if !op.info().full_capability().read_with_range {
@@ -787,6 +830,37 @@ pub async fn test_reader_from(op: Operator) -> Result<()> {
     Ok(())
 }
 
+/// Read range from should match.
+pub async fn test_reader_from_with_buffer(op: Operator) -> Result<()> {
+    if !op.info().full_capability().read_with_range {
+        return Ok(());
+    }
+
+    let path = uuid::Uuid::new_v4().to_string();
+    debug!("Generate a random file: {}", &path);
+    let (content, size) = gen_bytes(op.info().full_capability());
+    let (offset, _) = gen_offset_length(size);
+
+    op.write(&path, content.clone())
+        .await
+        .expect("write must succeed");
+
+    let mut r = op.reader_with(&path).range(offset..).buffer(4096).await?;
+
+    let mut bs = Vec::new();
+    r.read_to_end(&mut bs).await?;
+
+    assert_eq!(bs.len(), size - offset as usize, "read size");
+    assert_eq!(
+        format!("{:x}", Sha256::digest(&bs)),
+        format!("{:x}", Sha256::digest(&content[offset as usize..])),
+        "read content"
+    );
+
+    op.delete(&path).await.expect("delete must succeed");
+    Ok(())
+}
+
 /// Read range tail should match.
 pub async fn test_reader_tail(op: Operator) -> Result<()> {
     if !op.info().full_capability().read_with_range {
@@ -826,6 +900,45 @@ pub async fn test_reader_tail(op: Operator) -> Result<()> {
     Ok(())
 }
 
+/// Read range tail should match.
+pub async fn test_reader_tail_with_buffer(op: Operator) -> Result<()> {
+    if !op.info().full_capability().read_with_range {
+        return Ok(());
+    }
+
+    let path = uuid::Uuid::new_v4().to_string();
+    debug!("Generate a random file: {}", &path);
+    let (content, size) = gen_bytes(op.info().full_capability());
+    let (_, length) = gen_offset_length(size);
+
+    op.write(&path, content.clone())
+        .await
+        .expect("write must succeed");
+
+    let mut r = match op.reader_with(&path).range(..length).buffer(4096).await 
{
+        Ok(r) => r,
+        // Not all services support range with tail range, let's tolerate this.
+        Err(err) if err.kind() == ErrorKind::Unsupported => {
+            warn!("service doesn't support range with tail");
+            return Ok(());
+        }
+        Err(err) => return Err(err.into()),
+    };
+
+    let mut bs = Vec::new();
+    r.read_to_end(&mut bs).await?;
+
+    assert_eq!(bs.len(), length as usize, "read size");
+    assert_eq!(
+        format!("{:x}", Sha256::digest(&bs)),
+        format!("{:x}", Sha256::digest(&content[size - length as usize..])),
+        "read content"
+    );
+
+    op.delete(&path).await.expect("delete must succeed");
+    Ok(())
+}
+
 /// Read not exist file should return NotFound
 pub async fn test_read_not_exist(op: Operator) -> Result<()> {
     let path = uuid::Uuid::new_v4().to_string();
@@ -943,6 +1056,51 @@ pub async fn test_fuzz_reader_with_range(op: Operator) -> 
Result<()> {
     Ok(())
 }
 
+pub async fn test_fuzz_reader_with_range_and_buffer(op: Operator) -> 
Result<()> {
+    if !op.info().full_capability().read_with_range {
+        return Ok(());
+    }
+
+    let path = uuid::Uuid::new_v4().to_string();
+    debug!("Generate a random file: {}", &path);
+    let (content, _) = gen_bytes(op.info().full_capability());
+
+    op.write(&path, content.clone())
+        .await
+        .expect("write must succeed");
+
+    let mut fuzzer = ObjectReaderFuzzer::new(&path, content.clone(), 0, 
content.len());
+    let mut o = op
+        .reader_with(&path)
+        .range(0..content.len() as u64)
+        .buffer(4096)
+        .await?;
+
+    for _ in 0..100 {
+        match fuzzer.fuzz() {
+            ObjectReaderAction::Read(size) => {
+                let mut bs = vec![0; size];
+                let n = o.read(&mut bs).await?;
+                fuzzer.check_read(n, &bs[..n])
+            }
+            ObjectReaderAction::Seek(input_pos) => {
+                let actual_pos = o.seek(input_pos).await?;
+                fuzzer.check_seek(input_pos, actual_pos)
+            }
+            ObjectReaderAction::Next => {
+                let actual_bs = o
+                    .next()
+                    .await
+                    .map(|v| v.expect("next should not return error"));
+                fuzzer.check_next(actual_bs)
+            }
+        }
+    }
+
+    op.delete(&path).await.expect("delete must succeed");
+    Ok(())
+}
+
 pub async fn test_fuzz_offset_reader(op: Operator) -> Result<()> {
     if !op.info().full_capability().read_with_range {
         return Ok(());
@@ -984,6 +1142,47 @@ pub async fn test_fuzz_offset_reader(op: Operator) -> 
Result<()> {
     Ok(())
 }
 
+pub async fn test_fuzz_offset_reader_with_buffer(op: Operator) -> Result<()> {
+    if !op.info().full_capability().read_with_range {
+        return Ok(());
+    }
+
+    let path = uuid::Uuid::new_v4().to_string();
+    debug!("Generate a random file: {}", &path);
+    let (content, _) = gen_bytes(op.info().full_capability());
+
+    op.write(&path, content.clone())
+        .await
+        .expect("write must succeed");
+
+    let mut fuzzer = ObjectReaderFuzzer::new(&path, content.clone(), 0, 
content.len());
+    let mut o = op.reader_with(&path).range(0..).buffer(4096).await?;
+
+    for _ in 0..100 {
+        match fuzzer.fuzz() {
+            ObjectReaderAction::Read(size) => {
+                let mut bs = vec![0; size];
+                let n = o.read(&mut bs).await?;
+                fuzzer.check_read(n, &bs[..n])
+            }
+            ObjectReaderAction::Seek(input_pos) => {
+                let actual_pos = o.seek(input_pos).await?;
+                fuzzer.check_seek(input_pos, actual_pos)
+            }
+            ObjectReaderAction::Next => {
+                let actual_bs = o
+                    .next()
+                    .await
+                    .map(|v| v.expect("next should not return error"));
+                fuzzer.check_next(actual_bs)
+            }
+        }
+    }
+
+    op.delete(&path).await.expect("delete must succeed");
+    Ok(())
+}
+
 pub async fn test_fuzz_part_reader(op: Operator) -> Result<()> {
     if !op.info().full_capability().read_with_range {
         return Ok(());
@@ -1027,6 +1226,53 @@ pub async fn test_fuzz_part_reader(op: Operator) -> 
Result<()> {
     Ok(())
 }
 
+pub async fn test_fuzz_part_reader_with_buffer(op: Operator) -> Result<()> {
+    if !op.info().full_capability().read_with_range {
+        return Ok(());
+    }
+
+    let path = uuid::Uuid::new_v4().to_string();
+    debug!("Generate a random file: {}", &path);
+    let (content, size) = gen_bytes(op.info().full_capability());
+    let (offset, length) = gen_offset_length(size);
+
+    op.write(&path, content.clone())
+        .await
+        .expect("write must succeed");
+
+    let mut fuzzer =
+        ObjectReaderFuzzer::new(&path, content.clone(), offset as usize, 
length as usize);
+    let mut o = op
+        .reader_with(&path)
+        .range(offset..offset + length)
+        .buffer(4096)
+        .await?;
+
+    for _ in 0..100 {
+        match fuzzer.fuzz() {
+            ObjectReaderAction::Read(size) => {
+                let mut bs = vec![0; size];
+                let n = o.read(&mut bs).await?;
+                fuzzer.check_read(n, &bs[..n])
+            }
+            ObjectReaderAction::Seek(input_pos) => {
+                let actual_pos = o.seek(input_pos).await?;
+                fuzzer.check_seek(input_pos, actual_pos)
+            }
+            ObjectReaderAction::Next => {
+                let actual_bs = o
+                    .next()
+                    .await
+                    .map(|v| v.expect("next should not return error"));
+                fuzzer.check_next(actual_bs)
+            }
+        }
+    }
+
+    op.delete(&path).await.expect("delete must succeed");
+    Ok(())
+}
+
 /// Read with dir path should return an error.
 pub async fn test_read_with_dir_path(op: Operator) -> Result<()> {
     if !op.info().full_capability().create_dir {

Reply via email to