Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test file copying preserves mtime #335

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion commons/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ byte-unit = "5"
const_format = "0.2"
# TODO: Consolidate on either the regex crate or the fancy-regex crate, since this repo currently uses both.
fancy-regex = "0.13"
fs_extra = "1"
fs-err = "2"
fun_run = "0.2"
glob = "0.3"
Expand All @@ -32,6 +31,7 @@ sha2 = "0.10"
tempfile = "3"
thiserror = "1"
walkdir = "2"
cp_r = "0.5.2"

[dev-dependencies]
filetime = "0.2"
Expand Down
181 changes: 134 additions & 47 deletions commons/src/cache/app_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use crate::cache::clean::{lru_clean, FilesWithSize};
use crate::cache::in_app_dir_cache_layer::InAppDirCacheLayer;
use crate::cache::{CacheConfig, CacheError, KeepPath};
use byte_unit::{AdjustedByte, Byte, UnitType};
use fs_extra::dir::CopyOptions;
use libcnb::build::BuildContext;
use libcnb::data::layer::LayerName;
use std::path::Path;
Expand Down Expand Up @@ -148,22 +147,20 @@ impl AppCache {
fs_err::create_dir_all(&self.path).map_err(CacheError::IoError)?;
fs_err::create_dir_all(&self.cache).map_err(CacheError::IoError)?;

fs_extra::dir::move_dir(
&self.cache,
&self.path,
&CopyOptions {
overwrite: false,
skip_exist: true,
copy_inside: true,
content_only: true,
..CopyOptions::default()
},
)
.map_err(|error| CacheError::CopyCacheToAppError {
path: self.path.clone(),
cache: self.cache.clone(),
error,
})?;
cp_r::CopyOptions::new()
.create_destination(true)
// Do not overwrite
.filter(|to_file, _| {
let destination = self.path.join(to_file);
let exists = destination.try_exists().unwrap_or(false);
Ok(!exists)
})
.copy_tree(&self.cache, &self.path)
.map_err(|error| CacheError::CopyCacheToAppError {
path: self.path.clone(),
cache: self.cache.clone(),
error,
})?;

Ok(self)
}
Expand Down Expand Up @@ -276,21 +273,14 @@ pub fn build<B: libcnb::Buildpack>(
///
/// - If the copy command fails an `IoExtraError` will be raised.
fn preserve_path_save(store: &AppCache) -> Result<&AppCache, CacheError> {
fs_extra::dir::copy(
&store.path,
&store.cache,
&CopyOptions {
overwrite: true,
copy_inside: true, // Recursive
content_only: true, // Don't copy top level directory name
..CopyOptions::default()
},
)
.map_err(|error| CacheError::CopyAppToCacheError {
path: store.path.clone(),
cache: store.cache.clone(),
error,
})?;
cp_r::CopyOptions::new()
.create_destination(true)
.copy_tree(&store.path, &store.cache)
.map_err(|error| CacheError::CopyAppToCacheError {
path: store.path.clone(),
cache: store.cache.clone(),
error,
})?;

Ok(store)
}
Expand All @@ -306,21 +296,16 @@ fn preserve_path_save(store: &AppCache) -> Result<&AppCache, CacheError> {
///
/// - If the move command fails an `IoExtraError` will be raised.
fn remove_path_save(store: &AppCache) -> Result<&AppCache, CacheError> {
fs_extra::dir::move_dir(
&store.path,
&store.cache,
&CopyOptions {
overwrite: true,
copy_inside: true, // Recursive
content_only: true, // Don't copy top level directory name
..CopyOptions::default()
},
)
.map_err(|error| CacheError::DestructiveMoveAppToCacheError {
path: store.path.clone(),
cache: store.cache.clone(),
error,
})?;
cp_r::CopyOptions::new()
.create_destination(true)
.copy_tree(&store.path, &store.cache)
.map_err(|error| CacheError::DestructiveMoveAppToCacheError {
path: store.path.clone(),
cache: store.cache.clone(),
error,
})?;

fs_err::remove_dir_all(&store.path).map_err(CacheError::IoError)?;

Ok(store)
}
Expand Down Expand Up @@ -387,6 +372,31 @@ mod tests {
assert_eq!(layer_name!("cache_my_input"), layer);
}

#[test]
fn test_load_doesnot_clobber_files() {
let tmpdir = tempfile::tempdir().unwrap();
let cache_path = tmpdir.path().join("cache");
let app_path = tmpdir.path().join("app");
fs_err::create_dir_all(&cache_path).unwrap();
fs_err::create_dir_all(&app_path).unwrap();

fs_err::write(app_path.join("a.txt"), "app").unwrap();
fs_err::write(cache_path.join("a.txt"), "cache").unwrap();

let store = AppCache {
path: app_path.clone(),
cache: cache_path,
limit: Byte::from_u64(512),
keep_path: KeepPath::Runtime,
cache_state: CacheState::NewEmpty,
};

store.load().unwrap();

let contents = fs_err::read_to_string(app_path.join("a.txt")).unwrap();
assert_eq!("app", contents);
}

#[test]
fn test_copying_back_to_cache() {
let tmpdir = tempfile::tempdir().unwrap();
Expand Down Expand Up @@ -448,4 +458,81 @@ mod tests {
assert!(store.cache.join("lol.txt").exists());
assert!(!store.path.join("lol.txt").exists());
}

#[test]
fn mtime_preserved_keep_path_build_only() {
let tmpdir = tempfile::tempdir().unwrap();
let cache_path = tmpdir.path().join("cache");
let app_path = tmpdir.path().join("app");

fs_err::create_dir_all(&cache_path).unwrap();
fs_err::create_dir_all(&app_path).unwrap();

let store = AppCache {
path: app_path.clone(),
cache: cache_path,
limit: Byte::from_u64(512),
keep_path: KeepPath::BuildOnly,
cache_state: CacheState::NewEmpty,
};

let mtime = filetime::FileTime::from_unix_time(1000, 0);

let path = app_path.join("lol.txt");
fs_err::write(&path, "hahaha").unwrap();
filetime::set_file_mtime(&path, mtime).unwrap();

store.save().unwrap();

let metadata = fs_err::metadata(store.cache.join("lol.txt")).unwrap();
let actual = filetime::FileTime::from_last_modification_time(&metadata);
assert_eq!(mtime, actual);

assert!(!store.path.join("lol.txt").exists());

store.load().unwrap();

let metadata = fs_err::metadata(store.cache.join("lol.txt")).unwrap();
let actual = filetime::FileTime::from_last_modification_time(&metadata);
assert_eq!(mtime, actual);
}

#[test]
fn mtime_preserved_keep_path_runtime() {
let tmpdir = tempfile::tempdir().unwrap();
let cache_path = tmpdir.path().join("cache");
let app_path = tmpdir.path().join("app");

fs_err::create_dir_all(&cache_path).unwrap();
fs_err::create_dir_all(&app_path).unwrap();

let store = AppCache {
path: app_path.clone(),
cache: cache_path,
limit: Byte::from_u64(512),
keep_path: KeepPath::Runtime,
cache_state: CacheState::NewEmpty,
};

let mtime = filetime::FileTime::from_unix_time(1000, 0);

let path = app_path.join("lol.txt");
fs_err::write(&path, "hahaha").unwrap();
filetime::set_file_mtime(&path, mtime).unwrap();

store.save().unwrap();

let metadata = fs_err::metadata(store.cache.join("lol.txt")).unwrap();
let actual = filetime::FileTime::from_last_modification_time(&metadata);
assert_eq!(mtime, actual);

fs_err::remove_dir_all(&app_path).unwrap();
assert!(!store.path.join("lol.txt").exists());

store.load().unwrap();

let metadata = fs_err::metadata(store.cache.join("lol.txt")).unwrap();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be store.path and not store.cache

let actual = filetime::FileTime::from_last_modification_time(&metadata);
assert_eq!(mtime, actual);
}
}
6 changes: 3 additions & 3 deletions commons/src/cache/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,21 @@ pub enum CacheError {
CopyCacheToAppError {
path: PathBuf,
cache: PathBuf,
error: fs_extra::error::Error,
error: cp_r::Error,
},

#[error("Could not copy files from the application to cache.\nFrom: {path} To: {cache}\nError: {error}")]
CopyAppToCacheError {
path: PathBuf,
cache: PathBuf,
error: fs_extra::error::Error,
error: cp_r::Error,
},

#[error("Could not move files out of the application to the cache.\nFrom: {path} To: {cache}\nError: {error}")]
DestructiveMoveAppToCacheError {
path: PathBuf,
cache: PathBuf,
error: fs_extra::error::Error,
error: cp_r::Error,
},

#[error("IO error: {0}")]
Expand Down
Loading