opendal_testkit/read.rs
1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements. See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership. The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use bytes::Bytes;
19use opendal_core::*;
20use rand::Rng;
21use rand::rng;
22
23use crate::utils::sha256_digest;
24
25/// A reader operation executed by [`ReadChecker`].
26#[derive(Debug, Clone, Copy, Eq, PartialEq)]
27pub enum ReadAction {
28 /// Read `size` bytes beginning at `offset`.
29 ///
30 /// The first field is the offset and the second field is the requested
31 /// size. A reader can return fewer bytes at the end of an object.
32 Read(usize, usize),
33}
34
35/// Generates reference data and verifies reads against it.
36pub struct ReadChecker {
37 /// Data that callers write to the storage before checking reads.
38 raw_data: Bytes,
39}
40
41impl ReadChecker {
42 /// Create a checker containing `size` bytes of random reference data.
43 ///
44 /// Random input makes the checker sensitive to misplaced or repeated data;
45 /// callers should not depend on the generated content.
46 pub fn new(size: usize) -> Self {
47 let mut rng = rng();
48 let mut data = vec![0; size];
49 rng.fill_bytes(&mut data);
50
51 let raw_data = Bytes::from(data);
52
53 Self { raw_data }
54 }
55
56 /// Return the reference data that should be written before a check.
57 pub fn data(&self) -> Bytes {
58 self.raw_data.clone()
59 }
60
61 /// check_read checks the correctness of the read process after a read action.
62 ///
63 /// - buf_size is the read action's buf size.
64 /// - output is the output of this read action.
65 fn check_read(&self, offset: usize, size: usize, output: &[u8]) {
66 if size == 0 {
67 assert_eq!(
68 output.len(),
69 0,
70 "check read failed: output must be empty if buf_size is 0"
71 );
72 return;
73 }
74
75 if size > 0 && output.is_empty() {
76 assert!(
77 offset >= self.raw_data.len(),
78 "check read failed: no data read means cur must outsides of ranged_data",
79 );
80 return;
81 }
82
83 assert!(
84 offset + output.len() <= self.raw_data.len(),
85 "check read failed: cur + output length must be less than ranged_data length, offset: {}, output: {}, ranged_data: {}",
86 offset,
87 output.len(),
88 self.raw_data.len(),
89 );
90
91 let expected = &self.raw_data[offset..offset + output.len()];
92
93 if output != expected {
94 assert_eq!(
95 sha256_digest(output),
96 sha256_digest(expected),
97 "check read failed: output bs is different with expected bs",
98 );
99 }
100 }
101
102 /// Execute `actions` and verify each result against the reference data.
103 ///
104 /// This method panics if a read fails or returns incorrect data.
105 pub async fn check(&mut self, r: Reader, actions: &[ReadAction]) {
106 for action in actions {
107 match *action {
108 ReadAction::Read(offset, size) => {
109 let bs = r
110 .read(offset as u64..(offset + size) as u64)
111 .await
112 .expect("read must success");
113 self.check_read(offset, size, bs.to_bytes().as_ref());
114 }
115 }
116 }
117 }
118}