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 ca53000e refactor: Allow reusing the same operator to speed up tests
(#2068)
ca53000e is described below
commit ca53000efe8ac93d00e156548dc697e92649b5cb
Author: Xuanwo <[email protected]>
AuthorDate: Sun Apr 23 11:01:24 2023 +0800
refactor: Allow reusing the same operator to speed up tests (#2068)
* refactor: Allow reusing the same operator to speed up tests
Signed-off-by: Xuanwo <[email protected]>
* Fix scan root
Signed-off-by: Xuanwo <[email protected]>
* Fix debug for cap
Signed-off-by: Xuanwo <[email protected]>
* avoid conflict
Signed-off-by: Xuanwo <[email protected]>
* Fix fs
Signed-off-by: Xuanwo <[email protected]>
* Allow retry content incomplete
Signed-off-by: Xuanwo <[email protected]>
* Allow retry for copy and rename
Signed-off-by: Xuanwo <[email protected]>
* FIx list tests
Signed-off-by: Xuanwo <[email protected]>
* Fix ensure root
Signed-off-by: Xuanwo <[email protected]>
* Allow retry for redis error
Signed-off-by: Xuanwo <[email protected]>
* Refactor random root
Signed-off-by: Xuanwo <[email protected]>
* Use better cap debug format
Signed-off-by: Xuanwo <[email protected]>
* Better!
Signed-off-by: Xuanwo <[email protected]>
* Fix webdav
Signed-off-by: Xuanwo <[email protected]>
* ftp should ensure parent dir exists
Signed-off-by: Xuanwo <[email protected]>
* Refactor tests
Signed-off-by: Xuanwo <[email protected]>
* format code
Signed-off-by: Xuanwo <[email protected]>
---------
Signed-off-by: Xuanwo <[email protected]>
---
.github/workflows/service_test_http.yml | 2 +
.github/workflows/service_test_ipfs.yml | 2 +
core/src/layers/retry.rs | 28 ++++++++++++
core/src/raw/http_util/body.rs | 6 ++-
core/src/services/ftp/backend.rs | 46 +++++++++++++------
core/src/services/redis/backend.rs | 4 +-
core/src/services/webdav/backend.rs | 4 ++
core/src/services/webdav/pager.rs | 11 +++--
core/src/types/capability.rs | 49 +++++++++++++++++++-
core/tests/behavior/blocking_copy.rs | 41 ++++++++---------
core/tests/behavior/blocking_list.rs | 60 +++++++++++++-----------
core/tests/behavior/blocking_read.rs | 39 ++++++++--------
core/tests/behavior/blocking_rename.rs | 41 ++++++++---------
core/tests/behavior/blocking_write.rs | 39 ++++++++--------
core/tests/behavior/copy.rs | 39 ++++++++--------
core/tests/behavior/list.rs | 81 ++++++++++++++++++---------------
core/tests/behavior/list_only.rs | 35 +++++++-------
core/tests/behavior/main.rs | 68 ++++++++++++++++-----------
core/tests/behavior/presign.rs | 35 +++++++-------
core/tests/behavior/read_only.rs | 35 +++++++-------
core/tests/behavior/rename.rs | 39 ++++++++--------
core/tests/behavior/utils.rs | 6 ++-
core/tests/behavior/write.rs | 35 +++++++-------
23 files changed, 425 insertions(+), 320 deletions(-)
diff --git a/.github/workflows/service_test_http.yml
b/.github/workflows/service_test_http.yml
index 7e3a1ff6..ce32448a 100644
--- a/.github/workflows/service_test_http.yml
+++ b/.github/workflows/service_test_http.yml
@@ -66,6 +66,7 @@ jobs:
RUST_LOG: debug
OPENDAL_HTTP_TEST: on
OPENDAL_HTTP_ENDPOINT: http://127.0.0.1:8080
+ OPENDAL_DISABLE_RANDOM_ROOT: true
caddy:
runs-on: ${{ matrix.os }}
@@ -102,3 +103,4 @@ jobs:
RUST_LOG: debug
OPENDAL_HTTP_TEST: on
OPENDAL_HTTP_ENDPOINT: http://127.0.0.1:8080
+ OPENDAL_DISABLE_RANDOM_ROOT: true
diff --git a/.github/workflows/service_test_ipfs.yml
b/.github/workflows/service_test_ipfs.yml
index ad0edc53..b7138586 100644
--- a/.github/workflows/service_test_ipfs.yml
+++ b/.github/workflows/service_test_ipfs.yml
@@ -63,6 +63,8 @@ jobs:
OPENDAL_IPFS_TEST: on
OPENDAL_IPFS_ROOT:
/ipfs/QmPpCt1aYGb9JWJRmXRUnmJtVgeFFTJGzWFYEEX7bo9zGJ/
OPENDAL_IPFS_ENDPOINT: "http://127.0.0.1:8080"
+ OPENDAL_DISABLE_RANDOM_ROOT: true
+
# # ipfs.io can't pass our test by now, we should address them in the future.
# ipfs-io:
# runs-on: ubuntu-latest
diff --git a/core/src/layers/retry.rs b/core/src/layers/retry.rs
index 2193202a..341fd75e 100644
--- a/core/src/layers/retry.rs
+++ b/core/src/layers/retry.rs
@@ -247,6 +247,34 @@ impl<A: Accessor> LayeredAccessor for RetryAccessor<A> {
.await
}
+ async fn copy(&self, from: &str, to: &str, args: OpCopy) -> Result<RpCopy>
{
+ { || self.inner.copy(from, to, args.clone()) }
+ .retry(&self.builder)
+ .when(|e| e.is_temporary())
+ .notify(|err, dur| {
+ warn!(
+ target: "opendal::service",
+ "operation={} -> retry after {}s: error={:?}",
+ Operation::Copy, dur.as_secs_f64(), err)
+ })
+ .map(|v| v.map_err(|e| e.set_persistent()))
+ .await
+ }
+
+ async fn rename(&self, from: &str, to: &str, args: OpRename) ->
Result<RpRename> {
+ { || self.inner.rename(from, to, args.clone()) }
+ .retry(&self.builder)
+ .when(|e| e.is_temporary())
+ .notify(|err, dur| {
+ warn!(
+ target: "opendal::service",
+ "operation={} -> retry after {}s: error={:?}",
+ Operation::Rename, dur.as_secs_f64(), err)
+ })
+ .map(|v| v.map_err(|e| e.set_persistent()))
+ .await
+ }
+
async fn list(&self, path: &str, args: OpList) -> Result<(RpList,
Self::Pager)> {
{ || self.inner.list(path, args.clone()) }
.retry(&self.builder)
diff --git a/core/src/raw/http_util/body.rs b/core/src/raw/http_util/body.rs
index 4d8eec3f..28905707 100644
--- a/core/src/raw/http_util/body.rs
+++ b/core/src/raw/http_util/body.rs
@@ -146,11 +146,13 @@ impl IncomingAsyncBody {
Ordering::Less => Err(Error::new(
ErrorKind::ContentIncomplete,
&format!("reader got too less data, expect: {expect}, actual:
{actual}"),
- )),
+ )
+ .set_temporary()),
Ordering::Greater => Err(Error::new(
ErrorKind::ContentTruncated,
&format!("reader got too much data, expect: {expect}, actual:
{actual}"),
- )),
+ )
+ .set_temporary()),
}
}
}
diff --git a/core/src/services/ftp/backend.rs b/core/src/services/ftp/backend.rs
index 0b112c05..32338980 100644
--- a/core/src/services/ftp/backend.rs
+++ b/core/src/services/ftp/backend.rs
@@ -335,22 +335,16 @@ impl Accessor for FtpBackend {
for path in paths {
curr_path.push_str(path);
- // try to create directory
- if curr_path.ends_with('/') {
- match ftp_stream.mkdir(&curr_path).await {
- // Do nothing if status is FileUnavailable or OK(()) is
return.
- Err(FtpError::UnexpectedResponse(Response {
- status: Status::FileUnavailable,
- ..
- }))
- | Ok(()) => (),
- Err(e) => {
- return Err(e.into());
- }
+ match ftp_stream.mkdir(&curr_path).await {
+ // Do nothing if status is FileUnavailable or OK(()) is return.
+ Err(FtpError::UnexpectedResponse(Response {
+ status: Status::FileUnavailable,
+ ..
+ }))
+ | Ok(()) => (),
+ Err(e) => {
+ return Err(e.into());
}
- } else {
- // else, create file
- ftp_stream.put_file(&curr_path, &mut "".as_bytes()).await?;
}
}
@@ -398,6 +392,28 @@ impl Accessor for FtpBackend {
));
}
+ // Ensure the parent dir exists.
+ let parent = get_parent(path);
+ let paths: Vec<&str> = parent.split('/').collect();
+
+ // TODO: we can optimize this by checking dir existence first.
+ let mut ftp_stream = self.ftp_connect(Operation::Write).await?;
+ let mut curr_path = String::new();
+ for path in paths {
+ curr_path.push_str(path);
+ match ftp_stream.mkdir(&curr_path).await {
+ // Do nothing if status is FileUnavailable or OK(()) is return.
+ Err(FtpError::UnexpectedResponse(Response {
+ status: Status::FileUnavailable,
+ ..
+ }))
+ | Ok(()) => (),
+ Err(e) => {
+ return Err(e.into());
+ }
+ }
+ }
+
Ok((
RpWrite::new(),
FtpWriter::new(self.clone(), path.to_string()),
diff --git a/core/src/services/redis/backend.rs
b/core/src/services/redis/backend.rs
index 7d8ac059..3e484af0 100644
--- a/core/src/services/redis/backend.rs
+++ b/core/src/services/redis/backend.rs
@@ -356,6 +356,8 @@ impl kv::Adapter for Adapter {
impl From<RedisError> for Error {
fn from(e: RedisError) -> Self {
- Error::new(ErrorKind::Unexpected, e.category()).set_source(e)
+ Error::new(ErrorKind::Unexpected, e.category())
+ .set_source(e)
+ .set_temporary()
}
}
diff --git a/core/src/services/webdav/backend.rs
b/core/src/services/webdav/backend.rs
index bc1558e0..4a073d6e 100644
--- a/core/src/services/webdav/backend.rs
+++ b/core/src/services/webdav/backend.rs
@@ -648,6 +648,10 @@ impl WebdavBackend {
}
async fn ensure_parent_path(&self, path: &str) -> Result<()> {
+ if path == "/" {
+ return Ok(());
+ }
+
// create dir recursively, split path by `/` and create each dir
except the last one
let abs_path = build_abs_path(&self.root, path);
let abs_path = abs_path.as_str();
diff --git a/core/src/services/webdav/pager.rs
b/core/src/services/webdav/pager.rs
index 32cb5e0f..3cd8ebb8 100644
--- a/core/src/services/webdav/pager.rs
+++ b/core/src/services/webdav/pager.rs
@@ -54,12 +54,13 @@ impl oio::Page for WebdavPager {
.into_iter()
.filter_map(|de| {
let path = de.href;
- let normalized_path = if self.root != path {
- build_rel_path(&self.root, &path)
- } else {
- path
- };
+ // Ignore the root path itself.
+ if self.root == path {
+ return None;
+ }
+
+ let normalized_path = build_rel_path(&self.root, &path);
if normalized_path == self.path {
// WebDav server may return the current path as an entry.
return None;
diff --git a/core/src/types/capability.rs b/core/src/types/capability.rs
index eb6c756b..bb7a872d 100644
--- a/core/src/types/capability.rs
+++ b/core/src/types/capability.rs
@@ -15,6 +15,8 @@
// specific language governing permissions and limitations
// under the License.
+use std::fmt::Debug;
+
/// Capability is used to describe what operations are supported
/// by current Operator.
///
@@ -42,7 +44,7 @@
/// - Operation with variants should be named like `read_can_seek`.
/// - Operation with arguments should be named like `read_with_range`.
/// - Operation with limtations should be named like `batch_max_operations`.
-#[derive(Copy, Clone, Debug, Default)]
+#[derive(Copy, Clone, Default)]
pub struct Capability {
/// If operator supports stat natively, it will be true.
pub stat: bool,
@@ -127,3 +129,48 @@ pub struct Capability {
/// If operator supports blocking natively, it will be true.
pub blocking: bool,
}
+
+impl Debug for Capability {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let mut s = vec![];
+
+ if self.read {
+ s.push("Read");
+ }
+ if self.stat {
+ s.push("Stat");
+ }
+ if self.write {
+ s.push("Write");
+ }
+ if self.create_dir {
+ s.push("CreateDir");
+ }
+ if self.delete {
+ s.push("Delete");
+ }
+ if self.list {
+ s.push("List");
+ }
+ if self.scan {
+ s.push("Scan");
+ }
+ if self.copy {
+ s.push("Copy");
+ }
+ if self.rename {
+ s.push("Rename");
+ }
+ if self.presign {
+ s.push("Presign");
+ }
+ if self.batch {
+ s.push("Batch");
+ }
+ if self.blocking {
+ s.push("Blocking");
+ }
+
+ write!(f, "{{ {} }}", s.join(" | "))
+ }
+}
diff --git a/core/tests/behavior/blocking_copy.rs
b/core/tests/behavior/blocking_copy.rs
index 8cf724c4..346b8b45 100644
--- a/core/tests/behavior/blocking_copy.rs
+++ b/core/tests/behavior/blocking_copy.rs
@@ -30,31 +30,28 @@ use super::utils::*;
macro_rules! behavior_blocking_copy_test {
($service:ident, $($(#[$meta:meta])* $test:ident),*,) => {
paste::item! {
- mod [<services_ $service:lower _blocking_copy>] {
+ $(
+ #[test]
$(
- #[test]
- $(
- #[$meta]
- )*
- fn [< $test >]() -> anyhow::Result<()> {
- let op =
$crate::utils::init_service::<opendal::services::$service>(true);
- match op {
- Some(op) if op.info().can_read()
- && op.info().can_write()
- && op.info().can_copy()
- && op.info().can_blocking() =>
$crate::blocking_copy::$test(op.blocking()),
- Some(_) => {
- log::warn!("service {} doesn't support
blocking_copy, ignored", opendal::Scheme::$service);
- Ok(())
- },
- None => {
- log::warn!("service {} not initiated,
ignored", opendal::Scheme::$service);
- Ok(())
- }
+ #[$meta]
+ )*
+ fn [<blocking_copy_ $test >]() -> anyhow::Result<()> {
+ match OPERATOR.as_ref() {
+ Some(op) if op.info().can_read()
+ && op.info().can_write()
+ && op.info().can_copy()
+ && op.info().can_blocking() =>
$crate::blocking_copy::$test(op.blocking()),
+ Some(_) => {
+ log::warn!("service {} doesn't support
blocking_copy, ignored", opendal::Scheme::$service);
+ Ok(())
+ },
+ None => {
+ log::warn!("service {} not initiated, ignored",
opendal::Scheme::$service);
+ Ok(())
}
}
- )*
- }
+ }
+ )*
}
};
}
diff --git a/core/tests/behavior/blocking_list.rs
b/core/tests/behavior/blocking_list.rs
index c84084d3..20f333e5 100644
--- a/core/tests/behavior/blocking_list.rs
+++ b/core/tests/behavior/blocking_list.rs
@@ -34,30 +34,27 @@ use super::utils::*;
macro_rules! behavior_blocking_list_test {
($service:ident, $($(#[$meta:meta])* $test:ident),*,) => {
paste::item! {
- mod [<services_ $service:lower _blocking_list>] {
+ $(
+ #[test]
$(
- #[test]
- $(
- #[$meta]
- )*
- fn [< $test >]() -> anyhow::Result<()> {
- let op =
$crate::utils::init_service::<opendal::services::$service>(true);
- match op {
- Some(op) if op.info().can_read()
- && op.info().can_write()
- && op.info().can_blocking() &&
(op.info().can_list()||op.info().can_scan()) =>
$crate::blocking_list::$test(op.blocking()),
- Some(_) => {
- log::warn!("service {} doesn't support
blocking_list, ignored", opendal::Scheme::$service);
- Ok(())
- },
- None => {
- log::warn!("service {} not initiated,
ignored", opendal::Scheme::$service);
- Ok(())
- }
+ #[$meta]
+ )*
+ fn [<blocking_list_ $test >]() -> anyhow::Result<()> {
+ match OPERATOR.as_ref() {
+ Some(op) if op.info().can_read()
+ && op.info().can_write()
+ && op.info().can_blocking() &&
(op.info().can_list()||op.info().can_scan()) =>
$crate::blocking_list::$test(op.blocking()),
+ Some(_) => {
+ log::warn!("service {} doesn't support
blocking_list, ignored", opendal::Scheme::$service);
+ Ok(())
+ },
+ None => {
+ log::warn!("service {} not initiated, ignored",
opendal::Scheme::$service);
+ Ok(())
}
}
- )*
- }
+ }
+ )*
}
};
}
@@ -79,13 +76,14 @@ macro_rules! behavior_blocking_list_tests {
/// List dir should return newly created file.
pub fn test_list_dir(op: BlockingOperator) -> Result<()> {
- let path = uuid::Uuid::new_v4().to_string();
+ let parent = uuid::Uuid::new_v4().to_string();
+ let path = format!("{parent}/{}", uuid::Uuid::new_v4());
debug!("Generate a random file: {}", &path);
let (content, size) = gen_bytes();
op.write(&path, content).expect("write must succeed");
- let obs = op.list("/")?;
+ let obs = op.list(&format!("{parent}/"))?;
let mut found = false;
for de in obs {
let de = de?;
@@ -122,22 +120,30 @@ pub fn test_list_non_exist_dir(op: BlockingOperator) ->
Result<()> {
// Walk top down should output as expected
pub fn test_scan(op: BlockingOperator) -> Result<()> {
+ let parent = uuid::Uuid::new_v4().to_string();
+
let expected = vec![
"x/", "x/y", "x/x/", "x/x/y", "x/x/x/", "x/x/x/y", "x/x/x/x/",
];
for path in expected.iter() {
if path.ends_with('/') {
- op.create_dir(path)?;
+ op.create_dir(&format!("{parent}/{path}"))?;
} else {
- op.write(path, "test_scan")?;
+ op.write(&format!("{parent}/{path}"), "test_scan")?;
}
}
- let w = op.scan("x/")?;
+ let w = op.scan(&format!("{parent}/x/"))?;
let actual = w
.collect::<Vec<_>>()
.into_iter()
- .map(|v| v.unwrap().path().to_string())
+ .map(|v| {
+ v.unwrap()
+ .path()
+ .strip_prefix(&format!("{parent}/"))
+ .unwrap()
+ .to_string()
+ })
.collect::<HashSet<_>>();
debug!("walk top down: {:?}", actual);
diff --git a/core/tests/behavior/blocking_read.rs
b/core/tests/behavior/blocking_read.rs
index 2f4e77a7..2033398c 100644
--- a/core/tests/behavior/blocking_read.rs
+++ b/core/tests/behavior/blocking_read.rs
@@ -30,30 +30,27 @@ use sha2::Sha256;
macro_rules! behavior_blocking_read_test {
($service:ident, $($(#[$meta:meta])* $test:ident),*,) => {
paste::item! {
- mod [<services_ $service:lower _blocking_read>] {
+ $(
+ #[test]
$(
- #[test]
- $(
- #[$meta]
- )*
- fn [< $test >]() -> anyhow::Result<()> {
- let op =
$crate::utils::init_service::<opendal::services::$service>(true);
- match op {
- Some(op) if op.info().can_read()
- && !op.info().can_write()
- && op.info().can_blocking() =>
$crate::blocking_read::$test(op.blocking()),
- Some(_) => {
- log::warn!("service {} doesn't support
blocking_read, ignored", opendal::Scheme::$service);
- Ok(())
- },
- None => {
- log::warn!("service {} not initiated,
ignored", opendal::Scheme::$service);
- Ok(())
- }
+ #[$meta]
+ )*
+ fn [<blocking_read_ $test >]() -> anyhow::Result<()> {
+ match OPERATOR.as_ref() {
+ Some(op) if op.info().can_read()
+ && !op.info().can_write()
+ && op.info().can_blocking() =>
$crate::blocking_read::$test(op.blocking()),
+ Some(_) => {
+ log::warn!("service {} doesn't support
blocking_read, ignored", opendal::Scheme::$service);
+ Ok(())
+ },
+ None => {
+ log::warn!("service {} not initiated, ignored",
opendal::Scheme::$service);
+ Ok(())
}
}
- )*
- }
+ }
+ )*
}
};
}
diff --git a/core/tests/behavior/blocking_rename.rs
b/core/tests/behavior/blocking_rename.rs
index 7222b230..3973a954 100644
--- a/core/tests/behavior/blocking_rename.rs
+++ b/core/tests/behavior/blocking_rename.rs
@@ -30,31 +30,28 @@ use super::utils::*;
macro_rules! behavior_blocking_rename_test {
($service:ident, $($(#[$meta:meta])* $test:ident),*,) => {
paste::item! {
- mod [<services_ $service:lower _blocking_rename>] {
+ $(
+ #[test]
$(
- #[test]
- $(
- #[$meta]
- )*
- fn [< $test >]() -> anyhow::Result<()> {
- let op =
$crate::utils::init_service::<opendal::services::$service>(true);
- match op {
- Some(op) if op.info().can_read()
- && op.info().can_write()
- && op.info().can_rename()
- && op.info().can_blocking() =>
$crate::blocking_rename::$test(op.blocking()),
- Some(_) => {
- log::warn!("service {} doesn't support
blocking_rename, ignored", opendal::Scheme::$service);
- Ok(())
- },
- None => {
- log::warn!("service {} not initiated,
ignored", opendal::Scheme::$service);
- Ok(())
- }
+ #[$meta]
+ )*
+ fn [<blocking_rename_ $test >]() -> anyhow::Result<()> {
+ match OPERATOR.as_ref() {
+ Some(op) if op.info().can_read()
+ && op.info().can_write()
+ && op.info().can_rename()
+ && op.info().can_blocking() =>
$crate::blocking_rename::$test(op.blocking()),
+ Some(_) => {
+ log::warn!("service {} doesn't support
blocking_rename, ignored", opendal::Scheme::$service);
+ Ok(())
+ },
+ None => {
+ log::warn!("service {} not initiated, ignored",
opendal::Scheme::$service);
+ Ok(())
}
}
- )*
- }
+ }
+ )*
}
};
}
diff --git a/core/tests/behavior/blocking_write.rs
b/core/tests/behavior/blocking_write.rs
index a444b1ca..d1bd392b 100644
--- a/core/tests/behavior/blocking_write.rs
+++ b/core/tests/behavior/blocking_write.rs
@@ -36,30 +36,27 @@ use super::utils::*;
macro_rules! behavior_blocking_write_test {
($service:ident, $($(#[$meta:meta])* $test:ident),*,) => {
paste::item! {
- mod [<services_ $service:lower _blocking_write>] {
+ $(
+ #[test]
$(
- #[test]
- $(
- #[$meta]
- )*
- fn [< $test >]() -> anyhow::Result<()> {
- let op =
$crate::utils::init_service::<opendal::services::$service>(true);
- match op {
- Some(op) if op.info().can_read()
- && op.info().can_write()
- && op.info().can_blocking() =>
$crate::blocking_write::$test(op.blocking()),
- Some(_) => {
- log::warn!("service {} doesn't support
blocking_write, ignored", opendal::Scheme::$service);
- Ok(())
- },
- None => {
- log::warn!("service {} not initiated,
ignored", opendal::Scheme::$service);
- Ok(())
- }
+ #[$meta]
+ )*
+ fn [<blocking_write_ $test >]() -> anyhow::Result<()> {
+ match OPERATOR.as_ref() {
+ Some(op) if op.info().can_read()
+ && op.info().can_write()
+ && op.info().can_blocking() =>
$crate::blocking_write::$test(op.blocking()),
+ Some(_) => {
+ log::warn!("service {} doesn't support
blocking_write, ignored", opendal::Scheme::$service);
+ Ok(())
+ },
+ None => {
+ log::warn!("service {} not initiated, ignored",
opendal::Scheme::$service);
+ Ok(())
}
}
- )*
- }
+ }
+ )*
}
};
}
diff --git a/core/tests/behavior/copy.rs b/core/tests/behavior/copy.rs
index e09f6b8d..dfe0a7cc 100644
--- a/core/tests/behavior/copy.rs
+++ b/core/tests/behavior/copy.rs
@@ -29,30 +29,27 @@ use super::utils::*;
macro_rules! behavior_copy_test {
($service:ident, $($(#[$meta:meta])* $test:ident),*,) => {
paste::item! {
- mod [<services_ $service:lower _copy>] {
+ $(
+ #[test]
$(
- #[tokio::test]
- $(
- #[$meta]
- )*
- async fn [< $test >]() -> anyhow::Result<()> {
- let op =
$crate::utils::init_service::<opendal::services::$service>(true);
- match op {
- Some(op) if op.info().can_read()
- && op.info().can_write()
- && op.info().can_copy() =>
$crate::copy::$test(op).await,
- Some(_) => {
- log::warn!("service {} doesn't support copy,
ignored", opendal::Scheme::$service);
- Ok(())
- },
- None => {
- log::warn!("service {} not initiated,
ignored", opendal::Scheme::$service);
- Ok(())
- }
+ #[$meta]
+ )*
+ fn [<copy_ $test >]() -> anyhow::Result<()> {
+ match OPERATOR.as_ref() {
+ Some(op) if op.info().can_read()
+ && op.info().can_write()
+ && op.info().can_copy() =>
RUNTIME.block_on($crate::copy::$test(op.clone())),
+ Some(_) => {
+ log::warn!("service {} doesn't support copy,
ignored", opendal::Scheme::$service);
+ Ok(())
+ },
+ None => {
+ log::warn!("service {} not initiated, ignored",
opendal::Scheme::$service);
+ Ok(())
}
}
- )*
- }
+ }
+ )*
}
};
}
diff --git a/core/tests/behavior/list.rs b/core/tests/behavior/list.rs
index 3c8d5347..648818dd 100644
--- a/core/tests/behavior/list.rs
+++ b/core/tests/behavior/list.rs
@@ -37,31 +37,28 @@ use super::utils::*;
macro_rules! behavior_list_test {
($service:ident, $($(#[$meta:meta])* $test:ident),*,) => {
paste::item! {
- mod [<services_ $service:lower _list>] {
+ $(
+ #[test]
$(
- #[tokio::test]
- $(
- #[$meta]
- )*
- async fn [< $test >]() -> anyhow::Result<()> {
- let op =
$crate::utils::init_service::<opendal::services::$service>(true);
- match op {
- Some(op) if op.info().can_read()
- && op.info().can_write()
- && (op.info().can_list()
- || op.info().can_scan()) =>
$crate::list::$test(op).await,
- Some(_) => {
- log::warn!("service {} doesn't support list,
ignored", opendal::Scheme::$service);
- Ok(())
- },
- None => {
- log::warn!("service {} not initiated,
ignored", opendal::Scheme::$service);
- Ok(())
- }
+ #[$meta]
+ )*
+ fn [<list_ $test >]() -> anyhow::Result<()> {
+ match OPERATOR.as_ref() {
+ Some(op) if op.info().can_read()
+ && op.info().can_write()
+ && (op.info().can_list()
+ || op.info().can_scan()) =>
RUNTIME.block_on($crate::list::$test(op.clone())),
+ Some(_) => {
+ log::warn!("service {} doesn't support list,
ignored", opendal::Scheme::$service);
+ Ok(())
+ },
+ None => {
+ log::warn!("service {} not initiated, ignored",
opendal::Scheme::$service);
+ Ok(())
}
}
- )*
- }
+ }
+ )*
}
};
}
@@ -98,13 +95,14 @@ pub async fn test_check(op: Operator) -> Result<()> {
/// List dir should return newly created file.
pub async fn test_list_dir(op: Operator) -> Result<()> {
- let path = uuid::Uuid::new_v4().to_string();
+ let parent = uuid::Uuid::new_v4().to_string();
+ let path = format!("{parent}/{}", uuid::Uuid::new_v4());
debug!("Generate a random file: {}", &path);
let (content, size) = gen_bytes();
op.write(&path, content).await.expect("write must succeed");
- let mut obs = op.list("/").await?;
+ let mut obs = op.list(&format!("{parent}/")).await?;
let mut found = false;
while let Some(de) = obs.try_next().await? {
let meta = op.stat(de.path()).await?;
@@ -289,33 +287,37 @@ pub async fn test_scan_root(op: Operator) -> Result<()> {
.map(|v| v.path().to_string())
.collect::<HashSet<_>>();
- assert!(
- actual.is_empty(),
- "empty root should return empty, but got {:?}",
- actual
- );
+ assert!(!actual.contains("/"), "empty root should return itself");
+ assert!(!actual.contains(""), "empty root should return empty");
Ok(())
}
// Walk top down should output as expected
pub async fn test_scan(op: Operator) -> Result<()> {
+ let parent = uuid::Uuid::new_v4().to_string();
+
let expected = vec![
"x/", "x/y", "x/x/", "x/x/y", "x/x/x/", "x/x/x/y", "x/x/x/x/",
];
for path in expected.iter() {
if path.ends_with('/') {
- op.create_dir(path).await?;
+ op.create_dir(&format!("{parent}/{path}")).await?;
} else {
- op.write(path, "test_scan").await?;
+ op.write(&format!("{parent}/{path}"), "test_scan").await?;
}
}
- let w = op.scan("x/").await?;
+ let w = op.scan(&format!("{parent}/x/")).await?;
let actual = w
.try_collect::<Vec<_>>()
.await?
.into_iter()
- .map(|v| v.path().to_string())
+ .map(|v| {
+ v.path()
+ .strip_prefix(&format!("{parent}/"))
+ .unwrap()
+ .to_string()
+ })
.collect::<HashSet<_>>();
debug!("walk top down: {:?}", actual);
@@ -328,24 +330,29 @@ pub async fn test_scan(op: Operator) -> Result<()> {
// Remove all should remove all in this path.
pub async fn test_remove_all(op: Operator) -> Result<()> {
+ let parent = uuid::Uuid::new_v4().to_string();
+
let expected = vec![
"x/", "x/y", "x/x/", "x/x/y", "x/x/x/", "x/x/x/y", "x/x/x/x/",
];
for path in expected.iter() {
if path.ends_with('/') {
- op.create_dir(path).await?;
+ op.create_dir(&format!("{parent}/{path}")).await?;
} else {
- op.write(path, "test_remove_all").await?;
+ op.write(&format!("{parent}/{path}"), "test_scan").await?;
}
}
- op.remove_all("x/").await?;
+ op.remove_all(&format!("{parent}/x/")).await?;
for path in expected.iter() {
if path.ends_with('/') {
continue;
}
- assert!(!op.is_exist(path).await?, "{path} should be removed")
+ assert!(
+ !op.is_exist(&format!("{parent}/{path}")).await?,
+ "{parent}/{path} should be removed"
+ )
}
Ok(())
}
diff --git a/core/tests/behavior/list_only.rs b/core/tests/behavior/list_only.rs
index a8a292a4..a7e86a45 100644
--- a/core/tests/behavior/list_only.rs
+++ b/core/tests/behavior/list_only.rs
@@ -29,28 +29,25 @@ use opendal::Operator;
macro_rules! behavior_list_only_test {
($service:ident, $($(#[$meta:meta])* $test:ident),*,) => {
paste::item! {
- mod [<services_ $service:lower _list_only>] {
+ $(
+ #[test]
$(
- #[tokio::test]
- $(
- #[$meta]
- )*
- async fn [< $test >]() -> anyhow::Result<()> {
- let op =
$crate::utils::init_service::<opendal::services::$service>(false);
- match op {
- Some(op) if op.info().can_list() &&
!op.info().can_write() => $crate::list_only::$test(op).await,
- Some(_) => {
- log::warn!("service {} doesn't support list,
ignored", opendal::Scheme::$service);
- Ok(())
- },
- None => {
- log::warn!("service {} not initiated,
ignored", opendal::Scheme::$service);
- Ok(())
- }
+ #[$meta]
+ )*
+ fn [<list_only_ $test >]() -> anyhow::Result<()> {
+ match OPERATOR.as_ref() {
+ Some(op) if op.info().can_list() &&
!op.info().can_write() =>
RUNTIME.block_on($crate::list_only::$test(op.clone())),
+ Some(_) => {
+ log::warn!("service {} doesn't support list,
ignored", opendal::Scheme::$service);
+ Ok(())
+ },
+ None => {
+ log::warn!("service {} not initiated, ignored",
opendal::Scheme::$service);
+ Ok(())
}
}
- )*
- }
+ }
+ )*
}
};
}
diff --git a/core/tests/behavior/main.rs b/core/tests/behavior/main.rs
index 63d63b16..c9038d05 100644
--- a/core/tests/behavior/main.rs
+++ b/core/tests/behavior/main.rs
@@ -46,32 +46,48 @@ mod utils;
/// Update function list while changed.
macro_rules! behavior_tests {
($($service:ident),*) => {
- $(
- // can_read && !can_write
- behavior_read_tests!($service);
- // can_read && !can_write && can_blocking
- behavior_blocking_read_tests!($service);
- // can_read && can_write
- behavior_write_tests!($service);
- // can_read && can_write && can_blocking
- behavior_blocking_write_tests!($service);
- // can_read && can_write && can_copy
- behavior_copy_tests!($service);
- // can read && can_write && can_blocking && can_copy
- behavior_blocking_copy_tests!($service);
- // can_read && can_write && can_move
- behavior_rename_tests!($service);
- // can_read && can_write && can_blocking && can_move
- behavior_blocking_rename_tests!($service);
- // can_read && can_write && can_list
- behavior_list_tests!($service);
- // can_read && can_write && can_presign
- behavior_presign_tests!($service);
- // can_read && can_write && can_blocking && can_list
- behavior_blocking_list_tests!($service);
- // can_list && !can_write
- behavior_list_only_tests!($service);
- )*
+ paste::item! {
+ $(
+ mod [<services_ $service:lower>] {
+ use once_cell::sync::Lazy;
+
+ static RUNTIME: Lazy<tokio::runtime::Runtime> =
Lazy::new(|| {
+ tokio::runtime::Builder::new_multi_thread()
+ .enable_all()
+ .build()
+ .unwrap()
+ });
+ static OPERATOR: Lazy<Option<opendal::Operator>> =
Lazy::new(||
+
$crate::utils::init_service::<opendal::services::$service>()
+ );
+
+ // can_read && !can_write
+ behavior_read_tests!($service);
+ // can_read && !can_write && can_blocking
+ behavior_blocking_read_tests!($service);
+ // can_read && can_write
+ behavior_write_tests!($service);
+ // can_read && can_write && can_blocking
+ behavior_blocking_write_tests!($service);
+ // can_read && can_write && can_copy
+ behavior_copy_tests!($service);
+ // can read && can_write && can_blocking && can_copy
+ behavior_blocking_copy_tests!($service);
+ // can_read && can_write && can_move
+ behavior_rename_tests!($service);
+ // can_read && can_write && can_blocking && can_move
+ behavior_blocking_rename_tests!($service);
+ // can_read && can_write && can_list
+ behavior_list_tests!($service);
+ // can_read && can_write && can_presign
+ behavior_presign_tests!($service);
+ // can_read && can_write && can_blocking && can_list
+ behavior_blocking_list_tests!($service);
+ // can_list && !can_write
+ behavior_list_only_tests!($service);
+ }
+ )*
+ }
};
}
diff --git a/core/tests/behavior/presign.rs b/core/tests/behavior/presign.rs
index 8e42f902..ae4ae039 100644
--- a/core/tests/behavior/presign.rs
+++ b/core/tests/behavior/presign.rs
@@ -37,28 +37,25 @@ use super::utils::*;
macro_rules! behavior_presign_test {
($service:ident, $($(#[$meta:meta])* $test:ident),*,) => {
paste::item! {
- mod [<services_ $service:lower _presign>] {
+ $(
+ #[test]
$(
- #[tokio::test]
- $(
- #[$meta]
- )*
- async fn [< $test >]() -> anyhow::Result<()> {
- let op =
$crate::utils::init_service::<opendal::services::$service>(true);
- match op {
- Some(op) if op.info().can_read() &&
op.info().can_write() && op.info().can_presign() =>
$crate::presign::$test(op).await,
- Some(_) => {
- log::warn!("service {} doesn't support
presign, ignored", opendal::Scheme::$service);
- Ok(())
- },
- None => {
- log::warn!("service {} not initiated,
ignored", opendal::Scheme::$service);
- Ok(())
- }
+ #[$meta]
+ )*
+ fn [<presign_ $test >]() -> anyhow::Result<()> {
+ match OPERATOR.as_ref() {
+ Some(op) if op.info().can_read() &&
op.info().can_write() && op.info().can_presign() =>
RUNTIME.block_on($crate::presign::$test(op.clone())),
+ Some(_) => {
+ log::warn!("service {} doesn't support presign,
ignored", opendal::Scheme::$service);
+ Ok(())
+ },
+ None => {
+ log::warn!("service {} not initiated, ignored",
opendal::Scheme::$service);
+ Ok(())
}
}
- )*
- }
+ }
+ )*
}
};
}
diff --git a/core/tests/behavior/read_only.rs b/core/tests/behavior/read_only.rs
index 71054a74..6d3194cc 100644
--- a/core/tests/behavior/read_only.rs
+++ b/core/tests/behavior/read_only.rs
@@ -30,28 +30,25 @@ use sha2::Sha256;
macro_rules! behavior_read_test {
($service:ident, $($(#[$meta:meta])* $test:ident),*,) => {
paste::item! {
- mod [<services_ $service:lower _read_only>] {
+ $(
+ #[test]
$(
- #[tokio::test]
- $(
- #[$meta]
- )*
- async fn [< $test >]() -> anyhow::Result<()> {
- let op =
$crate::utils::init_service::<opendal::services::$service>(false);
- match op {
- Some(op) if op.info().can_read() &&
!op.info().can_write() => $crate::read_only::$test(op).await,
- Some(_) => {
- log::warn!("service {} doesn't support
read_only, ignored", opendal::Scheme::$service);
- Ok(())
- },
- None => {
- log::warn!("service {} not initiated,
ignored", opendal::Scheme::$service);
- Ok(())
- }
+ #[$meta]
+ )*
+ fn [<read_only_ $test >]() -> anyhow::Result<()> {
+ match OPERATOR.as_ref() {
+ Some(op) if op.info().can_read() &&
!op.info().can_write() =>
RUNTIME.block_on($crate::read_only::$test(op.clone())),
+ Some(_) => {
+ log::warn!("service {} doesn't support read_only,
ignored", opendal::Scheme::$service);
+ Ok(())
+ },
+ None => {
+ log::warn!("service {} not initiated, ignored",
opendal::Scheme::$service);
+ Ok(())
}
}
- )*
- }
+ }
+ )*
}
};
}
diff --git a/core/tests/behavior/rename.rs b/core/tests/behavior/rename.rs
index 14744869..cf4c0173 100644
--- a/core/tests/behavior/rename.rs
+++ b/core/tests/behavior/rename.rs
@@ -29,30 +29,27 @@ use super::utils::*;
macro_rules! behavior_rename_test {
($service:ident, $($(#[$meta:meta])* $test:ident),*,) => {
paste::item! {
- mod [<services_ $service:lower _rename>] {
+ $(
+ #[test]
$(
- #[tokio::test]
- $(
- #[$meta]
- )*
- async fn [< $test >]() -> anyhow::Result<()> {
- let op =
$crate::utils::init_service::<opendal::services::$service>(true);
- match op {
- Some(op) if op.info().can_read()
- && op.info().can_write()
- && op.info().can_rename() =>
$crate::rename::$test(op).await,
- Some(_) => {
- log::warn!("service {} doesn't support rename,
ignored", opendal::Scheme::$service);
- Ok(())
- },
- None => {
- log::warn!("service {} not initiated,
ignored", opendal::Scheme::$service);
- Ok(())
- }
+ #[$meta]
+ )*
+ fn [<rename_ $test >]() -> anyhow::Result<()> {
+ match OPERATOR.as_ref() {
+ Some(op) if op.info().can_read()
+ && op.info().can_write()
+ && op.info().can_rename() =>
RUNTIME.block_on($crate::rename::$test(op.clone())),
+ Some(_) => {
+ log::warn!("service {} doesn't support rename,
ignored", opendal::Scheme::$service);
+ Ok(())
+ },
+ None => {
+ log::warn!("service {} not initiated, ignored",
opendal::Scheme::$service);
+ Ok(())
}
}
- )*
- }
+ }
+ )*
}
};
}
diff --git a/core/tests/behavior/utils.rs b/core/tests/behavior/utils.rs
index b909d436..5592ecfe 100644
--- a/core/tests/behavior/utils.rs
+++ b/core/tests/behavior/utils.rs
@@ -34,7 +34,7 @@ use sha2::Sha256;
///
/// - If `opendal_{schema}_test` is on, construct a new Operator with given
root.
/// - Else, returns a `None` to represent no valid config for operator.
-pub fn init_service<B: Builder>(random_root: bool) -> Option<Operator> {
+pub fn init_service<B: Builder>() -> Option<Operator> {
let _ = env_logger::builder().is_test(true).try_init();
let _ = dotenvy::dotenv();
@@ -54,7 +54,9 @@ pub fn init_service<B: Builder>(random_root: bool) ->
Option<Operator> {
return None;
}
- if random_root {
+ // Use random root unless OPENDAL_DISABLE_RANDOM_ROOT is set to true.
+ let disable_random_root =
env::var("OPENDAL_DISABLE_RANDOM_ROOT").unwrap_or_default() == "true";
+ if !disable_random_root {
let root = format!(
"{}{}/",
cfg.get("root").cloned().unwrap_or_else(|| "/".to_string()),
diff --git a/core/tests/behavior/write.rs b/core/tests/behavior/write.rs
index 7dffcb24..bf2d5c18 100644
--- a/core/tests/behavior/write.rs
+++ b/core/tests/behavior/write.rs
@@ -40,28 +40,25 @@ use super::utils::*;
macro_rules! behavior_write_test {
($service:ident, $($(#[$meta:meta])* $test:ident),*,) => {
paste::item! {
- mod [<services_ $service:lower _write>] {
+ $(
+ #[test]
$(
- #[tokio::test]
- $(
- #[$meta]
- )*
- async fn [< $test >]() -> anyhow::Result<()> {
- let op =
$crate::utils::init_service::<opendal::services::$service>(true);
- match op {
- Some(op) if op.info().can_read() &&
op.info().can_write() => $crate::write::$test(op).await,
- Some(_) => {
- log::warn!("service {} doesn't support write,
ignored", opendal::Scheme::$service);
- Ok(())
- },
- None => {
- log::warn!("service {} not initiated,
ignored", opendal::Scheme::$service);
- Ok(())
- }
+ #[$meta]
+ )*
+ fn [<write_ $test >]() -> anyhow::Result<()> {
+ match OPERATOR.as_ref() {
+ Some(op) if op.info().can_read() &&
op.info().can_write() => RUNTIME.block_on($crate::write::$test(op.clone())),
+ Some(_) => {
+ log::warn!("service {} doesn't support write,
ignored", opendal::Scheme::$service);
+ Ok(())
+ },
+ None => {
+ log::warn!("service {} not initiated, ignored",
opendal::Scheme::$service);
+ Ok(())
}
}
- )*
- }
+ }
+ )*
}
};
}