aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/tests/package/archiver
diff options
context:
space:
mode:
Diffstat (limited to 'crates/shirabe/tests/package/archiver')
-rw-r--r--crates/shirabe/tests/package/archiver/archivable_files_finder_test.rs236
-rw-r--r--crates/shirabe/tests/package/archiver/archive_manager_test.rs10
-rw-r--r--crates/shirabe/tests/package/archiver/phar_archiver_test.rs125
-rw-r--r--crates/shirabe/tests/package/archiver/zip_archiver_test.rs176
4 files changed, 491 insertions, 56 deletions
diff --git a/crates/shirabe/tests/package/archiver/archivable_files_finder_test.rs b/crates/shirabe/tests/package/archiver/archivable_files_finder_test.rs
index 48f0fc5..aed0dc3 100644
--- a/crates/shirabe/tests/package/archiver/archivable_files_finder_test.rs
+++ b/crates/shirabe/tests/package/archiver/archivable_files_finder_test.rs
@@ -1,45 +1,235 @@
//! ref: composer/tests/Composer/Test/Package/Archiver/ArchivableFilesFinderTest.php
-/// Builds a temp directory tree of fixture files under a unique tmp dir; the Filesystem and
-/// getUniqueTmpDirectory infrastructure is not ported.
-#[allow(dead_code)]
-fn set_up() -> String {
- todo!()
-}
+use shirabe::package::archiver::ArchivableFilesFinder;
+use shirabe::util::Filesystem;
+use shirabe_external_packages::composer::pcre::Preg;
+use shirabe_php_shim::{dirname, file_put_contents, preg_quote};
+use tempfile::TempDir;
-#[allow(dead_code)]
-fn tear_down(_sources: &str) {
- // Removes the temp directory tree created in set_up.
- todo!()
+struct SetUp {
+ _sources_dir: TempDir,
+ sources: String,
+ fs: Filesystem,
}
-#[allow(dead_code)]
-struct TearDown {
- sources: String,
+fn set_up() -> SetUp {
+ let mut fs = Filesystem::new(None);
+
+ let sources_dir = TempDir::new().unwrap();
+ let sources = fs.normalize_path(&sources_dir.path().canonicalize().unwrap().to_string_lossy());
+
+ let file_tree = [
+ ".foo",
+ "A/prefixA.foo",
+ "A/prefixB.foo",
+ "A/prefixC.foo",
+ "A/prefixD.foo",
+ "A/prefixE.foo",
+ "A/prefixF.foo",
+ "B/sub/prefixA.foo",
+ "B/sub/prefixB.foo",
+ "B/sub/prefixC.foo",
+ "B/sub/prefixD.foo",
+ "B/sub/prefixE.foo",
+ "B/sub/prefixF.foo",
+ "C/prefixA.foo",
+ "C/prefixB.foo",
+ "C/prefixC.foo",
+ "C/prefixD.foo",
+ "C/prefixE.foo",
+ "C/prefixF.foo",
+ "D/prefixA",
+ "D/prefixB",
+ "D/prefixC",
+ "D/prefixD",
+ "D/prefixE",
+ "D/prefixF",
+ "E/subtestA.foo",
+ "F/subtestA.foo",
+ "G/subtestA.foo",
+ "H/subtestA.foo",
+ "I/J/subtestA.foo",
+ "K/dirJ/subtestA.foo",
+ "toplevelA.foo",
+ "toplevelB.foo",
+ "prefixA.foo",
+ "prefixB.foo",
+ "prefixC.foo",
+ "prefixD.foo",
+ "prefixE.foo",
+ "prefixF.foo",
+ "parameters.yml",
+ "parameters.yml.dist",
+ "!important!.txt",
+ "!important_too!.txt",
+ "#weirdfile",
+ ];
+
+ for relative_path in file_tree {
+ let path = format!("{}/{}", sources, relative_path);
+ fs.ensure_directory_exists(&dirname(&path)).unwrap();
+ file_put_contents(&path, b"");
+ }
+
+ SetUp {
+ _sources_dir: sources_dir,
+ sources,
+ fs,
+ }
}
-impl Drop for TearDown {
- fn drop(&mut self) {
- tear_down(&self.sources);
+fn get_archivable_files(set_up: &SetUp, finder: ArchivableFilesFinder) -> Vec<String> {
+ let mut files: Vec<String> = vec![];
+ for file in finder {
+ if !file.is_dir() {
+ let real_path = file.canonicalize().unwrap();
+ files.push(Preg::replace(
+ &format!("#^{}#", preg_quote(&set_up.sources, Some('#'))),
+ "",
+ &set_up.fs.normalize_path(&real_path.to_string_lossy()),
+ ));
+ }
}
+
+ files.sort();
+
+ files
+}
+
+fn assert_archivable_files(set_up: &SetUp, finder: ArchivableFilesFinder, expected_files: &[&str]) {
+ let actual_files = get_archivable_files(set_up, finder);
+
+ let expected_files: Vec<String> = expected_files.iter().map(|s| s.to_string()).collect();
+ assert_eq!(expected_files, actual_files);
}
-// These set up a temp directory tree (including a git repo) and assert the files the finder
-// selects with manual/git/skip excludes; the git-backed fixture setup is not ported.
-#[ignore = "setUp needs TestCase::getUniqueTmpDirectory to build the on-disk fixture tree; not ported"]
+// The manual exclude patterns (e.g. `.*`, `prefixC.*`) compile, via ComposerExcludeFilter, to
+// look-ahead regexes like `(?=[^\.])...(?=$|/)`, which the regex crate cannot compile.
+#[ignore = "ComposerExcludeFilter builds look-ahead regexes the regex crate does not support"]
#[test]
fn test_manual_excludes() {
- todo!()
+ let set_up = set_up();
+
+ let excludes = vec![
+ "prefixB.foo".to_string(),
+ "!/prefixB.foo".to_string(),
+ "/prefixA.foo".to_string(),
+ "prefixC.*".to_string(),
+ "!*/*/*/prefixC.foo".to_string(),
+ ".*".to_string(),
+ ];
+
+ let finder = ArchivableFilesFinder::new(&set_up.sources, excludes, false).unwrap();
+
+ assert_archivable_files(
+ &set_up,
+ finder,
+ &[
+ "/!important!.txt",
+ "/!important_too!.txt",
+ "/#weirdfile",
+ "/A/prefixA.foo",
+ "/A/prefixD.foo",
+ "/A/prefixE.foo",
+ "/A/prefixF.foo",
+ "/B/sub/prefixA.foo",
+ "/B/sub/prefixC.foo",
+ "/B/sub/prefixD.foo",
+ "/B/sub/prefixE.foo",
+ "/B/sub/prefixF.foo",
+ "/C/prefixA.foo",
+ "/C/prefixD.foo",
+ "/C/prefixE.foo",
+ "/C/prefixF.foo",
+ "/D/prefixA",
+ "/D/prefixB",
+ "/D/prefixC",
+ "/D/prefixD",
+ "/D/prefixE",
+ "/D/prefixF",
+ "/E/subtestA.foo",
+ "/F/subtestA.foo",
+ "/G/subtestA.foo",
+ "/H/subtestA.foo",
+ "/I/J/subtestA.foo",
+ "/K/dirJ/subtestA.foo",
+ "/parameters.yml",
+ "/parameters.yml.dist",
+ "/prefixB.foo",
+ "/prefixD.foo",
+ "/prefixE.foo",
+ "/prefixF.foo",
+ "/toplevelA.foo",
+ "/toplevelB.foo",
+ ],
+ );
}
-#[ignore = "setUp needs TestCase::getUniqueTmpDirectory plus skipIfNotExecutable/Process::fromShellCommandline/PharData/RecursiveIteratorIterator; none ported"]
+// getArchivedFiles drives a git pipeline (Process::fromShellCommandline) and reads the produced
+// zip back through PharData + RecursiveIteratorIterator; neither the process helper nor the zip
+// archive reader is ported, so this case cannot run.
+#[ignore = "getArchivedFiles needs a git process pipeline plus PharData/RecursiveIteratorIterator zip reading; not ported"]
#[test]
fn test_git_excludes() {
todo!()
}
-#[ignore = "setUp needs TestCase::getUniqueTmpDirectory to build the on-disk fixture tree; not ported"]
#[test]
fn test_skip_excludes() {
- todo!()
+ let set_up = set_up();
+
+ let excludes = vec!["prefixB.foo".to_string()];
+
+ let finder = ArchivableFilesFinder::new(&set_up.sources, excludes, true).unwrap();
+
+ assert_archivable_files(
+ &set_up,
+ finder,
+ &[
+ "/!important!.txt",
+ "/!important_too!.txt",
+ "/#weirdfile",
+ "/.foo",
+ "/A/prefixA.foo",
+ "/A/prefixB.foo",
+ "/A/prefixC.foo",
+ "/A/prefixD.foo",
+ "/A/prefixE.foo",
+ "/A/prefixF.foo",
+ "/B/sub/prefixA.foo",
+ "/B/sub/prefixB.foo",
+ "/B/sub/prefixC.foo",
+ "/B/sub/prefixD.foo",
+ "/B/sub/prefixE.foo",
+ "/B/sub/prefixF.foo",
+ "/C/prefixA.foo",
+ "/C/prefixB.foo",
+ "/C/prefixC.foo",
+ "/C/prefixD.foo",
+ "/C/prefixE.foo",
+ "/C/prefixF.foo",
+ "/D/prefixA",
+ "/D/prefixB",
+ "/D/prefixC",
+ "/D/prefixD",
+ "/D/prefixE",
+ "/D/prefixF",
+ "/E/subtestA.foo",
+ "/F/subtestA.foo",
+ "/G/subtestA.foo",
+ "/H/subtestA.foo",
+ "/I/J/subtestA.foo",
+ "/K/dirJ/subtestA.foo",
+ "/parameters.yml",
+ "/parameters.yml.dist",
+ "/prefixA.foo",
+ "/prefixB.foo",
+ "/prefixC.foo",
+ "/prefixD.foo",
+ "/prefixE.foo",
+ "/prefixF.foo",
+ "/toplevelA.foo",
+ "/toplevelB.foo",
+ ],
+ );
}
diff --git a/crates/shirabe/tests/package/archiver/archive_manager_test.rs b/crates/shirabe/tests/package/archiver/archive_manager_test.rs
index 2712da6..fdc4bf0 100644
--- a/crates/shirabe/tests/package/archiver/archive_manager_test.rs
+++ b/crates/shirabe/tests/package/archiver/archive_manager_test.rs
@@ -29,31 +29,31 @@ impl Drop for TearDown {
// the filename-derivation helpers over packages; the archiving and fixture setup are not
// ported.
#[test]
-#[ignore = "setupPackage requires CompletePackage::setSourceType, not exposed on the package handle API; also needs Factory-built ArchiveManager (set_up) which is unported"]
+#[ignore = "needs the Factory/FactoryMock-built ArchiveManager (createDownloadManager/createHttpDownloader/FactoryMock::createConfig) in set_up, which is unported"]
fn test_unknown_format() {
todo!()
}
#[test]
-#[ignore = "needs setupGitRepo + PharData tar archiving and Factory-built ArchiveManager (set_up), and setupPackage's setSourceType which is not exposed on the package handle API"]
+#[ignore = "needs setupGitRepo + PharData tar archiving and the Factory/FactoryMock-built ArchiveManager (set_up), which is unported"]
fn test_archive_tar() {
todo!()
}
#[test]
-#[ignore = "needs setupGitRepo + PharData tar archiving and Factory-built ArchiveManager (set_up), and setupPackage's setSourceType which is not exposed on the package handle API"]
+#[ignore = "needs setupGitRepo + PharData tar archiving and the Factory/FactoryMock-built ArchiveManager (set_up), which is unported"]
fn test_archive_custom_file_name() {
todo!()
}
#[test]
-#[ignore = "setupPackage requires CompletePackage::setSourceType, not exposed on the package handle API; also needs Factory-built ArchiveManager (set_up) which is unported"]
+#[ignore = "needs the Factory/FactoryMock-built ArchiveManager (createDownloadManager/createHttpDownloader/FactoryMock::createConfig) in set_up, which is unported"]
fn test_get_package_filename_parts() {
todo!()
}
#[test]
-#[ignore = "setupPackage requires CompletePackage::setSourceType, not exposed on the package handle API; also needs Factory-built ArchiveManager (set_up) which is unported"]
+#[ignore = "needs the Factory/FactoryMock-built ArchiveManager (createDownloadManager/createHttpDownloader/FactoryMock::createConfig) in set_up, which is unported"]
fn test_get_package_filename() {
todo!()
}
diff --git a/crates/shirabe/tests/package/archiver/phar_archiver_test.rs b/crates/shirabe/tests/package/archiver/phar_archiver_test.rs
index 385177b..fe6185d 100644
--- a/crates/shirabe/tests/package/archiver/phar_archiver_test.rs
+++ b/crates/shirabe/tests/package/archiver/phar_archiver_test.rs
@@ -1,13 +1,130 @@
//! ref: composer/tests/Composer/Test/Package/Archiver/PharArchiverTest.php
-#[ignore = "setUp/setupDummyRepo need TestCase::getUniqueTmpDirectory plus Filesystem to build the on-disk fixture tree; not ported"]
+use shirabe::package::archiver::{ArchiverInterface, PharArchiver};
+use shirabe::package::handle::CompletePackageHandle;
+use shirabe::util::{Filesystem, Platform};
+use shirabe_php_shim::{dirname, file_exists, file_put_contents, mkdir, realpath};
+use tempfile::TempDir;
+
+// ref: ArchiverTestCase.
+struct ArchiverTestCase {
+ filesystem: Filesystem,
+ test_dir: String,
+ _test_dir_guard: TempDir,
+}
+
+impl ArchiverTestCase {
+ fn set_up() -> Self {
+ let guard = TempDir::new().unwrap();
+ let test_dir = guard.path().to_string_lossy().to_string();
+ Self {
+ filesystem: Filesystem::new(None),
+ test_dir,
+ _test_dir_guard: guard,
+ }
+ }
+
+ fn setup_package(&self) -> CompletePackageHandle {
+ let package = CompletePackageHandle::new(
+ "archivertest/archivertest".to_string(),
+ "master".to_string(),
+ "master".to_string(),
+ );
+ package.set_source_url(Some(realpath(&self.test_dir).unwrap_or_default()));
+ package.set_source_reference(Some("master".to_string()));
+ package.__set_source_type(Some("git".to_string()));
+
+ package
+ }
+
+ fn setup_dummy_repo(&self) {
+ let current_work_dir = Platform::get_cwd(false).unwrap();
+ std::env::set_current_dir(&self.test_dir).unwrap();
+
+ self.write_file("file.txt", "content", &current_work_dir);
+ self.write_file("foo/bar/baz", "content", &current_work_dir);
+ self.write_file("foo/bar/ignoreme", "content", &current_work_dir);
+ self.write_file("x/baz", "content", &current_work_dir);
+ self.write_file("x/includeme", "content", &current_work_dir);
+
+ std::env::set_current_dir(&current_work_dir).unwrap();
+ }
+
+ fn write_file(&self, path: &str, content: &str, current_work_dir: &str) {
+ if !file_exists(dirname(path)) {
+ mkdir(&dirname(path), 0o777, true);
+ }
+
+ let result = file_put_contents(path, content.as_bytes());
+ if result.is_none() {
+ std::env::set_current_dir(current_work_dir).unwrap();
+ panic!("Could not save file.");
+ }
+ }
+}
+
+#[ignore = "PharArchiver::archive builds the archive via PharData, which is todo!() in the php-shim"]
#[test]
fn test_tar_archive() {
- todo!()
+ let mut test_case = ArchiverTestCase::set_up();
+
+ test_case.setup_dummy_repo();
+ let package = test_case.setup_package();
+ let target_dir = TempDir::new().unwrap();
+ let target = format!(
+ "{}/composer_archiver_test.tar",
+ target_dir.path().to_string_lossy()
+ );
+
+ let archiver = PharArchiver::new();
+ archiver
+ .archive(
+ package.get_source_url().unwrap(),
+ target.clone(),
+ "tar".to_string(),
+ vec![
+ "foo/bar".to_string(),
+ "baz".to_string(),
+ "!/foo/bar/baz".to_string(),
+ ],
+ false,
+ )
+ .unwrap();
+ assert!(file_exists(&target));
+
+ test_case
+ .filesystem
+ .remove_directory(dirname(&target))
+ .unwrap();
}
-#[ignore = "setUp/setupDummyRepo need TestCase::getUniqueTmpDirectory plus Filesystem to build the on-disk fixture tree; not ported"]
+#[ignore = "PharArchiver::archive builds the archive via PharData, which is todo!() in the php-shim"]
#[test]
fn test_zip_archive() {
- todo!()
+ let mut test_case = ArchiverTestCase::set_up();
+
+ test_case.setup_dummy_repo();
+ let package = test_case.setup_package();
+ let target_dir = TempDir::new().unwrap();
+ let target = format!(
+ "{}/composer_archiver_test.zip",
+ target_dir.path().to_string_lossy()
+ );
+
+ let archiver = PharArchiver::new();
+ archiver
+ .archive(
+ package.get_source_url().unwrap(),
+ target.clone(),
+ "zip".to_string(),
+ vec![],
+ false,
+ )
+ .unwrap();
+ assert!(file_exists(&target));
+
+ test_case
+ .filesystem
+ .remove_directory(dirname(&target))
+ .unwrap();
}
diff --git a/crates/shirabe/tests/package/archiver/zip_archiver_test.rs b/crates/shirabe/tests/package/archiver/zip_archiver_test.rs
index 5b51ce9..7785976 100644
--- a/crates/shirabe/tests/package/archiver/zip_archiver_test.rs
+++ b/crates/shirabe/tests/package/archiver/zip_archiver_test.rs
@@ -1,46 +1,174 @@
//! ref: composer/tests/Composer/Test/Package/Archiver/ZipArchiverTest.php
-/// Creates the Filesystem/ProcessExecutor and a unique tmp testDir; that infrastructure is
-/// not ported. Returns testDir.
-#[allow(dead_code)]
-fn set_up() -> String {
- todo!()
-}
-
-/// Unlinks the zip files collected in filesToCleanup, then (parent) removes testDir.
-#[allow(dead_code)]
-fn tear_down(_files_to_cleanup: &[String], _test_dir: &str) {
- todo!()
-}
+use indexmap::IndexMap;
+use shirabe::package::archiver::{ArchiverInterface, ZipArchiver};
+use shirabe::package::handle::CompletePackageHandle;
+use shirabe::util::Platform;
+use shirabe_php_shim::{
+ ZipArchive, class_exists, dirname, file_exists, file_put_contents, mkdir, realpath,
+ sys_get_temp_dir, unlink,
+};
+use tempfile::TempDir;
-#[allow(dead_code)]
-struct TearDown {
- files_to_cleanup: Vec<String>,
+// ref: ArchiverTestCase. Holds the unique tmp testDir plus the ZipArchiverTest cleanup list.
+struct ArchiverTestCase {
test_dir: String,
+ _test_dir_guard: TempDir,
+ files_to_cleanup: Vec<String>,
}
-impl Drop for TearDown {
+impl Drop for ArchiverTestCase {
fn drop(&mut self) {
- tear_down(&self.files_to_cleanup, &self.test_dir);
+ for file in &self.files_to_cleanup {
+ unlink(file);
+ }
}
}
-// ZipArchiver::archive builds a zip via ZipArchive, which is todo!() in the php-shim.
+impl ArchiverTestCase {
+ fn set_up() -> Self {
+ let guard = TempDir::new().unwrap();
+ let test_dir = guard.path().to_string_lossy().to_string();
+ Self {
+ test_dir,
+ _test_dir_guard: guard,
+ files_to_cleanup: vec![],
+ }
+ }
+
+ fn setup_package(&self) -> CompletePackageHandle {
+ let package = CompletePackageHandle::new(
+ "archivertest/archivertest".to_string(),
+ "master".to_string(),
+ "master".to_string(),
+ );
+ package.set_source_url(Some(realpath(&self.test_dir).unwrap_or_default()));
+ package.set_source_reference(Some("master".to_string()));
+ package.__set_source_type(Some("git".to_string()));
+
+ package
+ }
+ fn assert_zip_archive(&mut self, mut files: IndexMap<String, Option<String>>) {
+ if !class_exists("ZipArchive") {
+ // markTestSkipped('Cannot run ZipArchiverTest, missing class "ZipArchive".')
+ return;
+ }
+
+ self.setup_dummy_repo(&mut files);
+ let package = self.setup_package();
+ let target = format!("{}/composer_archiver_test.zip", sys_get_temp_dir());
+ self.files_to_cleanup.push(target.clone());
+
+ let archiver = ZipArchiver::new();
+ archiver
+ .archive(
+ package.get_source_url().unwrap(),
+ target.clone(),
+ "zip".to_string(),
+ vec![],
+ false,
+ )
+ .unwrap();
+ assert!(file_exists(&target));
+ let mut zip = ZipArchive::new();
+ let res = zip.open(&target, 0);
+ assert!(res.is_ok(), "Failed asserting that Zip file can be opened");
+
+ let mut zip_contents: IndexMap<String, Option<String>> = IndexMap::new();
+ for i in 0..zip.num_files {
+ let path = zip.get_name_index(i);
+ zip_contents.insert(path.clone(), zip.get_from_name(&path));
+ }
+ zip.close();
+
+ let files: IndexMap<String, Option<String>> = files;
+ assert_eq!(
+ files, zip_contents,
+ "Failed asserting that Zip created with the ZipArchiver contains all files from the repository."
+ );
+ }
+
+ fn setup_dummy_repo(&self, files: &mut IndexMap<String, Option<String>>) {
+ let current_work_dir = Platform::get_cwd(false).unwrap();
+ std::env::set_current_dir(&self.test_dir).unwrap();
+ let paths: Vec<String> = files.keys().cloned().collect();
+ for path in paths {
+ if files[&path].is_none() {
+ files.insert(path.clone(), Some("content".to_string()));
+ }
+ self.write_file(&path, files[&path].clone().unwrap(), &current_work_dir);
+ }
+
+ std::env::set_current_dir(&current_work_dir).unwrap();
+ }
+
+ fn write_file(&self, path: &str, content: String, current_work_dir: &str) {
+ if !file_exists(dirname(path)) {
+ mkdir(&dirname(path), 0o777, true);
+ }
+
+ let result = file_put_contents(path, content.as_bytes());
+ if result.is_none() {
+ std::env::set_current_dir(current_work_dir).unwrap();
+ panic!("Could not save file.");
+ }
+ }
+}
+
+#[ignore = "ZipArchiver::archive and the ZipArchive reader (open/get_from_name/...) are todo!() in the php-shim"]
#[test]
-#[ignore = "ArchiverTestCase infrastructure (setup_package/setup_dummy_repo/test_dir) not ported; ZipArchive shim lacks get_from_name"]
fn test_simple_files() {
- todo!()
+ let mut test_case = ArchiverTestCase::set_up();
+
+ let mut files: IndexMap<String, Option<String>> = IndexMap::new();
+ files.insert("file.txt".to_string(), None);
+ files.insert("foo/bar/baz".to_string(), None);
+ files.insert("x/baz".to_string(), None);
+ files.insert("x/includeme".to_string(), None);
+
+ if !Platform::is_windows() {
+ files.insert(
+ format!("zfoo{}/file.txt", Platform::get_cwd(false).unwrap()),
+ None,
+ );
+ }
+
+ test_case.assert_zip_archive(files);
}
+#[ignore = "ZipArchiver::archive and the ZipArchive reader (open/get_from_name/...) are todo!() in the php-shim"]
#[test]
-#[ignore = "ArchiverTestCase infrastructure (setup_package/setup_dummy_repo/test_dir) not ported; ZipArchive shim lacks get_from_name"]
fn test_gitignore_exclude_negation() {
- todo!()
+ for include in ["!/docs", "!/docs/"] {
+ let mut test_case = ArchiverTestCase::set_up();
+
+ let mut files: IndexMap<String, Option<String>> = IndexMap::new();
+ files.insert(
+ ".gitignore".to_string(),
+ Some(format!("/*\n.*\n!.git*\n{}", include)),
+ );
+ files.insert("docs/README.md".to_string(), Some("# The doc".to_string()));
+
+ test_case.assert_zip_archive(files);
+ }
}
+#[ignore = "ZipArchiver::archive and the ZipArchive reader (open/get_from_name/...) are todo!() in the php-shim"]
#[test]
-#[ignore = "ArchiverTestCase infrastructure (setup_package/setup_dummy_repo/test_dir) not ported; ZipArchive shim lacks get_from_name"]
fn test_folder_with_backslashes() {
- todo!()
+ if Platform::is_windows() {
+ // markTestSkipped('Folder names cannot contain backslashes on Windows.')
+ return;
+ }
+
+ let mut test_case = ArchiverTestCase::set_up();
+
+ let mut files: IndexMap<String, Option<String>> = IndexMap::new();
+ files.insert(
+ "folder\\with\\backslashes/README.md".to_string(),
+ Some("# doc".to_string()),
+ );
+
+ test_case.assert_zip_archive(files);
}