This is an automated email from the ASF dual-hosted git repository. xuanwo pushed a commit to branch polish-fuzz in repository https://gitbox.apache.org/repos/asf/incubator-opendal.git
commit 83d8b8427fe9335119b61cda0a5544241bc50ef8 Author: Xuanwo <[email protected]> AuthorDate: Thu Jul 27 13:39:07 2023 +0800 refactor: Polish fuzz build time Signed-off-by: Xuanwo <[email protected]> --- .github/workflows/fuzz_test.yml | 31 +++-- Cargo.lock | 1 + core/fuzz/Cargo.toml | 13 +-- core/fuzz/fuzz_range_reader.rs | 252 ---------------------------------------- core/fuzz/fuzz_reader.rs | 190 +++++++++++++++++------------- core/fuzz/fuzz_writer.rs | 128 +++++++++----------- core/fuzz/utils.rs | 29 +++-- 7 files changed, 205 insertions(+), 439 deletions(-) diff --git a/.github/workflows/fuzz_test.yml b/.github/workflows/fuzz_test.yml index 1a11e06aa..b13b12148 100644 --- a/.github/workflows/fuzz_test.yml +++ b/.github/workflows/fuzz_test.yml @@ -15,7 +15,6 @@ # specific language governing permissions and limitations # under the License. - name: Fuzz Test on: @@ -39,12 +38,16 @@ concurrency: cancel-in-progress: true jobs: - fuzz-test-build-target: + fuzz-build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Rust toolchain uses: ./.github/actions/setup + - name: Install libfuzz + shell: bash + run: sudo apt-get install -y libfuzzer-14-dev + - name: Install cargo fuzz shell: bash run: rustup install nightly && cargo +nightly install cargo-fuzz @@ -52,12 +55,16 @@ jobs: shell: bash working-directory: core/fuzz run: cargo +nightly fuzz build + env: + CUSTOM_LIBFUZZER_PATH: /usr/lib/llvm-14/lib + - name: Upload build artifacts uses: actions/upload-artifact@v3 with: name: fuzz_targets path: ./target/x86_64-unknown-linux-gnu/release/fuzz_* - fuzz-test-run-s3: + + fuzz-test-s3: runs-on: ubuntu-latest needs: fuzz-test-build-target services: @@ -71,8 +78,10 @@ jobs: strategy: fail-fast: true matrix: - fuzz-targets: [ fuzz_reader, fuzz_range_reader, fuzz_writer ] + fuzz-targets: [ fuzz_reader, fuzz_writer ] steps: + - name: Install libfuzzer + run: sudo apt-get install -y libfuzzer-14-dev - name: Setup Test Bucket env: AWS_ACCESS_KEY_ID: "minioadmin" @@ -102,14 +111,17 @@ jobs: with: name: crash_s3_${{ matrix.fuzz-targets }}_${{ github.event_name }}_${{ github.run_attempt }}_${{ github.sha }} path: ./crash* - fuzz-test-run-fs: + + fuzz-test-fs: runs-on: ubuntu-latest needs: fuzz-test-build-target strategy: fail-fast: true matrix: - fuzz-targets: [ fuzz_reader, fuzz_range_reader, fuzz_writer ] + fuzz-targets: [ fuzz_reader, fuzz_writer ] steps: + - name: Install libfuzzer + run: sudo apt-get install -y libfuzzer-14-dev - name: Download Fuzz Targets uses: actions/download-artifact@v3 with: @@ -130,14 +142,17 @@ jobs: with: name: crash_fs_${{ matrix.fuzz-targets }}_${{ github.event_name }}_${{ github.run_attempt }}_${{ github.sha }} path: ./crash* - fuzz-test-run-memory: + + fuzz-test-memory: runs-on: ubuntu-latest needs: fuzz-test-build-target strategy: fail-fast: true matrix: - fuzz-targets: [ fuzz_reader, fuzz_range_reader, fuzz_writer ] + fuzz-targets: [ fuzz_reader, fuzz_writer ] steps: + - name: Install libfuzzer + run: sudo apt-get install -y libfuzzer-14-dev - name: Download Fuzz Targets uses: actions/download-artifact@v3 with: diff --git a/Cargo.lock b/Cargo.lock index cb0b29ed6..29292833f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3194,6 +3194,7 @@ dependencies = [ "dotenvy", "libfuzzer-sys", "opendal", + "rand 0.8.5", "sha2", "tokio", "uuid", diff --git a/core/fuzz/Cargo.toml b/core/fuzz/Cargo.toml index 37184ff5a..8020dfaf4 100644 --- a/core/fuzz/Cargo.toml +++ b/core/fuzz/Cargo.toml @@ -31,24 +31,15 @@ bytes = "1.2" dotenvy = "0.15.6" libfuzzer-sys = "0.4" opendal = { path = ".." } +rand = "0.8" sha2 = { version = "0.10.6" } tokio = { version = "1", features = ["full"] } -uuid = { version = "1.3.0", features = ["v4"] } +uuid = { version = "1", features = ["v4"] } [[bin]] -doc = false name = "fuzz_reader" path = "fuzz_reader.rs" -test = false [[bin]] -doc = false name = "fuzz_writer" path = "fuzz_writer.rs" -test = false - -[[bin]] -doc = false -name = "fuzz_range_reader" -path = "fuzz_range_reader.rs" -test = false diff --git a/core/fuzz/fuzz_range_reader.rs b/core/fuzz/fuzz_range_reader.rs deleted file mode 100644 index ba7ade043..000000000 --- a/core/fuzz/fuzz_range_reader.rs +++ /dev/null @@ -1,252 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -#![no_main] - -use std::io::SeekFrom; - -use bytes::Bytes; -use libfuzzer_sys::arbitrary::Arbitrary; -use libfuzzer_sys::arbitrary::Result; -use libfuzzer_sys::arbitrary::Unstructured; -use libfuzzer_sys::fuzz_target; -use sha2::Digest; -use sha2::Sha256; - -use opendal::raw::oio::ReadExt; -use opendal::Operator; - -mod utils; - -const MAX_DATA_SIZE: usize = 16 * 1024 * 1024; - -#[derive(Debug, Clone)] -enum ReaderAction { - Read { size: usize }, - Seek(SeekFrom), - Next, -} - -#[derive(Debug, Clone)] -struct FuzzInput { - actions: Vec<ReaderAction>, - data: Vec<u8>, - - range: (u64, u64), -} - -impl Arbitrary<'_> for FuzzInput { - fn arbitrary(u: &mut Unstructured<'_>) -> Result<Self> { - let data_len = u.int_in_range(1..=MAX_DATA_SIZE)?; - let data: Vec<u8> = u.bytes(data_len)?.to_vec(); - - let range_start = u.int_in_range(0..=data_len as u64 - 1)?; - let range_end = u.int_in_range(range_start + 1..=data_len as u64)?; - - let range = (range_start, range_end); - - let mut actions = vec![]; - let mut action_count = u.int_in_range(128..=1024)?; - - while action_count != 0 { - action_count -= 1; - match u.int_in_range(0..=2)? { - 0 => { - let size = u.int_in_range(0..=data_len * 2)?; - actions.push(ReaderAction::Read { size }); - } - 1 => { - let offset: i64 = u.int_in_range(-(data_len as i64)..=(data_len as i64))?; - let seek_from = match u.int_in_range(0..=2)? { - 0 => SeekFrom::Start(offset.unsigned_abs()), - 1 => SeekFrom::End(offset), - _ => SeekFrom::Current(offset), - }; - actions.push(ReaderAction::Seek(seek_from)); - } - _ => actions.push(ReaderAction::Next), - } - } - Ok(FuzzInput { - actions, - data, - range, - }) - } -} - -struct ReaderFuzzerChecker { - data: Vec<u8>, - size: usize, - cur: usize, - start: usize, -} - -impl ReaderFuzzerChecker { - fn new(data: Vec<u8>, start: usize, end: usize) -> Self { - Self { - size: end - start, - data, - cur: 0, - start, - } - } - - fn check_read(&mut self, n: usize, output: &[u8]) { - if n == 0 { - return; - } - - let current = self.cur + self.start; - let expected = &self.data[current..current + n]; - - // Check the read result - assert_eq!( - format!("{:x}", Sha256::digest(output)), - format!("{:x}", Sha256::digest(expected)), - "check read failed: output bs is different with expected bs", - ); - - // Update the current position - self.cur += n; - } - - fn check_seek(&mut self, seek_from: SeekFrom, output: opendal::Result<u64>) { - let expected = match seek_from { - SeekFrom::Start(offset) => offset as i64, - SeekFrom::End(offset) => self.size as i64 + offset, - SeekFrom::Current(offset) => self.cur as i64 + offset, - }; - - if expected < 0 { - assert!(output.is_err(), "check seek failed: seek should fail"); - assert_eq!( - output.unwrap_err().kind(), - opendal::ErrorKind::InvalidInput, - "check seek failed: seek result is different with expected result", - ); - } else { - assert_eq!( - output.unwrap(), - expected as u64, - "check seek failed: seek result is different with expected result", - ); - - self.cur = expected as usize; - } - } - - fn check_next(&mut self, output: Option<Bytes>) { - if let Some(output) = output { - assert!( - self.cur + output.len() <= self.size, - "check next failed: output bs is larger than remaining bs", - ); - - let current = self.cur + self.start; - let expected = &self.data[current..current + output.len()]; - - assert_eq!( - format!("{:x}", Sha256::digest(&output)), - format!("{:x}", Sha256::digest(expected)), - "check next failed: output bs is different with expected bs", - ); - - // update the current position - self.cur += output.len(); - } else { - assert!( - self.cur >= self.size, - "check next failed: output bs is None, we still have bytes to read", - ) - } - } -} - -async fn fuzz_range_reader_process(input: FuzzInput, op: &Operator, name: &str) -> Result<()> { - let path = uuid::Uuid::new_v4().to_string(); - - let mut checker = ReaderFuzzerChecker::new( - input.data.clone(), - input.range.0 as usize, - input.range.1 as usize, - ); - - op.write(&path, input.data) - .await - .unwrap_or_else(|_| panic!("{} write must succeed", name)); - - let mut o = op - .range_reader(&path, input.range.0..input.range.1) - .await - .unwrap_or_else(|_| panic!("{} init reader must succeed", name)); - - for action in input.actions { - match action { - ReaderAction::Read { size } => { - let mut buf = vec![0; size]; - let n = o - .read(&mut buf) - .await - .unwrap_or_else(|_| panic!("{} read must succeed", name)); - checker.check_read(n, &buf[..n]); - } - - ReaderAction::Seek(seek_from) => { - let res = o.seek(seek_from).await; - checker.check_seek(seek_from, res); - } - - ReaderAction::Next => { - let res = o - .next() - .await - .map(|v| v.unwrap_or_else(|_| panic!("{} next should not return error", name))); - checker.check_next(res); - } - } - } - - op.delete(&path) - .await - .unwrap_or_else(|_| panic!("{} delete must succeed", name)); - Ok(()) -} - -fn fuzz_reader(name: &str, op: &Operator, input: FuzzInput) { - let runtime = tokio::runtime::Runtime::new().unwrap(); - - runtime.block_on(async { - fuzz_range_reader_process(input, op, name) - .await - .unwrap_or_else(|_| panic!("{} fuzz range reader must succeed", name)); - }); -} - -fuzz_target!(|input: FuzzInput| { - let _ = dotenvy::dotenv(); - - for service in utils::init_services() { - if service.1.is_none() { - continue; - } - - let op = service.1.unwrap(); - - fuzz_reader(service.0, &op, input.clone()); - } -}); diff --git a/core/fuzz/fuzz_reader.rs b/core/fuzz/fuzz_reader.rs index 8ec87f591..26b6323fc 100644 --- a/core/fuzz/fuzz_reader.rs +++ b/core/fuzz/fuzz_reader.rs @@ -21,21 +21,23 @@ use std::io::SeekFrom; use bytes::Bytes; use libfuzzer_sys::arbitrary::Arbitrary; -use libfuzzer_sys::arbitrary::Result; use libfuzzer_sys::arbitrary::Unstructured; use libfuzzer_sys::fuzz_target; +use rand::prelude::*; use sha2::Digest; use sha2::Sha256; use opendal::raw::oio::ReadExt; +use opendal::raw::BytesRange; use opendal::Operator; +use opendal::Result; mod utils; const MAX_DATA_SIZE: usize = 16 * 1024 * 1024; #[derive(Debug, Clone)] -enum ReaderAction { +enum ReadAction { Read { size: usize }, Seek(SeekFrom), Next, @@ -43,58 +45,103 @@ enum ReaderAction { #[derive(Debug, Clone)] struct FuzzInput { - actions: Vec<ReaderAction>, - data: Vec<u8>, + size: usize, + range: BytesRange, + actions: Vec<ReadAction>, } impl Arbitrary<'_> for FuzzInput { - fn arbitrary(u: &mut Unstructured<'_>) -> Result<Self> { - let data_len = u.int_in_range(1..=MAX_DATA_SIZE)?; - let data: Vec<u8> = u.bytes(data_len)?.to_vec(); + fn arbitrary(u: &mut Unstructured<'_>) -> arbitrary::Result<Self> { + let total_size = u.int_in_range(1..=MAX_DATA_SIZE)?; + + // TODO: it's valid that size is larger than total_size. + let (offset, size) = match u.int_in_range(0..=3)? { + // Full range + 0 => (None, None), + 1 => { + let offset = u.int_in_range(0..=total_size as u64 - 1)?; + (Some(offset), None) + } + 2 => { + let size = u.int_in_range(1..=total_size as u64)?; + (None, Some(size)) + } + 3 => { + let offset = u.int_in_range(0..=total_size as u64 - 1)?; + let size = u.int_in_range(1..=total_size as u64 - offset)?; + (Some(offset), Some(size)) + } + _ => unreachable!("invalid int generated by arbitrary"), + }; + let range = BytesRange::new(offset, size); + let count = u.int_in_range(128..=1024)?; let mut actions = vec![]; - let mut action_count = u.int_in_range(128..=1024)?; - while action_count != 0 { - action_count -= 1; - match u.int_in_range(0..=2)? { + for _ in 0..count { + let action = match u.int_in_range(0..=4)? { + // Read 0 => { - let size = u.int_in_range(0..=data_len * 2)?; - actions.push(ReaderAction::Read { size }); + let size = u.int_in_range(0..=total_size * 2)?; + ReadAction::Read { size } } - 1 => { - let offset: i64 = u.int_in_range(-(data_len as i64)..=(data_len as i64))?; - let seek_from = match u.int_in_range(0..=2)? { - 0 => SeekFrom::Start(offset.unsigned_abs()), - 1 => SeekFrom::End(offset), - _ => SeekFrom::Current(offset), - }; - actions.push(ReaderAction::Seek(seek_from)); + // Next + 1 => ReadAction::Next, + // Seek Start + 2 => { + // NOTE: seek out of the end of file is valid. + let offset = u.int_in_range(0..=total_size * 2)?; + ReadAction::Seek(SeekFrom::Start(offset as u64)) } - _ => actions.push(ReaderAction::Next), - } + // Seek Current + 3 => { + let offset = u.int_in_range(-(total_size as i64)..=(total_size as i64))?; + ReadAction::Seek(SeekFrom::Current(offset)) + } + // Seek End + 4 => { + let offset = u.int_in_range(-(total_size as i64)..=(total_size as i64))?; + ReadAction::Seek(SeekFrom::End(offset)) + } + _ => unreachable!("invalid int generated by arbitrary"), + }; + + actions.push(action); } - Ok(FuzzInput { actions, data }) + + Ok(FuzzInput { + size: total_size, + range, + actions, + }) } } -struct ReaderFuzzerChecker { - data: Vec<u8>, +struct ReadChecker { size: usize, + + data: Bytes, cur: usize, } -impl ReaderFuzzerChecker { - fn new(data: Vec<u8>) -> Self { - Self { - size: data.len(), - data, - cur: 0, - } +impl ReadChecker { + fn new(size: usize, range: BytesRange) -> Self { + let mut rng = thread_rng(); + let mut data = vec![0; size]; + rng.fill_bytes(&mut data); + + let data = range.apply_on_bytes(Bytes::from(data)); + + Self { size, data, cur: 0 } } fn check_read(&mut self, n: usize, output: &[u8]) { if n == 0 { + assert_eq!( + output.len(), + 0, + "check read failed: output bs is not empty when read size is 0" + ); return; } @@ -111,7 +158,7 @@ impl ReaderFuzzerChecker { self.cur += n; } - fn check_seek(&mut self, seek_from: SeekFrom, output: opendal::Result<u64>) { + fn check_seek(&mut self, seek_from: SeekFrom, output: Result<u64>) { let expected = match seek_from { SeekFrom::Start(offset) => offset as i64, SeekFrom::End(offset) => self.size as i64 + offset, @@ -125,16 +172,18 @@ impl ReaderFuzzerChecker { opendal::ErrorKind::InvalidInput, "check seek failed: seek result is different with expected result" ); - } else { - assert_eq!( - output.unwrap(), - expected as u64, - "check seek failed: seek result is different with expected result", - ); - // only update the current position when seek succeed - self.cur = expected as usize; + return; } + + assert_eq!( + output.unwrap(), + expected as u64, + "check seek failed: seek result is different with expected result", + ); + + // only update the current position when seek succeed + self.cur = expected as usize; } fn check_next(&mut self, output: Option<Bytes>) { @@ -164,71 +213,48 @@ impl ReaderFuzzerChecker { } } -async fn fuzz_reader_process(input: FuzzInput, op: &Operator, name: &str) -> Result<()> { +async fn fuzz_reader(op: Operator, input: FuzzInput) -> Result<()> { let path = uuid::Uuid::new_v4().to_string(); - let mut checker = ReaderFuzzerChecker::new(input.data.clone()); - op.write(&path, input.data) - .await - .unwrap_or_else(|_| panic!("{} write must succeed", name)); + let mut checker = ReadChecker::new(input.size, input.range); + op.write(&path, checker.data.clone()).await?; - let mut o = op - .reader(&path) - .await - .unwrap_or_else(|_| panic!("{} init reader must succeed", name)); + let mut o = op.range_reader(&path, input.range.to_range()).await?; for action in input.actions { match action { - ReaderAction::Read { size } => { + ReadAction::Read { size } => { let mut buf = vec![0; size]; - let n = o - .read(&mut buf) - .await - .unwrap_or_else(|_| panic!("{} read must succeed", name)); + let n = o.read(&mut buf).await?; checker.check_read(n, &buf[..n]); } - ReaderAction::Seek(seek_from) => { + ReadAction::Seek(seek_from) => { let res = o.seek(seek_from).await; checker.check_seek(seek_from, res); } - ReaderAction::Next => { - let res = o - .next() - .await - .map(|v| v.unwrap_or_else(|_| panic!("{} next should not return error", name))); + ReadAction::Next => { + let res = o.next().await.transpose()?; checker.check_next(res); } } } - op.delete(&path) - .await - .unwrap_or_else(|_| panic!("{} delete must succeed", name)); + op.delete(&path).await?; Ok(()) } -fn fuzz_reader(name: &str, op: &Operator, input: FuzzInput) { - let runtime = tokio::runtime::Runtime::new().unwrap(); - - runtime.block_on(async { - fuzz_reader_process(input, op, name) - .await - .unwrap_or_else(|_| panic!("{} fuzz reader must succeed", name)); - }); -} - fuzz_target!(|input: FuzzInput| { let _ = dotenvy::dotenv(); - for service in utils::init_services() { - if service.1.is_none() { - continue; - } - - let op = service.1.unwrap(); + let runtime = tokio::runtime::Runtime::new().expect("init runtime must succeed"); - fuzz_reader(service.0, &op, input.clone()); + for op in utils::init_services() { + runtime.block_on(async { + fuzz_reader(op, input.clone()) + .await + .unwrap_or_else(|_| panic!("fuzz reader must succeed")); + }) } }); diff --git a/core/fuzz/fuzz_writer.rs b/core/fuzz/fuzz_writer.rs index 261ffedf5..63c3ebb3a 100644 --- a/core/fuzz/fuzz_writer.rs +++ b/core/fuzz/fuzz_writer.rs @@ -17,15 +17,16 @@ #![no_main] -use bytes::Bytes; +use bytes::{Bytes, BytesMut}; use libfuzzer_sys::arbitrary::Arbitrary; -use libfuzzer_sys::arbitrary::Result; use libfuzzer_sys::arbitrary::Unstructured; use libfuzzer_sys::fuzz_target; +use rand::prelude::*; use sha2::Digest; use sha2::Sha256; use opendal::Operator; +use opendal::Result; mod utils; @@ -33,7 +34,7 @@ const MAX_DATA_SIZE: usize = 16 * 1024 * 1024; #[derive(Debug, Clone)] enum WriterAction { - Write { data: Bytes }, + Write { size: usize }, } #[derive(Debug, Clone)] @@ -42,40 +43,46 @@ struct FuzzInput { } impl Arbitrary<'_> for FuzzInput { - fn arbitrary(u: &mut Unstructured<'_>) -> Result<Self> { + fn arbitrary(u: &mut Unstructured<'_>) -> arbitrary::Result<Self> { let mut actions = vec![]; - let mut action_count = u.int_in_range(128..=1024)?; - - while action_count != 0 { - action_count -= 1; - let data_len = u.int_in_range(1..=MAX_DATA_SIZE)?; - let data: Vec<u8> = u.bytes(data_len)?.to_vec(); - actions.push(WriterAction::Write { - data: Bytes::from(data), - }); + + let count = u.int_in_range(128..=1024)?; + + for _ in 0..count { + let size = u.int_in_range(1..=MAX_DATA_SIZE)?; + actions.push(WriterAction::Write { size }); } Ok(FuzzInput { actions }) } } -struct WriterFuzzChecker { - data: Vec<u8>, +struct WriteChecker { + chunks: Vec<Bytes>, + data: Bytes, } -impl WriterFuzzChecker { - fn new(input: FuzzInput) -> Self { - let mut data = vec![]; +impl WriteChecker { + fn new(size: Vec<usize>) -> Self { + let mut rng = thread_rng(); - for action in input.actions { - match action { - WriterAction::Write { data: d } => { - data.extend_from_slice(&d); - } - } + let mut chunks = Vec::with_capacity(size.len()); + + for i in size { + let mut bs = vec![0u8; i]; + rng.fill_bytes(&mut bs); + chunks.push(Bytes::from(bs)); } - WriterFuzzChecker { data } + let data = chunks.iter().fold(BytesMut::new(), |mut acc, x| { + acc.extend_from_slice(x); + acc + }); + + WriteChecker { + chunks, + data: data.freeze(), + } } fn check(&self, actual: &[u8]) { @@ -87,64 +94,45 @@ impl WriterFuzzChecker { } } -async fn fuzz_writer_process(input: FuzzInput, op: &Operator, name: &str) -> Result<()> { +async fn fuzz_writer(op: Operator, input: FuzzInput) -> Result<()> { let path = uuid::Uuid::new_v4().to_string(); - let checker = WriterFuzzChecker::new(input.clone()); - - let mut writer = op - .writer(&path) - .await - .unwrap_or_else(|_| panic!("{} create must succeed", name)); - - for action in input.actions { - match action { - WriterAction::Write { data } => { - writer - .write(data) - .await - .unwrap_or_else(|_| panic!("{} write must succeed", name)); - } - } + let total_size = input + .actions + .iter() + .map(|a| match a { + WriterAction::Write { size } => *size, + }) + .collect(); + + let checker = WriteChecker::new(total_size); + + let mut writer = op.writer(&path).await?; + + for chunk in &checker.chunks { + writer.write(chunk.clone()).await?; } - writer - .close() - .await - .unwrap_or_else(|_| panic!("{} close must succeed", name)); - let result = op - .read(&path) - .await - .unwrap_or_else(|_| panic!("{} read must succeed", name)); + writer.close().await?; + + let result = op.read(&path).await?; checker.check(&result); - op.delete(&path) - .await - .unwrap_or_else(|_| panic!("{} delete must succeed", name)); + op.delete(&path).await?; Ok(()) } -fn fuzz_writer(name: &str, op: &Operator, input: FuzzInput) { - let runtime = tokio::runtime::Runtime::new().unwrap(); - - runtime.block_on(async { - fuzz_writer_process(input, op, name) - .await - .unwrap_or_else(|_| panic!("{} fuzz writer must succeed", name)); - }); -} - fuzz_target!(|input: FuzzInput| { let _ = dotenvy::dotenv(); - for service in utils::init_services() { - if service.1.is_none() { - continue; - } - - let op = service.1.unwrap(); + let runtime = tokio::runtime::Runtime::new().expect("init runtime must succeed"); - fuzz_writer(service.0, &op, input.clone()); + for op in utils::init_services() { + runtime.block_on(async { + fuzz_writer(op, input.clone()) + .await + .unwrap_or_else(|_| panic!("fuzz reader must succeed")); + }) } }); diff --git a/core/fuzz/utils.rs b/core/fuzz/utils.rs index 83b708a9c..adc301557 100644 --- a/core/fuzz/utils.rs +++ b/core/fuzz/utils.rs @@ -17,17 +17,16 @@ use std::env; -use opendal::services; -use opendal::Builder; use opendal::Operator; +use opendal::Scheme; -fn service<B: Builder>() -> Option<Operator> { - let test_key = format!("opendal_{}_test", B::SCHEME).to_uppercase(); +fn service(scheme: Scheme) -> Option<Operator> { + let test_key = format!("opendal_{}_test", scheme).to_uppercase(); if env::var(test_key).unwrap_or_default() != "on" { return None; } - let prefix = format!("opendal_{}_", B::SCHEME); + let prefix = format!("opendal_{}_", scheme); let envs = env::vars() .filter_map(move |(k, v)| { k.to_lowercase() @@ -36,17 +35,15 @@ fn service<B: Builder>() -> Option<Operator> { }) .collect(); - Some( - Operator::from_map::<B>(envs) - .unwrap_or_else(|_| panic!("init {} must succeed", B::SCHEME)) - .finish(), - ) + Some(Operator::via_map(scheme, envs).unwrap_or_else(|_| panic!("init {} must succeed", scheme))) } -pub fn init_services() -> Vec<(&'static str, Option<Operator>)> { - vec![ - ("fs", service::<services::Fs>()), - ("memory", service::<services::Memory>()), - ("s3", service::<services::S3>()), - ] +pub fn init_services() -> Vec<Operator> { + let ops = vec![ + service(Scheme::Memory), + service(Scheme::Fs), + service(Scheme::S3), + ]; + + ops.into_iter().flatten().collect() }
