diff --git a/jimfs/src/main/java/com/google/common/jimfs/BasicAttributeProvider.java b/jimfs/src/main/java/com/google/common/jimfs/BasicAttributeProvider.java index 35dc8112..247aa163 100644 --- a/jimfs/src/main/java/com/google/common/jimfs/BasicAttributeProvider.java +++ b/jimfs/src/main/java/com/google/common/jimfs/BasicAttributeProvider.java @@ -73,11 +73,11 @@ public Object get(File file, String attribute) { case "isOther": return !file.isDirectory() && !file.isRegularFile() && !file.isSymbolicLink(); case "creationTime": - return FileTime.fromMillis(file.getCreationTime()); + return file.getCreationTime(); case "lastAccessTime": - return FileTime.fromMillis(file.getLastAccessTime()); + return file.getLastAccessTime(); case "lastModifiedTime": - return FileTime.fromMillis(file.getLastModifiedTime()); + return file.getLastModifiedTime(); default: return null; } @@ -88,15 +88,15 @@ public void set(File file, String view, String attribute, Object value, boolean switch (attribute) { case "creationTime": checkNotCreate(view, attribute, create); - file.setCreationTime(checkType(view, attribute, value, FileTime.class).toMillis()); + file.setCreationTime(checkType(view, attribute, value, FileTime.class)); break; case "lastAccessTime": checkNotCreate(view, attribute, create); - file.setLastAccessTime(checkType(view, attribute, value, FileTime.class).toMillis()); + file.setLastAccessTime(checkType(view, attribute, value, FileTime.class)); break; case "lastModifiedTime": checkNotCreate(view, attribute, create); - file.setLastModifiedTime(checkType(view, attribute, value, FileTime.class).toMillis()); + file.setLastModifiedTime(checkType(view, attribute, value, FileTime.class)); break; case "size": case "fileKey": @@ -156,15 +156,15 @@ public void setTimes( File file = lookupFile(); if (lastModifiedTime != null) { - file.setLastModifiedTime(lastModifiedTime.toMillis()); + file.setLastModifiedTime(lastModifiedTime); } if (lastAccessTime != null) { - file.setLastAccessTime(lastAccessTime.toMillis()); + file.setLastAccessTime(lastAccessTime); } if (createTime != null) { - file.setCreationTime(createTime.toMillis()); + file.setCreationTime(createTime); } } } @@ -182,9 +182,9 @@ static class Attributes implements BasicFileAttributes { private final Object fileKey; protected Attributes(File file) { - this.lastModifiedTime = FileTime.fromMillis(file.getLastModifiedTime()); - this.lastAccessTime = FileTime.fromMillis(file.getLastAccessTime()); - this.creationTime = FileTime.fromMillis(file.getCreationTime()); + this.lastModifiedTime = file.getLastModifiedTime(); + this.lastAccessTime = file.getLastAccessTime(); + this.creationTime = file.getCreationTime(); this.regularFile = file.isRegularFile(); this.directory = file.isDirectory(); this.symbolicLink = file.isSymbolicLink(); diff --git a/jimfs/src/main/java/com/google/common/jimfs/Configuration.java b/jimfs/src/main/java/com/google/common/jimfs/Configuration.java index 06630eb7..71202982 100644 --- a/jimfs/src/main/java/com/google/common/jimfs/Configuration.java +++ b/jimfs/src/main/java/com/google/common/jimfs/Configuration.java @@ -31,6 +31,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Sets; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.nio.channels.FileChannel; import java.nio.file.FileSystem; import java.nio.file.InvalidPathException; @@ -233,6 +234,7 @@ public static Builder builder(PathType pathType) { final ImmutableSet attributeViews; final ImmutableSet attributeProviders; final ImmutableMap defaultAttributeValues; + final FileTimeSource fileTimeSource; // Watch service final WatchServiceConfiguration watchServiceConfig; @@ -261,6 +263,7 @@ private Configuration(Builder builder) { builder.defaultAttributeValues == null ? ImmutableMap.of() : ImmutableMap.copyOf(builder.defaultAttributeValues); + this.fileTimeSource = builder.fileTimeSource; this.watchServiceConfig = builder.watchServiceConfig; this.roots = builder.roots; this.workingDirectory = builder.workingDirectory; @@ -301,6 +304,7 @@ public String toString() { if (!defaultAttributeValues.isEmpty()) { helper.add("defaultAttributeValues", defaultAttributeValues); } + helper.add("fileTimeSource", fileTimeSource); if (watchServiceConfig != WatchServiceConfiguration.DEFAULT) { helper.add("watchServiceConfig", watchServiceConfig); } @@ -341,6 +345,7 @@ public static final class Builder { private ImmutableSet attributeViews = ImmutableSet.of(); private Set attributeProviders = null; private Map defaultAttributeValues; + private FileTimeSource fileTimeSource = SystemFileTimeSource.INSTANCE; // Watch service private WatchServiceConfiguration watchServiceConfig = WatchServiceConfiguration.DEFAULT; @@ -372,6 +377,7 @@ private Builder(Configuration configuration) { configuration.defaultAttributeValues.isEmpty() ? null : new HashMap<>(configuration.defaultAttributeValues); + this.fileTimeSource = configuration.fileTimeSource; this.watchServiceConfig = configuration.watchServiceConfig; this.roots = configuration.roots; this.workingDirectory = configuration.workingDirectory; @@ -383,6 +389,7 @@ private Builder(Configuration configuration) { * Sets the normalizations that will be applied to the display form of filenames. The display * form is used in the {@code toString()} of {@code Path} objects. */ + @CanIgnoreReturnValue public Builder setNameDisplayNormalization(PathNormalization first, PathNormalization... more) { this.nameDisplayNormalization = checkNormalizations(Lists.asList(first, more)); return this; @@ -393,6 +400,7 @@ public Builder setNameDisplayNormalization(PathNormalization first, PathNormaliz * file system. The canonical form is used to determine the equality of two filenames when * performing a file lookup. */ + @CanIgnoreReturnValue public Builder setNameCanonicalNormalization( PathNormalization first, PathNormalization... more) { this.nameCanonicalNormalization = checkNormalizations(Lists.asList(first, more)); @@ -447,6 +455,7 @@ private static void checkNormalizationNotSet( * *

The default is false. */ + @CanIgnoreReturnValue public Builder setPathEqualityUsesCanonicalForm(boolean useCanonicalForm) { this.pathEqualityUsesCanonicalForm = useCanonicalForm; return this; @@ -458,6 +467,7 @@ public Builder setPathEqualityUsesCanonicalForm(boolean useCanonicalForm) { * *

The default is 8192 bytes (8 KB). */ + @CanIgnoreReturnValue public Builder setBlockSize(int blockSize) { checkArgument(blockSize > 0, "blockSize (%s) must be positive", blockSize); this.blockSize = blockSize; @@ -478,6 +488,7 @@ public Builder setBlockSize(int blockSize) { * *

The default is 4 GB. */ + @CanIgnoreReturnValue public Builder setMaxSize(long maxSize) { checkArgument(maxSize > 0, "maxSize (%s) must be positive", maxSize); this.maxSize = maxSize; @@ -497,6 +508,7 @@ public Builder setMaxSize(long maxSize) { *

Like the maximum size, the actual value will be the closest multiple of the block size * that is less than or equal to the given size. */ + @CanIgnoreReturnValue public Builder setMaxCacheSize(long maxCacheSize) { checkArgument(maxCacheSize >= 0, "maxCacheSize (%s) may not be negative", maxCacheSize); this.maxCacheSize = maxCacheSize; @@ -553,12 +565,14 @@ public Builder setMaxCacheSize(long maxCacheSize) { *

If any other views should be supported, attribute providers for those views must be * {@linkplain #addAttributeProvider(AttributeProvider) added}. */ + @CanIgnoreReturnValue public Builder setAttributeViews(String first, String... more) { this.attributeViews = ImmutableSet.copyOf(Lists.asList(first, more)); return this; } /** Adds an attribute provider for a custom view for the file system to support. */ + @CanIgnoreReturnValue public Builder addAttributeProvider(AttributeProvider provider) { checkNotNull(provider); if (attributeProviders == null) { @@ -614,6 +628,7 @@ public Builder addAttributeProvider(AttributeProvider provider) { * * */ + @CanIgnoreReturnValue public Builder setDefaultAttributeValue(String attribute, Object value) { checkArgument( ATTRIBUTE_PATTERN.matcher(attribute).matches(), @@ -631,6 +646,17 @@ public Builder setDefaultAttributeValue(String attribute, Object value) { private static final Pattern ATTRIBUTE_PATTERN = Pattern.compile("[^:]+:[^:]+"); + /** + * Sets the {@link FileTimeSource} that will supply the current time for this file system. + * + * @since 1.3 + */ + @CanIgnoreReturnValue + public Builder setFileTimeSource(FileTimeSource source) { + this.fileTimeSource = checkNotNull(source); + return this; + } + /** * Sets the roots for the file system. * @@ -639,6 +665,7 @@ public Builder setDefaultAttributeValue(String attribute, Object value) { * @throws IllegalArgumentException if any of the given roots is a valid path for this builder's * path type but is not a root path with no name elements */ + @CanIgnoreReturnValue public Builder setRoots(String first, String... more) { List roots = Lists.asList(first, more); for (String root : roots) { @@ -657,6 +684,7 @@ public Builder setRoots(String first, String... more) { * @throws IllegalArgumentException if the given path is valid for this builder's path type but * is not an absolute path */ + @CanIgnoreReturnValue public Builder setWorkingDirectory(String workingDirectory) { PathType.ParseResult parseResult = pathType.parsePath(workingDirectory); checkArgument( @@ -671,6 +699,7 @@ public Builder setWorkingDirectory(String workingDirectory) { * Sets the given features to be supported by the file system. Any features not provided here * will not be supported. */ + @CanIgnoreReturnValue public Builder setSupportedFeatures(Feature... features) { supportedFeatures = Sets.immutableEnumSet(Arrays.asList(features)); return this; @@ -682,6 +711,7 @@ public Builder setSupportedFeatures(Feature... features) { * * @since 1.1 */ + @CanIgnoreReturnValue public Builder setWatchServiceConfiguration(WatchServiceConfiguration config) { this.watchServiceConfig = checkNotNull(config); return this; diff --git a/jimfs/src/main/java/com/google/common/jimfs/Directory.java b/jimfs/src/main/java/com/google/common/jimfs/Directory.java index aaab83b6..83093e69 100644 --- a/jimfs/src/main/java/com/google/common/jimfs/Directory.java +++ b/jimfs/src/main/java/com/google/common/jimfs/Directory.java @@ -19,6 +19,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.AbstractIterator; import com.google.common.collect.ImmutableSortedSet; +import java.nio.file.attribute.FileTime; import java.util.Iterator; import org.checkerframework.checker.nullness.compatqual.NullableDecl; @@ -32,23 +33,23 @@ final class Directory extends File implements Iterable { /** The entry linking to this directory in its parent directory. */ private DirectoryEntry entryInParent; - /** Creates a new normal directory with the given ID. */ - public static Directory create(int id) { - return new Directory(id); + /** Creates a new normal directory with the given ID and creation time. */ + public static Directory create(int id, FileTime creationTime) { + return new Directory(id, creationTime); } - /** Creates a new root directory with the given ID and name. */ - public static Directory createRoot(int id, Name name) { - return new Directory(id, name); + /** Creates a new root directory with the given ID, creation time, and name. */ + public static Directory createRoot(int id, FileTime creationTime, Name name) { + return new Directory(id, creationTime, name); } - private Directory(int id) { - super(id); + private Directory(int id, FileTime creationTime) { + super(id, creationTime); put(new DirectoryEntry(this, Name.SELF, this)); } - private Directory(int id, Name rootName) { - this(id); + private Directory(int id, FileTime creationTime, Name rootName) { + this(id, creationTime); linked(new DirectoryEntry(this, rootName, this)); } @@ -57,8 +58,8 @@ private Directory(int id, Name rootName) { * this directory. */ @Override - Directory copyWithoutContent(int id) { - return Directory.create(id); + Directory copyWithoutContent(int id, FileTime creationTime) { + return Directory.create(id, creationTime); } /** diff --git a/jimfs/src/main/java/com/google/common/jimfs/File.java b/jimfs/src/main/java/com/google/common/jimfs/File.java index ce1cc001..f363ac97 100644 --- a/jimfs/src/main/java/com/google/common/jimfs/File.java +++ b/jimfs/src/main/java/com/google/common/jimfs/File.java @@ -24,6 +24,7 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Table; import java.io.IOException; +import java.nio.file.attribute.FileTime; import java.util.concurrent.locks.ReadWriteLock; import org.checkerframework.checker.nullness.compatqual.NullableDecl; @@ -38,20 +39,19 @@ public abstract class File { private int links; - private long creationTime; - private long lastAccessTime; - private long lastModifiedTime; + private FileTime creationTime; + private FileTime lastAccessTime; + private FileTime lastModifiedTime; @NullableDecl // null when only the basic view is used (default) private Table attributes; - File(int id) { + File(int id, FileTime creationTime) { this.id = id; - long now = System.currentTimeMillis(); // TODO(cgdecker): Use a Clock - this.creationTime = now; - this.lastAccessTime = now; - this.lastModifiedTime = now; + this.creationTime = creationTime; + this.lastAccessTime = creationTime; + this.lastModifiedTime = creationTime; } /** Returns the ID of this file. */ @@ -83,11 +83,11 @@ public final boolean isSymbolicLink() { } /** - * Creates a new file of the same type as this file with the given ID. Does not copy the content - * of this file unless the cost of copying the content is minimal. This is because this method is - * called with a hold on the file system's lock. + * Creates a new file of the same type as this file with the given ID and creation time. Does not + * copy the content of this file unless the cost of copying the content is minimal. This is + * because this method is called with a hold on the file system's lock. */ - abstract File copyWithoutContent(int id); + abstract File copyWithoutContent(int id, FileTime creationTime); /** * Copies the content of this file to the given file. The given file must be the same type of file @@ -155,48 +155,35 @@ final synchronized void decrementLinkCount() { } /** Gets the creation time of the file. */ - @SuppressWarnings("GoodTime") // should return a java.time.Instant - public final synchronized long getCreationTime() { + public final synchronized FileTime getCreationTime() { return creationTime; } /** Gets the last access time of the file. */ - @SuppressWarnings("GoodTime") // should return a java.time.Instant - public final synchronized long getLastAccessTime() { + public final synchronized FileTime getLastAccessTime() { return lastAccessTime; } /** Gets the last modified time of the file. */ - @SuppressWarnings("GoodTime") // should return a java.time.Instant - public final synchronized long getLastModifiedTime() { + public final synchronized FileTime getLastModifiedTime() { return lastModifiedTime; } /** Sets the creation time of the file. */ - final synchronized void setCreationTime(long creationTime) { + final synchronized void setCreationTime(FileTime creationTime) { this.creationTime = creationTime; } /** Sets the last access time of the file. */ - final synchronized void setLastAccessTime(long lastAccessTime) { + final synchronized void setLastAccessTime(FileTime lastAccessTime) { this.lastAccessTime = lastAccessTime; } /** Sets the last modified time of the file. */ - final synchronized void setLastModifiedTime(long lastModifiedTime) { + final synchronized void setLastModifiedTime(FileTime lastModifiedTime) { this.lastModifiedTime = lastModifiedTime; } - /** Sets the last access time of the file to the current time. */ - final void updateAccessTime() { - setLastAccessTime(System.currentTimeMillis()); - } - - /** Sets the last modified time of the file to the current time. */ - final void updateModifiedTime() { - setLastModifiedTime(System.currentTimeMillis()); - } - /** * Returns the names of the attributes contained in the given attribute view in the file's * attributes table. @@ -252,7 +239,7 @@ final synchronized void copyBasicAttributes(File target) { } private synchronized void setFileTimes( - long creationTime, long lastModifiedTime, long lastAccessTime) { + FileTime creationTime, FileTime lastModifiedTime, FileTime lastAccessTime) { this.creationTime = creationTime; this.lastModifiedTime = lastModifiedTime; this.lastAccessTime = lastAccessTime; diff --git a/jimfs/src/main/java/com/google/common/jimfs/FileFactory.java b/jimfs/src/main/java/com/google/common/jimfs/FileFactory.java index e26d41de..b4363937 100644 --- a/jimfs/src/main/java/com/google/common/jimfs/FileFactory.java +++ b/jimfs/src/main/java/com/google/common/jimfs/FileFactory.java @@ -33,10 +33,14 @@ final class FileFactory { private final AtomicInteger idGenerator = new AtomicInteger(); private final HeapDisk disk; + private final FileTimeSource fileTimeSource; - /** Creates a new file factory using the given disk for regular files. */ - public FileFactory(HeapDisk disk) { + /** + * Creates a new file factory using the given disk for regular files and the given time source. + */ + public FileFactory(HeapDisk disk, FileTimeSource fileTimeSource) { this.disk = checkNotNull(disk); + this.fileTimeSource = checkNotNull(fileTimeSource); } private int nextFileId() { @@ -45,29 +49,29 @@ private int nextFileId() { /** Creates a new directory. */ public Directory createDirectory() { - return Directory.create(nextFileId()); + return Directory.create(nextFileId(), fileTimeSource.now()); } /** Creates a new root directory with the given name. */ public Directory createRootDirectory(Name name) { - return Directory.createRoot(nextFileId(), name); + return Directory.createRoot(nextFileId(), fileTimeSource.now(), name); } /** Creates a new regular file. */ @VisibleForTesting RegularFile createRegularFile() { - return RegularFile.create(nextFileId(), disk); + return RegularFile.create(nextFileId(), fileTimeSource.now(), disk); } /** Creates a new symbolic link referencing the given target path. */ @VisibleForTesting SymbolicLink createSymbolicLink(JimfsPath target) { - return SymbolicLink.create(nextFileId(), target); + return SymbolicLink.create(nextFileId(), fileTimeSource.now(), target); } /** Creates and returns a copy of the given file. */ public File copyWithoutContent(File file) throws IOException { - return file.copyWithoutContent(nextFileId()); + return file.copyWithoutContent(nextFileId(), fileTimeSource.now()); } // suppliers to act as file creation callbacks diff --git a/jimfs/src/main/java/com/google/common/jimfs/FileSystemState.java b/jimfs/src/main/java/com/google/common/jimfs/FileSystemState.java index f15a5ff0..e57a5f13 100644 --- a/jimfs/src/main/java/com/google/common/jimfs/FileSystemState.java +++ b/jimfs/src/main/java/com/google/common/jimfs/FileSystemState.java @@ -23,6 +23,7 @@ import java.io.Closeable; import java.io.IOException; import java.nio.file.ClosedFileSystemException; +import java.nio.file.attribute.FileTime; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -37,6 +38,7 @@ final class FileSystemState implements Closeable { private final Set resources = Sets.newConcurrentHashSet(); + private final FileTimeSource fileTimeSource; private final Runnable onClose; private final AtomicBoolean open = new AtomicBoolean(true); @@ -44,7 +46,8 @@ final class FileSystemState implements Closeable { /** Count of resources currently in the process of being registered. */ private final AtomicInteger registering = new AtomicInteger(); - FileSystemState(Runnable onClose) { + FileSystemState(FileTimeSource fileTimeSource, Runnable onClose) { + this.fileTimeSource = checkNotNull(fileTimeSource); this.onClose = checkNotNull(onClose); } @@ -90,6 +93,11 @@ public void unregister(Closeable resource) { resources.remove(resource); } + /** Returns the current {@link FileTime}. */ + public FileTime now() { + return fileTimeSource.now(); + } + /** * Closes the file system, runs the {@code onClose} callback and closes all registered resources. */ diff --git a/jimfs/src/main/java/com/google/common/jimfs/FileSystemView.java b/jimfs/src/main/java/com/google/common/jimfs/FileSystemView.java index 62e8739c..a27c4e45 100644 --- a/jimfs/src/main/java/com/google/common/jimfs/FileSystemView.java +++ b/jimfs/src/main/java/com/google/common/jimfs/FileSystemView.java @@ -42,6 +42,7 @@ import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileAttribute; import java.nio.file.attribute.FileAttributeView; +import java.nio.file.attribute.FileTime; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -86,6 +87,10 @@ public FileSystemState state() { return store.state(); } + private FileTime now() { + return state().now(); + } + /** * Returns the path of the working directory at the time this view was created. Does not reflect * changes to the path caused by the directory being moved. @@ -135,7 +140,7 @@ public ImmutableSortedSet snapshotWorkingDirectoryEntries() { store.readLock().lock(); try { ImmutableSortedSet names = workingDirectory.snapshot(); - workingDirectory.updateAccessTime(); + workingDirectory.setLastAccessTime(now()); return names; } finally { store.readLock().unlock(); @@ -146,8 +151,8 @@ public ImmutableSortedSet snapshotWorkingDirectoryEntries() { * Returns a snapshot mapping the names of each file in the directory at the given path to the * last modified time of that file. */ - public ImmutableMap snapshotModifiedTimes(JimfsPath path) throws IOException { - ImmutableMap.Builder modifiedTimes = ImmutableMap.builder(); + public ImmutableMap snapshotModifiedTimes(JimfsPath path) throws IOException { + ImmutableMap.Builder modifiedTimes = ImmutableMap.builder(); store.readLock().lock(); try { @@ -271,7 +276,7 @@ private File createFile( File newFile = fileCreator.get(); store.setInitialAttributes(newFile, attrs); parent.link(path.name(), newFile); - parent.updateModifiedTime(); + parent.setLastModifiedTime(now()); return newFile; } finally { store.writeLock().unlock(); @@ -423,7 +428,7 @@ public void link(JimfsPath link, FileSystemView existingView, JimfsPath existing lookUp(link, Options.NOFOLLOW_LINKS).requireDoesNotExist(link).directory(); linkParent.link(linkName, existingFile); - linkParent.updateModifiedTime(); + linkParent.setLastModifiedTime(now()); } finally { store.writeLock().unlock(); } @@ -448,7 +453,7 @@ private void delete(DirectoryEntry entry, DeleteMode deleteMode, JimfsPath pathF checkDeletable(file, deleteMode, pathForException); parent.unlink(entry.name()); - parent.updateModifiedTime(); + parent.setLastModifiedTime(now()); file.deleted(); } @@ -545,10 +550,10 @@ public void copy( if (move && sameFileSystem) { // Real move on the same file system. sourceParent.unlink(source.name()); - sourceParent.updateModifiedTime(); + sourceParent.setLastModifiedTime(now()); destParent.link(dest.name(), sourceFile); - destParent.updateModifiedTime(); + destParent.setLastModifiedTime(now()); } else { // Doing a copy OR a move to a different file system, which must be implemented by copy and // delete. @@ -571,7 +576,7 @@ public void copy( // Copy the file, but don't copy its content while we're holding the file store locks. copyFile = destView.store.copyWithoutContent(sourceFile, attributeCopyOption); destParent.link(dest.name(), copyFile); - destParent.updateModifiedTime(); + destParent.setLastModifiedTime(now()); // In order for the copy to be atomic (not strictly necessary, but seems preferable since // we can) lock both source and copy files before leaving the file store locks. This diff --git a/jimfs/src/main/java/com/google/common/jimfs/FileTimeSource.java b/jimfs/src/main/java/com/google/common/jimfs/FileTimeSource.java new file mode 100644 index 00000000..bbc12928 --- /dev/null +++ b/jimfs/src/main/java/com/google/common/jimfs/FileTimeSource.java @@ -0,0 +1,29 @@ +/* + * Copyright 2021 Google Inc. + * + * Licensed 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. + */ + +package com.google.common.jimfs; + +import java.nio.file.attribute.FileTime; + +/** + * A source of the current time as a {@link FileTime}, to enable fake time sources for testing. + * + * @since 1.3 + */ +public interface FileTimeSource { + /** Returns the current time according to this source as a {@link FileTime}. */ + FileTime now(); +} diff --git a/jimfs/src/main/java/com/google/common/jimfs/HeapDisk.java b/jimfs/src/main/java/com/google/common/jimfs/HeapDisk.java index abfb6177..81328698 100644 --- a/jimfs/src/main/java/com/google/common/jimfs/HeapDisk.java +++ b/jimfs/src/main/java/com/google/common/jimfs/HeapDisk.java @@ -83,7 +83,14 @@ private static int toBlockCount(long size, int blockSize) { } private RegularFile createBlockCache(int maxCachedBlockCount) { - return new RegularFile(-1, this, new byte[Math.min(maxCachedBlockCount, 8192)][], 0, 0); + // This file is just for holding blocks so things like the creation time don't matter + return new RegularFile( + -1, + SystemFileTimeSource.INSTANCE.now(), + this, + new byte[Math.min(maxCachedBlockCount, 8192)][], + 0, + 0); } /** Returns the size of blocks created by this disk. */ diff --git a/jimfs/src/main/java/com/google/common/jimfs/JimfsFileChannel.java b/jimfs/src/main/java/com/google/common/jimfs/JimfsFileChannel.java index 95863cc5..57240bfd 100644 --- a/jimfs/src/main/java/com/google/common/jimfs/JimfsFileChannel.java +++ b/jimfs/src/main/java/com/google/common/jimfs/JimfsFileChannel.java @@ -158,7 +158,7 @@ public int read(ByteBuffer dst) throws IOException { if (read != -1) { position += read; } - file.updateAccessTime(); + file.setLastAccessTime(fileSystemState.now()); completed = true; } finally { file.readLock().unlock(); @@ -195,7 +195,7 @@ public long read(ByteBuffer[] dsts, int offset, int length) throws IOException { if (read != -1) { position += read; } - file.updateAccessTime(); + file.setLastAccessTime(fileSystemState.now()); completed = true; } finally { file.readLock().unlock(); @@ -228,7 +228,7 @@ public int read(ByteBuffer dst, long position) throws IOException { file.readLock().lockInterruptibly(); try { read = file.read(position, dst); - file.updateAccessTime(); + file.setLastAccessTime(fileSystemState.now()); completed = true; } finally { file.readLock().unlock(); @@ -263,7 +263,7 @@ public int write(ByteBuffer src) throws IOException { } written = file.write(position, src); position += written; - file.updateModifiedTime(); + file.setLastModifiedTime(fileSystemState.now()); completed = true; } finally { file.writeLock().unlock(); @@ -301,7 +301,7 @@ public long write(ByteBuffer[] srcs, int offset, int length) throws IOException } written = file.write(position, buffers); position += written; - file.updateModifiedTime(); + file.setLastModifiedTime(fileSystemState.now()); completed = true; } finally { file.writeLock().unlock(); @@ -339,7 +339,7 @@ public int write(ByteBuffer src, long position) throws IOException { position = file.sizeWithoutLocking(); written = file.write(position, src); this.position = position + written; - file.updateModifiedTime(); + file.setLastModifiedTime(fileSystemState.now()); completed = true; } finally { file.writeLock().unlock(); @@ -360,7 +360,7 @@ public int write(ByteBuffer src, long position) throws IOException { file.writeLock().lockInterruptibly(); try { written = file.write(position, src); - file.updateModifiedTime(); + file.setLastModifiedTime(fileSystemState.now()); completed = true; } finally { file.writeLock().unlock(); @@ -465,7 +465,7 @@ public FileChannel truncate(long size) throws IOException { if (position > size) { position = size; } - file.updateModifiedTime(); + file.setLastModifiedTime(fileSystemState.now()); completed = true; } finally { file.writeLock().unlock(); @@ -514,7 +514,7 @@ public long transferTo(long position, long count, WritableByteChannel target) th file.readLock().lockInterruptibly(); try { transferred = file.transferTo(position, count, target); - file.updateAccessTime(); + file.setLastAccessTime(fileSystemState.now()); completed = true; } finally { file.readLock().unlock(); @@ -552,7 +552,7 @@ public long transferFrom(ReadableByteChannel src, long position, long count) thr position = file.sizeWithoutLocking(); transferred = file.transferFrom(src, position, count); this.position = position + transferred; - file.updateModifiedTime(); + file.setLastModifiedTime(fileSystemState.now()); completed = true; } finally { file.writeLock().unlock(); @@ -573,7 +573,7 @@ public long transferFrom(ReadableByteChannel src, long position, long count) thr file.writeLock().lockInterruptibly(); try { transferred = file.transferFrom(src, position, count); - file.updateModifiedTime(); + file.setLastModifiedTime(fileSystemState.now()); completed = true; } finally { file.writeLock().unlock(); diff --git a/jimfs/src/main/java/com/google/common/jimfs/JimfsFileSystems.java b/jimfs/src/main/java/com/google/common/jimfs/JimfsFileSystems.java index bd36c8f2..7143dae6 100644 --- a/jimfs/src/main/java/com/google/common/jimfs/JimfsFileSystems.java +++ b/jimfs/src/main/java/com/google/common/jimfs/JimfsFileSystems.java @@ -68,7 +68,8 @@ private static Runnable removeFileSystemRunnable(URI uri) { public static JimfsFileSystem newFileSystem( JimfsFileSystemProvider provider, URI uri, Configuration config) throws IOException { PathService pathService = new PathService(config); - FileSystemState state = new FileSystemState(removeFileSystemRunnable(uri)); + FileSystemState state = + new FileSystemState(config.fileTimeSource, removeFileSystemRunnable(uri)); JimfsFileStore fileStore = createFileStore(config, pathService, state); FileSystemView defaultView = createDefaultView(config, fileStore, pathService); @@ -87,7 +88,7 @@ private static JimfsFileStore createFileStore( AttributeService attributeService = new AttributeService(config); HeapDisk disk = new HeapDisk(config); - FileFactory fileFactory = new FileFactory(disk); + FileFactory fileFactory = new FileFactory(disk, config.fileTimeSource); Map roots = new HashMap<>(); diff --git a/jimfs/src/main/java/com/google/common/jimfs/JimfsInputStream.java b/jimfs/src/main/java/com/google/common/jimfs/JimfsInputStream.java index 750530c5..41bcc911 100644 --- a/jimfs/src/main/java/com/google/common/jimfs/JimfsInputStream.java +++ b/jimfs/src/main/java/com/google/common/jimfs/JimfsInputStream.java @@ -63,7 +63,7 @@ public synchronized int read() throws IOException { if (b == -1) { finished = true; } else { - file.updateAccessTime(); + file.setLastAccessTime(fileSystemState.now()); } return b; } finally { @@ -97,7 +97,7 @@ private synchronized int readInternal(byte[] b, int off, int len) throws IOExcep pos += read; } - file.updateAccessTime(); + file.setLastAccessTime(fileSystemState.now()); return read; } finally { file.readLock().unlock(); diff --git a/jimfs/src/main/java/com/google/common/jimfs/JimfsOutputStream.java b/jimfs/src/main/java/com/google/common/jimfs/JimfsOutputStream.java index 0b88046e..654857e4 100644 --- a/jimfs/src/main/java/com/google/common/jimfs/JimfsOutputStream.java +++ b/jimfs/src/main/java/com/google/common/jimfs/JimfsOutputStream.java @@ -58,7 +58,7 @@ public synchronized void write(int b) throws IOException { } file.write(pos++, (byte) b); - file.updateModifiedTime(); + file.setLastModifiedTime(fileSystemState.now()); } finally { file.writeLock().unlock(); } @@ -85,7 +85,7 @@ private synchronized void writeInternal(byte[] b, int off, int len) throws IOExc } pos += file.write(pos, b, off, len); - file.updateModifiedTime(); + file.setLastModifiedTime(fileSystemState.now()); } finally { file.writeLock().unlock(); } diff --git a/jimfs/src/main/java/com/google/common/jimfs/JimfsPath.java b/jimfs/src/main/java/com/google/common/jimfs/JimfsPath.java index 73a3b989..a9a39fea 100644 --- a/jimfs/src/main/java/com/google/common/jimfs/JimfsPath.java +++ b/jimfs/src/main/java/com/google/common/jimfs/JimfsPath.java @@ -49,6 +49,7 @@ * * @author Colin Decker */ +@SuppressWarnings("ShouldNotSubclass") // I know what I'm doing I promise final class JimfsPath implements Path { @NullableDecl private final Name root; diff --git a/jimfs/src/main/java/com/google/common/jimfs/PollingWatchService.java b/jimfs/src/main/java/com/google/common/jimfs/PollingWatchService.java index d1f5b898..b9438834 100644 --- a/jimfs/src/main/java/com/google/common/jimfs/PollingWatchService.java +++ b/jimfs/src/main/java/com/google/common/jimfs/PollingWatchService.java @@ -31,6 +31,7 @@ import java.nio.file.WatchEvent; import java.nio.file.WatchService; import java.nio.file.Watchable; +import java.nio.file.attribute.FileTime; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -198,9 +199,9 @@ private Snapshot takeSnapshot(JimfsPath path) throws IOException { private final class Snapshot { /** Maps directory entry names to last modified times. */ - private final ImmutableMap modifiedTimes; + private final ImmutableMap modifiedTimes; - Snapshot(Map modifiedTimes) { + Snapshot(Map modifiedTimes) { this.modifiedTimes = ImmutableMap.copyOf(modifiedTimes); } @@ -232,11 +233,11 @@ boolean postChanges(Snapshot newState, Key key) { } if (key.subscribesTo(ENTRY_MODIFY)) { - for (Map.Entry entry : modifiedTimes.entrySet()) { + for (Map.Entry entry : modifiedTimes.entrySet()) { Name name = entry.getKey(); - Long modifiedTime = entry.getValue(); + FileTime modifiedTime = entry.getValue(); - Long newModifiedTime = newState.modifiedTimes.get(name); + FileTime newModifiedTime = newState.modifiedTimes.get(name); if (newModifiedTime != null && !modifiedTime.equals(newModifiedTime)) { key.post(new Event<>(ENTRY_MODIFY, 1, pathService.createFileName(name))); changesPosted = true; diff --git a/jimfs/src/main/java/com/google/common/jimfs/RegularFile.java b/jimfs/src/main/java/com/google/common/jimfs/RegularFile.java index 20c9f4b8..171d97e8 100644 --- a/jimfs/src/main/java/com/google/common/jimfs/RegularFile.java +++ b/jimfs/src/main/java/com/google/common/jimfs/RegularFile.java @@ -28,6 +28,7 @@ import java.nio.channels.FileChannel; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; +import java.nio.file.attribute.FileTime; import java.util.Arrays; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; @@ -53,12 +54,18 @@ final class RegularFile extends File { private long size; /** Creates a new regular file with the given ID and using the given disk. */ - public static RegularFile create(int id, HeapDisk disk) { - return new RegularFile(id, disk, new byte[32][], 0, 0); - } - - RegularFile(int id, HeapDisk disk, byte[][] blocks, int blockCount, long size) { - super(id); + public static RegularFile create(int id, FileTime creationTime, HeapDisk disk) { + return new RegularFile(id, creationTime, disk, new byte[32][], 0, 0); + } + + RegularFile( + int id, + FileTime creationTime, + HeapDisk disk, + byte[][] blocks, + int blockCount, + long size) { + super(id, creationTime); this.disk = checkNotNull(disk); this.blocks = checkNotNull(blocks); this.blockCount = blockCount; @@ -150,9 +157,9 @@ public long size() { } @Override - RegularFile copyWithoutContent(int id) { + RegularFile copyWithoutContent(int id, FileTime creationTime) { byte[][] copyBlocks = new byte[Math.max(blockCount * 2, 32)][]; - return new RegularFile(id, disk, copyBlocks, 0, size); + return new RegularFile(id, creationTime, disk, copyBlocks, 0, size); } @Override diff --git a/jimfs/src/main/java/com/google/common/jimfs/SymbolicLink.java b/jimfs/src/main/java/com/google/common/jimfs/SymbolicLink.java index 29f4aa55..c563d6a4 100644 --- a/jimfs/src/main/java/com/google/common/jimfs/SymbolicLink.java +++ b/jimfs/src/main/java/com/google/common/jimfs/SymbolicLink.java @@ -18,6 +18,8 @@ import static com.google.common.base.Preconditions.checkNotNull; +import java.nio.file.attribute.FileTime; + /** * A symbolic link file, containing a {@linkplain JimfsPath path}. * @@ -28,12 +30,12 @@ final class SymbolicLink extends File { private final JimfsPath target; /** Creates a new symbolic link with the given ID and target. */ - public static SymbolicLink create(int id, JimfsPath target) { - return new SymbolicLink(id, target); + public static SymbolicLink create(int id, FileTime creationTime, JimfsPath target) { + return new SymbolicLink(id, creationTime, target); } - private SymbolicLink(int id, JimfsPath target) { - super(id); + private SymbolicLink(int id, FileTime creationTime, JimfsPath target) { + super(id, creationTime); this.target = checkNotNull(target); } @@ -43,7 +45,7 @@ JimfsPath target() { } @Override - File copyWithoutContent(int id) { - return SymbolicLink.create(id, target); + File copyWithoutContent(int id, FileTime creationTime) { + return SymbolicLink.create(id, creationTime, target); } } diff --git a/jimfs/src/main/java/com/google/common/jimfs/SystemFileTimeSource.java b/jimfs/src/main/java/com/google/common/jimfs/SystemFileTimeSource.java new file mode 100644 index 00000000..47635b81 --- /dev/null +++ b/jimfs/src/main/java/com/google/common/jimfs/SystemFileTimeSource.java @@ -0,0 +1,38 @@ +/* + * Copyright 2021 Google Inc. + * + * Licensed 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. + */ + +package com.google.common.jimfs; + +import java.nio.file.attribute.FileTime; + +/** Implementation of of {@link FileTimeSource} that gets the current time from the system. */ +enum SystemFileTimeSource implements FileTimeSource { + INSTANCE; + + // If/when Jimfs requires Java 8 this should use the FileTime factory that takes an Instant as + // that has the potential to be more precise. At that point, we should make a similar change to + // FakeFileTimeSource. + + @Override + public FileTime now() { + return FileTime.fromMillis(System.currentTimeMillis()); + } + + @Override + public String toString() { + return "SystemFileTimeSource"; + } +} diff --git a/jimfs/src/main/java/com/google/common/jimfs/UnixAttributeProvider.java b/jimfs/src/main/java/com/google/common/jimfs/UnixAttributeProvider.java index 4d1f5082..b1e3b5cf 100644 --- a/jimfs/src/main/java/com/google/common/jimfs/UnixAttributeProvider.java +++ b/jimfs/src/main/java/com/google/common/jimfs/UnixAttributeProvider.java @@ -21,7 +21,6 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import java.nio.file.attribute.FileAttributeView; -import java.nio.file.attribute.FileTime; import java.nio.file.attribute.GroupPrincipal; import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.UserPrincipal; @@ -111,7 +110,7 @@ public Object get(File file, String attribute) { (Set) file.getAttribute("posix", "permissions"); return toMode(permissions); case "ctime": - return FileTime.fromMillis(file.getCreationTime()); + return file.getCreationTime(); case "rdev": return 0L; case "dev": diff --git a/jimfs/src/test/java/com/google/common/jimfs/AbstractAttributeProviderTest.java b/jimfs/src/test/java/com/google/common/jimfs/AbstractAttributeProviderTest.java index 7e2bdf95..1b9a639b 100644 --- a/jimfs/src/test/java/com/google/common/jimfs/AbstractAttributeProviderTest.java +++ b/jimfs/src/test/java/com/google/common/jimfs/AbstractAttributeProviderTest.java @@ -36,6 +36,8 @@ public abstract class AbstractAttributeProviderTest

protected static final ImmutableMap NO_INHERITED_VIEWS = ImmutableMap.of(); + protected final FakeFileTimeSource fileTimeSource = new FakeFileTimeSource(); + protected P provider; protected File file; @@ -57,7 +59,7 @@ public File lookup() throws IOException { @Before public void setUp() { this.provider = createProvider(); - this.file = Directory.create(0); + this.file = Directory.create(0, fileTimeSource.now()); Map defaultValues = createDefaultValues(); setDefaultValues(file, provider, defaultValues); diff --git a/jimfs/src/test/java/com/google/common/jimfs/AttributeServiceTest.java b/jimfs/src/test/java/com/google/common/jimfs/AttributeServiceTest.java index 80b01910..c7a12f4f 100644 --- a/jimfs/src/test/java/com/google/common/jimfs/AttributeServiceTest.java +++ b/jimfs/src/test/java/com/google/common/jimfs/AttributeServiceTest.java @@ -43,6 +43,8 @@ public class AttributeServiceTest { private AttributeService service; + private final FakeFileTimeSource fileTimeSource = new FakeFileTimeSource(); + @Before public void setUp() { ImmutableSet providers = @@ -53,6 +55,10 @@ public void setUp() { service = new AttributeService(providers, ImmutableMap.of()); } + private File createFile() { + return Directory.create(0, fileTimeSource.now()); + } + @Test public void testSupportedFileAttributeViews() { assertThat(service.supportedFileAttributeViews()) @@ -68,7 +74,7 @@ public void testSupportsFileAttributeView() { @Test public void testSetInitialAttributes() { - File file = Directory.create(0); + File file = createFile(); service.setInitialAttributes(file); assertThat(file.getAttributeNames("test")).containsExactly("bar", "baz"); @@ -81,7 +87,7 @@ public void testSetInitialAttributes() { @Test public void testGetAttribute() { - File file = Directory.create(0); + File file = createFile(); service.setInitialAttributes(file); assertThat(service.getAttribute(file, "test:foo")).isEqualTo("hello"); @@ -93,7 +99,7 @@ public void testGetAttribute() { @Test public void testGetAttribute_fromInheritedProvider() { - File file = Directory.create(0); + File file = createFile(); assertThat(service.getAttribute(file, "test:isRegularFile")).isEqualTo(false); assertThat(service.getAttribute(file, "test:isDirectory")).isEqualTo(true); assertThat(service.getAttribute(file, "test", "fileKey")).isEqualTo(0); @@ -101,7 +107,7 @@ public void testGetAttribute_fromInheritedProvider() { @Test public void testGetAttribute_failsForAttributesNotDefinedByProvider() { - File file = Directory.create(0); + File file = createFile(); try { service.getAttribute(file, "test:blah"); fail(); @@ -118,7 +124,7 @@ public void testGetAttribute_failsForAttributesNotDefinedByProvider() { @Test public void testSetAttribute() { - File file = Directory.create(0); + File file = createFile(); service.setAttribute(file, "test:bar", 10L, false); assertThat(file.getAttribute("test", "bar")).isEqualTo(10L); @@ -128,7 +134,7 @@ public void testSetAttribute() { @Test public void testSetAttribute_forInheritedProvider() { - File file = Directory.create(0); + File file = createFile(); service.setAttribute(file, "test:lastModifiedTime", FileTime.fromMillis(0), false); assertThat(file.getAttribute("test", "lastModifiedTime")).isNull(); assertThat(service.getAttribute(file, "basic:lastModifiedTime")) @@ -137,7 +143,7 @@ public void testSetAttribute_forInheritedProvider() { @Test public void testSetAttribute_withAlternateAcceptedType() { - File file = Directory.create(0); + File file = createFile(); service.setAttribute(file, "test:bar", 10F, false); assertThat(file.getAttribute("test", "bar")).isEqualTo(10L); @@ -147,14 +153,14 @@ public void testSetAttribute_withAlternateAcceptedType() { @Test public void testSetAttribute_onCreate() { - File file = Directory.create(0); + File file = createFile(); service.setInitialAttributes(file, new BasicFileAttribute<>("test:baz", 123)); assertThat(file.getAttribute("test", "baz")).isEqualTo(123); } @Test public void testSetAttribute_failsForAttributesNotDefinedByProvider() { - File file = Directory.create(0); + File file = createFile(); service.setInitialAttributes(file); try { @@ -175,7 +181,7 @@ public void testSetAttribute_failsForAttributesNotDefinedByProvider() { @Test public void testSetAttribute_failsForArgumentThatIsNotOfCorrectType() { - File file = Directory.create(0); + File file = createFile(); service.setInitialAttributes(file); try { service.setAttribute(file, "test:bar", "wrong", false); @@ -188,7 +194,7 @@ public void testSetAttribute_failsForArgumentThatIsNotOfCorrectType() { @Test public void testSetAttribute_failsForNullArgument() { - File file = Directory.create(0); + File file = createFile(); service.setInitialAttributes(file); try { service.setAttribute(file, "test:bar", null, false); @@ -201,7 +207,7 @@ public void testSetAttribute_failsForNullArgument() { @Test public void testSetAttribute_failsForAttributeThatIsNotSettable() { - File file = Directory.create(0); + File file = createFile(); try { service.setAttribute(file, "test:foo", "world", false); fail(); @@ -213,7 +219,7 @@ public void testSetAttribute_failsForAttributeThatIsNotSettable() { @Test public void testSetAttribute_onCreate_failsForAttributeThatIsNotSettableOnCreate() { - File file = Directory.create(0); + File file = createFile(); try { service.setInitialAttributes(file, new BasicFileAttribute<>("test:foo", "world")); fail(); @@ -232,7 +238,7 @@ public void testSetAttribute_onCreate_failsForAttributeThatIsNotSettableOnCreate @SuppressWarnings("ConstantConditions") @Test public void testGetFileAttributeView() throws IOException { - final File file = Directory.create(0); + final File file = createFile(); service.setInitialAttributes(file); FileLookup fileLookup = @@ -255,7 +261,7 @@ public File lookup() throws IOException { @Test public void testGetFileAttributeView_isNullForUnsupportedView() { - final File file = Directory.create(0); + final File file = createFile(); FileLookup fileLookup = new FileLookup() { @Override @@ -268,13 +274,13 @@ public File lookup() throws IOException { @Test public void testReadAttributes_asMap() { - File file = Directory.create(0); + File file = createFile(); service.setInitialAttributes(file); ImmutableMap map = service.readAttributes(file, "test:foo,bar,baz"); assertThat(map).isEqualTo(ImmutableMap.of("foo", "hello", "bar", 0L, "baz", 1)); - FileTime time = (FileTime) service.getAttribute(file, "basic:creationTime"); + FileTime time = fileTimeSource.now(); map = service.readAttributes(file, "test:*"); assertThat(map) @@ -312,7 +318,7 @@ public void testReadAttributes_asMap() { @Test public void testReadAttributes_asMap_failsForInvalidAttributes() { - File file = Directory.create(0); + File file = createFile(); try { service.readAttributes(file, "basic:fileKey,isOther,*,creationTime"); fail(); @@ -330,7 +336,7 @@ public void testReadAttributes_asMap_failsForInvalidAttributes() { @Test public void testReadAttributes_asObject() { - File file = Directory.create(0); + File file = createFile(); service.setInitialAttributes(file); BasicFileAttributes basicAttrs = service.readAttributes(file, BasicFileAttributes.class); @@ -349,7 +355,7 @@ public void testReadAttributes_asObject() { @Test public void testReadAttributes_failsForUnsupportedAttributesType() { - File file = Directory.create(0); + File file = createFile(); try { service.readAttributes(file, PosixFileAttributes.class); fail(); @@ -359,7 +365,7 @@ public void testReadAttributes_failsForUnsupportedAttributesType() { @Test public void testIllegalAttributeFormats() { - File file = Directory.create(0); + File file = createFile(); try { service.getAttribute(file, ":bar"); fail(); diff --git a/jimfs/src/test/java/com/google/common/jimfs/BasicAttributeProviderTest.java b/jimfs/src/test/java/com/google/common/jimfs/BasicAttributeProviderTest.java index a101b789..5c72eeb6 100644 --- a/jimfs/src/test/java/com/google/common/jimfs/BasicAttributeProviderTest.java +++ b/jimfs/src/test/java/com/google/common/jimfs/BasicAttributeProviderTest.java @@ -64,10 +64,10 @@ public void testSupportedAttributes() { @Test public void testInitialAttributes() { - long time = file.getCreationTime(); - assertThat(time).isNotEqualTo(0L); - assertThat(time).isEqualTo(file.getLastAccessTime()); - assertThat(time).isEqualTo(file.getLastModifiedTime()); + FileTime expected = fileTimeSource.now(); + assertThat(file.getCreationTime()).isEqualTo(expected); + assertThat(file.getLastAccessTime()).isEqualTo(expected); + assertThat(file.getLastModifiedTime()).isEqualTo(expected); assertContainsAll( file, @@ -121,22 +121,22 @@ public void testView() throws IOException { BasicFileAttributes attrs = view.readAttributes(); assertThat(attrs.fileKey()).isEqualTo(0); - FileTime time = attrs.creationTime(); - assertThat(attrs.lastAccessTime()).isEqualTo(time); - assertThat(attrs.lastModifiedTime()).isEqualTo(time); + FileTime initial = fileTimeSource.now(); + assertThat(attrs.creationTime()).isEqualTo(initial); + assertThat(attrs.lastAccessTime()).isEqualTo(initial); + assertThat(attrs.lastModifiedTime()).isEqualTo(initial); view.setTimes(null, null, null); - attrs = view.readAttributes(); - assertThat(attrs.creationTime()).isEqualTo(time); - assertThat(attrs.lastAccessTime()).isEqualTo(time); - assertThat(attrs.lastModifiedTime()).isEqualTo(time); + assertThat(attrs.creationTime()).isEqualTo(initial); + assertThat(attrs.lastAccessTime()).isEqualTo(initial); + assertThat(attrs.lastModifiedTime()).isEqualTo(initial); view.setTimes(FileTime.fromMillis(0L), null, null); attrs = view.readAttributes(); - assertThat(attrs.creationTime()).isEqualTo(time); - assertThat(attrs.lastAccessTime()).isEqualTo(time); + assertThat(attrs.creationTime()).isEqualTo(initial); + assertThat(attrs.lastAccessTime()).isEqualTo(initial); assertThat(attrs.lastModifiedTime()).isEqualTo(FileTime.fromMillis(0L)); } diff --git a/jimfs/src/test/java/com/google/common/jimfs/ConfigurationTest.java b/jimfs/src/test/java/com/google/common/jimfs/ConfigurationTest.java index 0404f57f..19898c90 100644 --- a/jimfs/src/test/java/com/google/common/jimfs/ConfigurationTest.java +++ b/jimfs/src/test/java/com/google/common/jimfs/ConfigurationTest.java @@ -70,6 +70,7 @@ public void testDefaultUnixConfiguration() { assertThat(config.attributeViews).containsExactly("basic"); assertThat(config.attributeProviders).isEmpty(); assertThat(config.defaultAttributeValues).isEmpty(); + assertThat(config.fileTimeSource).isEqualTo(SystemFileTimeSource.INSTANCE); } @Test @@ -104,6 +105,7 @@ public void testDefaultOsXConfiguration() { assertThat(config.attributeViews).containsExactly("basic"); assertThat(config.attributeProviders).isEmpty(); assertThat(config.defaultAttributeValues).isEmpty(); + assertThat(config.fileTimeSource).isEqualTo(SystemFileTimeSource.INSTANCE); } @Test @@ -143,6 +145,7 @@ public void testDefaultWindowsConfiguration() { assertThat(config.attributeViews).containsExactly("basic"); assertThat(config.attributeProviders).isEmpty(); assertThat(config.defaultAttributeValues).isEmpty(); + assertThat(config.fileTimeSource).isEqualTo(SystemFileTimeSource.INSTANCE); } @Test @@ -170,6 +173,7 @@ public void testFileSystemForDefaultWindowsConfiguration() throws IOException { public void testBuilder() { AttributeProvider unixProvider = StandardAttributeProviders.get("unix"); + FileTimeSource fileTimeSource = new FakeFileTimeSource(); Configuration config = Configuration.builder(PathType.unix()) .setRoots("/") @@ -184,6 +188,7 @@ public void testBuilder() { .addAttributeProvider(unixProvider) .setDefaultAttributeValue( "posix:permissions", PosixFilePermissions.fromString("---------")) + .setFileTimeSource(fileTimeSource) .build(); assertThat(config.pathType).isEqualTo(PathType.unix()); @@ -199,10 +204,12 @@ public void testBuilder() { assertThat(config.attributeProviders).containsExactly(unixProvider); assertThat(config.defaultAttributeValues) .containsEntry("posix:permissions", PosixFilePermissions.fromString("---------")); + assertThat(config.fileTimeSource).isEqualTo(fileTimeSource); } @Test public void testFileSystemForCustomConfiguration() throws IOException { + FileTimeSource fileTimeSource = new FakeFileTimeSource(); Configuration config = Configuration.builder(PathType.unix()) .setRoots("/") @@ -216,6 +223,7 @@ public void testFileSystemForCustomConfiguration() throws IOException { .setAttributeViews("unix") .setDefaultAttributeValue( "posix:permissions", PosixFilePermissions.fromString("---------")) + .setFileTimeSource(fileTimeSource) .build(); FileSystem fs = Jimfs.newFileSystem(config); @@ -230,6 +238,8 @@ public void testFileSystemForCustomConfiguration() throws IOException { Files.createFile(fs.getPath("/foo")); assertThat(Files.getAttribute(fs.getPath("/foo"), "posix:permissions")) .isEqualTo(PosixFilePermissions.fromString("---------")); + assertThat(Files.getAttribute(fs.getPath("/foo"), "creationTime")) + .isEqualTo(fileTimeSource.now()); try { Files.createFile(fs.getPath("/FOO")); diff --git a/jimfs/src/test/java/com/google/common/jimfs/DirectoryTest.java b/jimfs/src/test/java/com/google/common/jimfs/DirectoryTest.java index 1fee1e5c..ebbd9fd4 100644 --- a/jimfs/src/test/java/com/google/common/jimfs/DirectoryTest.java +++ b/jimfs/src/test/java/com/google/common/jimfs/DirectoryTest.java @@ -42,17 +42,23 @@ @RunWith(JUnit4.class) public class DirectoryTest { + private final FakeFileTimeSource fileTimeSource = new FakeFileTimeSource(); + private Directory root; private Directory dir; @Before public void setUp() { - root = Directory.createRoot(0, Name.simple("/")); + root = Directory.createRoot(0, fileTimeSource.now(), Name.simple("/")); - dir = Directory.create(1); + dir = createDirectory(1); root.link(Name.simple("foo"), dir); } + private Directory createDirectory(int id) { + return Directory.create(id, fileTimeSource.now()); + } + @Test public void testRootDirectory() { assertThat(root.entryCount()).isEqualTo(3); // two for parent/self, one for dir @@ -82,7 +88,7 @@ public void testGet() { public void testLink() { assertThat(dir.get(Name.simple("bar"))).isNull(); - File bar = Directory.create(2); + File bar = createDirectory(2); dir.link(Name.simple("bar"), bar); assertThat(dir.get(Name.simple("bar"))).isEqualTo(entry(dir, "bar", bar)); @@ -91,7 +97,7 @@ public void testLink() { @Test public void testLink_existingNameFails() { try { - root.link(Name.simple("foo"), Directory.create(2)); + root.link(Name.simple("foo"), createDirectory(2)); fail(); } catch (IllegalArgumentException expected) { } @@ -100,13 +106,13 @@ public void testLink_existingNameFails() { @Test public void testLink_parentAndSelfNameFails() { try { - dir.link(Name.simple("."), Directory.create(2)); + dir.link(Name.simple("."), createDirectory(2)); fail(); } catch (IllegalArgumentException expected) { } try { - dir.link(Name.simple(".."), Directory.create(2)); + dir.link(Name.simple(".."), createDirectory(2)); fail(); } catch (IllegalArgumentException expected) { } @@ -114,7 +120,7 @@ public void testLink_parentAndSelfNameFails() { @Test public void testGet_normalizingCaseInsensitive() { - File bar = Directory.create(2); + File bar = createDirectory(2); Name barName = caseInsensitive("bar"); dir.link(barName, bar); @@ -161,7 +167,7 @@ public void testUnlink_parentAndSelfNameFails() { @Test public void testUnlink_normalizingCaseInsensitive() { - dir.link(caseInsensitive("bar"), Directory.create(2)); + dir.link(caseInsensitive("bar"), createDirectory(2)); assertThat(dir.get(caseInsensitive("bar"))).isNotNull(); @@ -172,7 +178,7 @@ public void testUnlink_normalizingCaseInsensitive() { @Test public void testLinkDirectory() { - Directory newDir = Directory.create(10); + Directory newDir = createDirectory(10); assertThat(newDir.entryInParent()).isNull(); assertThat(newDir.get(Name.SELF).file()).isEqualTo(newDir); @@ -191,7 +197,7 @@ public void testLinkDirectory() { @Test public void testUnlinkDirectory() { - Directory newDir = Directory.create(10); + Directory newDir = createDirectory(10); dir.link(Name.simple("foo"), newDir); @@ -244,7 +250,7 @@ public void testSnapshot_sortsUsingStringAndNotCanonicalValueOfNames() { // Tests for internal hash table implementation - private static final Directory A = Directory.create(0); + private final Directory a = createDirectory(0); @Test public void testInitialState() { @@ -361,15 +367,15 @@ public void testManyPutsAndRemoves() { } } - private static DirectoryEntry entry(String name) { - return new DirectoryEntry(A, Name.simple(name), A); + private DirectoryEntry entry(String name) { + return new DirectoryEntry(a, Name.simple(name), a); } - private static DirectoryEntry entry(Directory dir, String name, @NullableDecl File file) { + private DirectoryEntry entry(Directory dir, String name, @NullableDecl File file) { return new DirectoryEntry(dir, Name.simple(name), file); } - private static void assertParentAndSelf(Directory dir, File parent, File self) { + private void assertParentAndSelf(Directory dir, File parent, File self) { assertThat(dir).isEqualTo(self); assertThat(dir.parent()).isEqualTo(parent); diff --git a/jimfs/src/test/java/com/google/common/jimfs/FakeFileTimeSource.java b/jimfs/src/test/java/com/google/common/jimfs/FakeFileTimeSource.java new file mode 100644 index 00000000..942c8354 --- /dev/null +++ b/jimfs/src/test/java/com/google/common/jimfs/FakeFileTimeSource.java @@ -0,0 +1,64 @@ +/* + * Copyright 2021 Google Inc. + * + * Licensed 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. + */ + +package com.google.common.jimfs; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.math.LongMath; +import com.google.errorprone.annotations.CanIgnoreReturnValue; +import java.nio.file.attribute.FileTime; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +/** Fake implementation of {@link FileTimeSource}. */ +final class FakeFileTimeSource implements FileTimeSource { + + private final Random random = new Random(System.currentTimeMillis()); + private FileTime now; + + FakeFileTimeSource() { + randomize(); + } + + @CanIgnoreReturnValue + FakeFileTimeSource setNow(FileTime now) { + this.now = checkNotNull(now); + return this; + } + + @CanIgnoreReturnValue + private FakeFileTimeSource setNowMillis(long millis) { + return setNow(FileTime.fromMillis(millis)); + } + + @CanIgnoreReturnValue + FakeFileTimeSource randomize() { + // Use a random int rather than long as an easy way of ensuring we don't get something near + // Long.MAX_VALUE + return setNowMillis(random.nextInt()); + } + + @CanIgnoreReturnValue + FakeFileTimeSource advance(long duration, TimeUnit unit) { + return setNowMillis(LongMath.checkedAdd(now.toMillis(), unit.toMillis(duration))); + } + + @Override + public FileTime now() { + return now; + } +} diff --git a/jimfs/src/test/java/com/google/common/jimfs/FileFactoryTest.java b/jimfs/src/test/java/com/google/common/jimfs/FileFactoryTest.java index 9e3cb403..ad2e0dc4 100644 --- a/jimfs/src/test/java/com/google/common/jimfs/FileFactoryTest.java +++ b/jimfs/src/test/java/com/google/common/jimfs/FileFactoryTest.java @@ -31,11 +31,13 @@ @RunWith(JUnit4.class) public class FileFactoryTest { + private final FakeFileTimeSource fileTimeSource = new FakeFileTimeSource(); + private FileFactory factory; @Before public void setUp() { - factory = new FileFactory(new HeapDisk(2, 2, 0)); + factory = new FileFactory(new HeapDisk(2, 2, 0), fileTimeSource); } @Test @@ -43,14 +45,19 @@ public void testCreateFiles_basic() { File file = factory.createDirectory(); assertThat(file.id()).isEqualTo(0L); assertThat(file.isDirectory()).isTrue(); + assertThat(file.getCreationTime()).isEqualTo(fileTimeSource.now()); + fileTimeSource.randomize(); file = factory.createRegularFile(); assertThat(file.id()).isEqualTo(1L); assertThat(file.isRegularFile()).isTrue(); + assertThat(file.getCreationTime()).isEqualTo(fileTimeSource.now()); + fileTimeSource.randomize(); file = factory.createSymbolicLink(fakePath()); assertThat(file.id()).isEqualTo(2L); assertThat(file.isSymbolicLink()).isTrue(); + assertThat(file.getCreationTime()).isEqualTo(fileTimeSource.now()); } @Test @@ -58,14 +65,19 @@ public void testCreateFiles_withSupplier() { File file = factory.directoryCreator().get(); assertThat(file.id()).isEqualTo(0L); assertThat(file.isDirectory()).isTrue(); + assertThat(file.getCreationTime()).isEqualTo(fileTimeSource.now()); + fileTimeSource.randomize(); file = factory.regularFileCreator().get(); assertThat(file.id()).isEqualTo(1L); assertThat(file.isRegularFile()).isTrue(); + assertThat(file.getCreationTime()).isEqualTo(fileTimeSource.now()); + fileTimeSource.randomize(); file = factory.symbolicLinkCreator(fakePath()).get(); assertThat(file.id()).isEqualTo(2L); assertThat(file.isSymbolicLink()).isTrue(); + assertThat(file.getCreationTime()).isEqualTo(fileTimeSource.now()); } static JimfsPath fakePath() { diff --git a/jimfs/src/test/java/com/google/common/jimfs/FileSystemStateTest.java b/jimfs/src/test/java/com/google/common/jimfs/FileSystemStateTest.java index f8143b6e..7274ab1e 100644 --- a/jimfs/src/test/java/com/google/common/jimfs/FileSystemStateTest.java +++ b/jimfs/src/test/java/com/google/common/jimfs/FileSystemStateTest.java @@ -16,6 +16,8 @@ package com.google.common.jimfs; +import static com.google.common.truth.Truth.assertThat; +import static java.util.concurrent.TimeUnit.SECONDS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -40,7 +42,8 @@ public class FileSystemStateTest { private final TestRunnable onClose = new TestRunnable(); - private final FileSystemState state = new FileSystemState(onClose); + private final FakeFileTimeSource fileTimeSource = new FakeFileTimeSource(); + private final FileSystemState state = new FileSystemState(fileTimeSource, onClose); @Test public void testIsOpen() throws IOException { @@ -60,6 +63,13 @@ public void testCheckOpen() throws IOException { } } + @Test + public void testNow() { + assertThat(state.now()).isEqualTo(fileTimeSource.now()); + fileTimeSource.advance(1, SECONDS); + assertThat(state.now()).isEqualTo(fileTimeSource.now()); + } + @Test public void testClose_callsOnCloseRunnable() throws IOException { assertEquals(0, onClose.runCount); diff --git a/jimfs/src/test/java/com/google/common/jimfs/FileTest.java b/jimfs/src/test/java/com/google/common/jimfs/FileTest.java index 83cda00e..d128c6a2 100644 --- a/jimfs/src/test/java/com/google/common/jimfs/FileTest.java +++ b/jimfs/src/test/java/com/google/common/jimfs/FileTest.java @@ -32,12 +32,14 @@ @RunWith(JUnit4.class) public class FileTest { + private final FakeFileTimeSource fileTimeSource = new FakeFileTimeSource(); + @Test public void testAttributes() { // these methods are basically just thin wrappers around a map, so no need to test too // thoroughly - File file = RegularFile.create(0, new HeapDisk(10, 10, 10)); + File file = RegularFile.create(0, fileTimeSource.now(), new HeapDisk(10, 10, 10)); assertThat(file.getAttributeKeys()).isEmpty(); assertThat(file.getAttribute("foo", "foo")).isNull(); @@ -65,7 +67,7 @@ public void testFileBasics() { @Test public void testDirectory() { - File file = Directory.create(0); + File file = Directory.create(0, fileTimeSource.now()); assertThat(file.isDirectory()).isTrue(); assertThat(file.isRegularFile()).isFalse(); assertThat(file.isSymbolicLink()).isFalse(); @@ -81,7 +83,7 @@ public void testRegularFile() { @Test public void testSymbolicLink() { - File file = SymbolicLink.create(0, fakePath()); + File file = SymbolicLink.create(0, fileTimeSource.now(), fakePath()); assertThat(file.isDirectory()).isFalse(); assertThat(file.isRegularFile()).isFalse(); assertThat(file.isSymbolicLink()).isTrue(); @@ -89,10 +91,10 @@ public void testSymbolicLink() { @Test public void testRootDirectory() { - Directory file = Directory.createRoot(0, Name.simple("/")); + Directory file = Directory.createRoot(0, fileTimeSource.now(), Name.simple("/")); assertThat(file.isRootDirectory()).isTrue(); - Directory otherFile = Directory.createRoot(1, Name.simple("$")); + Directory otherFile = Directory.createRoot(1, fileTimeSource.now(), Name.simple("$")); assertThat(otherFile.isRootDirectory()).isTrue(); } diff --git a/jimfs/src/test/java/com/google/common/jimfs/FileTreeTest.java b/jimfs/src/test/java/com/google/common/jimfs/FileTreeTest.java index e9259718..12dc7244 100644 --- a/jimfs/src/test/java/com/google/common/jimfs/FileTreeTest.java +++ b/jimfs/src/test/java/com/google/common/jimfs/FileTreeTest.java @@ -108,16 +108,18 @@ public ParseResult parseUriPath(String uriPath) { }, false); + private final FakeFileTimeSource fileTimeSource = new FakeFileTimeSource(); + private FileTree fileTree; private File workingDirectory; private final Map files = new HashMap<>(); @Before public void setUp() { - Directory root = Directory.createRoot(0, Name.simple("/")); + Directory root = Directory.createRoot(0, fileTimeSource.now(), Name.simple("/")); files.put("/", root); - Directory otherRoot = Directory.createRoot(2, Name.simple("$")); + Directory otherRoot = Directory.createRoot(2, fileTimeSource.now(), Name.simple("$")); files.put("$", otherRoot); Map roots = new HashMap<>(); @@ -440,7 +442,7 @@ private void assertParentExists(DirectoryEntry entry, String parent) { private File createDirectory(String parent, String name) { Directory dir = (Directory) files.get(parent); - Directory newFile = Directory.create(new Random().nextInt()); + Directory newFile = Directory.create(new Random().nextInt(), fileTimeSource.now()); dir.link(Name.simple(name), newFile); files.put(name, newFile); return newFile; @@ -456,7 +458,9 @@ private File createFile(String parent, String name) { private File createSymbolicLink(String parent, String name, String target) { Directory dir = (Directory) files.get(parent); - File newFile = SymbolicLink.create(new Random().nextInt(), pathService.parsePath(target)); + File newFile = + SymbolicLink.create( + new Random().nextInt(), fileTimeSource.now(), pathService.parsePath(target)); dir.link(Name.simple(name), newFile); files.put(name, newFile); return newFile; diff --git a/jimfs/src/test/java/com/google/common/jimfs/HeapDiskTest.java b/jimfs/src/test/java/com/google/common/jimfs/HeapDiskTest.java index af09b85e..48235893 100644 --- a/jimfs/src/test/java/com/google/common/jimfs/HeapDiskTest.java +++ b/jimfs/src/test/java/com/google/common/jimfs/HeapDiskTest.java @@ -35,12 +35,14 @@ @RunWith(JUnit4.class) public class HeapDiskTest { + private final FakeFileTimeSource fileTimeSource = new FakeFileTimeSource(); + private RegularFile blocks; @Before public void setUp() { // the HeapDisk of this file is unused; it's passed to other HeapDisks to test operations - blocks = RegularFile.create(-1, new HeapDisk(2, 2, 2)); + blocks = RegularFile.create(-1, fileTimeSource.now(), new HeapDisk(2, 2, 2)); } @Test @@ -216,7 +218,7 @@ public void testFullDisk_doesNotAllocatePartiallyWhenTooManyBlocksRequested() th HeapDisk disk = new HeapDisk(4, 10, 4); disk.allocate(blocks, 6); - RegularFile blocks2 = RegularFile.create(-2, disk); + RegularFile blocks2 = RegularFile.create(-2, fileTimeSource.now(), disk); try { disk.allocate(blocks2, 5); diff --git a/jimfs/src/test/java/com/google/common/jimfs/JimfsAsynchronousFileChannelTest.java b/jimfs/src/test/java/com/google/common/jimfs/JimfsAsynchronousFileChannelTest.java index 7d47588a..a4a4ba91 100644 --- a/jimfs/src/test/java/com/google/common/jimfs/JimfsAsynchronousFileChannelTest.java +++ b/jimfs/src/test/java/com/google/common/jimfs/JimfsAsynchronousFileChannelTest.java @@ -63,7 +63,7 @@ private static JimfsAsynchronousFileChannel channel( new JimfsFileChannel( file, Options.getOptionsForChannel(ImmutableSet.copyOf(options)), - new FileSystemState(Runnables.doNothing())); + new FileSystemState(new FakeFileTimeSource(), Runnables.doNothing())); return new JimfsAsynchronousFileChannel(channel, executor); } diff --git a/jimfs/src/test/java/com/google/common/jimfs/JimfsFileChannelTest.java b/jimfs/src/test/java/com/google/common/jimfs/JimfsFileChannelTest.java index c525ef1f..28723069 100644 --- a/jimfs/src/test/java/com/google/common/jimfs/JimfsFileChannelTest.java +++ b/jimfs/src/test/java/com/google/common/jimfs/JimfsFileChannelTest.java @@ -48,6 +48,7 @@ import java.nio.channels.NonReadableChannelException; import java.nio.channels.NonWritableChannelException; import java.nio.file.OpenOption; +import java.nio.file.attribute.FileTime; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; @@ -74,7 +75,7 @@ private static FileChannel channel(RegularFile file, OpenOption... options) thro return new JimfsFileChannel( file, Options.getOptionsForChannel(ImmutableSet.copyOf(options)), - new FileSystemState(Runnables.doNothing())); + new FileSystemState(new FakeFileTimeSource(), Runnables.doNothing())); } @Test @@ -227,76 +228,77 @@ public void testTruncate() throws IOException { @Test public void testFileTimeUpdates() throws IOException { RegularFile file = regularFile(10); + FakeFileTimeSource fileTimeSource = new FakeFileTimeSource(); FileChannel channel = new JimfsFileChannel( file, ImmutableSet.of(READ, WRITE), - new FileSystemState(Runnables.doNothing())); + new FileSystemState(fileTimeSource, Runnables.doNothing())); - // accessed - long accessTime = file.getLastAccessTime(); - Uninterruptibles.sleepUninterruptibly(2, MILLISECONDS); + // accessedTime + FileTime accessTime = file.getLastAccessTime(); + fileTimeSource.advance(2, MILLISECONDS); channel.read(ByteBuffer.allocate(10)); assertNotEquals(accessTime, file.getLastAccessTime()); accessTime = file.getLastAccessTime(); - Uninterruptibles.sleepUninterruptibly(2, MILLISECONDS); + fileTimeSource.advance(2, MILLISECONDS); channel.read(ByteBuffer.allocate(10), 0); assertNotEquals(accessTime, file.getLastAccessTime()); accessTime = file.getLastAccessTime(); - Uninterruptibles.sleepUninterruptibly(2, MILLISECONDS); + fileTimeSource.advance(2, MILLISECONDS); channel.read(new ByteBuffer[] {ByteBuffer.allocate(10)}); assertNotEquals(accessTime, file.getLastAccessTime()); accessTime = file.getLastAccessTime(); - Uninterruptibles.sleepUninterruptibly(2, MILLISECONDS); + fileTimeSource.advance(2, MILLISECONDS); channel.read(new ByteBuffer[] {ByteBuffer.allocate(10)}, 0, 1); assertNotEquals(accessTime, file.getLastAccessTime()); accessTime = file.getLastAccessTime(); - Uninterruptibles.sleepUninterruptibly(2, MILLISECONDS); + fileTimeSource.advance(2, MILLISECONDS); channel.transferTo(0, 10, new ByteBufferChannel(10)); assertNotEquals(accessTime, file.getLastAccessTime()); // modified - long modifiedTime = file.getLastModifiedTime(); - Uninterruptibles.sleepUninterruptibly(2, MILLISECONDS); + FileTime modifiedTime = file.getLastModifiedTime(); + fileTimeSource.advance(2, MILLISECONDS); channel.write(ByteBuffer.allocate(10)); assertNotEquals(modifiedTime, file.getLastModifiedTime()); modifiedTime = file.getLastModifiedTime(); - Uninterruptibles.sleepUninterruptibly(2, MILLISECONDS); + fileTimeSource.advance(2, MILLISECONDS); channel.write(ByteBuffer.allocate(10), 0); assertNotEquals(modifiedTime, file.getLastModifiedTime()); modifiedTime = file.getLastModifiedTime(); - Uninterruptibles.sleepUninterruptibly(2, MILLISECONDS); + fileTimeSource.advance(2, MILLISECONDS); channel.write(new ByteBuffer[] {ByteBuffer.allocate(10)}); assertNotEquals(modifiedTime, file.getLastModifiedTime()); modifiedTime = file.getLastModifiedTime(); - Uninterruptibles.sleepUninterruptibly(2, MILLISECONDS); + fileTimeSource.advance(2, MILLISECONDS); channel.write(new ByteBuffer[] {ByteBuffer.allocate(10)}, 0, 1); assertNotEquals(modifiedTime, file.getLastModifiedTime()); modifiedTime = file.getLastModifiedTime(); - Uninterruptibles.sleepUninterruptibly(2, MILLISECONDS); + fileTimeSource.advance(2, MILLISECONDS); channel.truncate(0); assertNotEquals(modifiedTime, file.getLastModifiedTime()); modifiedTime = file.getLastModifiedTime(); - Uninterruptibles.sleepUninterruptibly(2, MILLISECONDS); + fileTimeSource.advance(2, MILLISECONDS); channel.transferFrom(new ByteBufferChannel(10), 0, 10); assertNotEquals(modifiedTime, file.getLastModifiedTime()); diff --git a/jimfs/src/test/java/com/google/common/jimfs/JimfsInputStreamTest.java b/jimfs/src/test/java/com/google/common/jimfs/JimfsInputStreamTest.java index 94cc5f47..8a2947e1 100644 --- a/jimfs/src/test/java/com/google/common/jimfs/JimfsInputStreamTest.java +++ b/jimfs/src/test/java/com/google/common/jimfs/JimfsInputStreamTest.java @@ -228,7 +228,8 @@ private static JimfsInputStream newInputStream(int... bytes) throws IOException RegularFile file = regularFile(0); file.write(0, b, 0, b.length); - return new JimfsInputStream(file, new FileSystemState(Runnables.doNothing())); + return new JimfsInputStream( + file, new FileSystemState(new FakeFileTimeSource(), Runnables.doNothing())); } private static void assertEmpty(JimfsInputStream in) throws IOException { diff --git a/jimfs/src/test/java/com/google/common/jimfs/JimfsOutputStreamTest.java b/jimfs/src/test/java/com/google/common/jimfs/JimfsOutputStreamTest.java index 3c230a74..fdec32aa 100644 --- a/jimfs/src/test/java/com/google/common/jimfs/JimfsOutputStreamTest.java +++ b/jimfs/src/test/java/com/google/common/jimfs/JimfsOutputStreamTest.java @@ -184,7 +184,8 @@ public void testClosedOutputStream_doesNotThrowOnFlush() throws IOException { private static JimfsOutputStream newOutputStream(boolean append) { RegularFile file = regularFile(0); - return new JimfsOutputStream(file, append, new FileSystemState(Runnables.doNothing())); + return new JimfsOutputStream( + file, append, new FileSystemState(new FakeFileTimeSource(), Runnables.doNothing())); } @SuppressWarnings("GuardedByChecker") diff --git a/jimfs/src/test/java/com/google/common/jimfs/PollingWatchServiceTest.java b/jimfs/src/test/java/com/google/common/jimfs/PollingWatchServiceTest.java index 86f98323..407f70dd 100644 --- a/jimfs/src/test/java/com/google/common/jimfs/PollingWatchServiceTest.java +++ b/jimfs/src/test/java/com/google/common/jimfs/PollingWatchServiceTest.java @@ -62,7 +62,7 @@ public void setUp() { new PollingWatchService( fs.getDefaultView(), fs.getPathService(), - new FileSystemState(Runnables.doNothing()), + new FileSystemState(new FakeFileTimeSource(), Runnables.doNothing()), 4, MILLISECONDS); } diff --git a/jimfs/src/test/java/com/google/common/jimfs/RegularFileBlocksTest.java b/jimfs/src/test/java/com/google/common/jimfs/RegularFileBlocksTest.java index 5febfc56..22697ffc 100644 --- a/jimfs/src/test/java/com/google/common/jimfs/RegularFileBlocksTest.java +++ b/jimfs/src/test/java/com/google/common/jimfs/RegularFileBlocksTest.java @@ -37,6 +37,8 @@ public class RegularFileBlocksTest { private static final int BLOCK_SIZE = 2; + private final FakeFileTimeSource fileTimeSource = new FakeFileTimeSource(); + private RegularFile file; @Before @@ -44,8 +46,8 @@ public void setUp() { file = createFile(); } - private static RegularFile createFile() { - return RegularFile.create(-1, new HeapDisk(BLOCK_SIZE, 2, 2)); + private RegularFile createFile() { + return RegularFile.create(-1, fileTimeSource.now(), new HeapDisk(BLOCK_SIZE, 2, 2)); } @Test diff --git a/jimfs/src/test/java/com/google/common/jimfs/RegularFileTest.java b/jimfs/src/test/java/com/google/common/jimfs/RegularFileTest.java index 65a47cdd..6beb8873 100644 --- a/jimfs/src/test/java/com/google/common/jimfs/RegularFileTest.java +++ b/jimfs/src/test/java/com/google/common/jimfs/RegularFileTest.java @@ -121,6 +121,8 @@ public static final class TestConfiguration { private final int cacheSize; private final ReuseStrategy reuseStrategy; + private final FakeFileTimeSource fileTimeSource = new FakeFileTimeSource(); + private HeapDisk disk; public TestConfiguration(int blockSize, int cacheSize, ReuseStrategy reuseStrategy) { @@ -142,7 +144,7 @@ public RegularFile createRegularFile() { if (reuseStrategy == ReuseStrategy.NEW_DISK) { disk = createDisk(); } - return RegularFile.create(0, disk); + return RegularFile.create(0, fileTimeSource.now(), disk); } public void tearDown(RegularFile file) { @@ -172,6 +174,7 @@ public String toString() { public static class RegularFileTestRunner extends TestCase { private final TestConfiguration configuration; + private final FakeFileTimeSource fileTimeSource = new FakeFileTimeSource(); protected RegularFile file; @@ -394,7 +397,7 @@ public void testEmpty_transferTo() throws IOException { } public void testEmpty_copy() throws IOException { - RegularFile copy = file.copyWithoutContent(1); + RegularFile copy = file.copyWithoutContent(1, fileTimeSource.now()); assertContentEquals("", copy); } @@ -817,16 +820,16 @@ public void testNonEmpty_transferFrom_hugeOverestimateCount() throws IOException public void testNonEmpty_copy() throws IOException { fillContent("123456"); - RegularFile copy = file.copyWithoutContent(1); + RegularFile copy = file.copyWithoutContent(1, fileTimeSource.now()); file.copyContentTo(copy); assertContentEquals("123456", copy); } public void testNonEmpty_copy_multipleTimes() throws IOException { fillContent("123456"); - RegularFile copy = file.copyWithoutContent(1); + RegularFile copy = file.copyWithoutContent(1, fileTimeSource.now()); file.copyContentTo(copy); - RegularFile copy2 = copy.copyWithoutContent(2); + RegularFile copy2 = copy.copyWithoutContent(2, fileTimeSource.now()); copy.copyContentTo(copy2); assertContentEquals("123456", copy); } diff --git a/jimfs/src/test/java/com/google/common/jimfs/TestUtils.java b/jimfs/src/test/java/com/google/common/jimfs/TestUtils.java index 30e0930b..61e138e9 100644 --- a/jimfs/src/test/java/com/google/common/jimfs/TestUtils.java +++ b/jimfs/src/test/java/com/google/common/jimfs/TestUtils.java @@ -133,7 +133,8 @@ public static void assertNotEquals(Object unexpected, Object actual) { } static RegularFile regularFile(int size) { - RegularFile file = RegularFile.create(0, new HeapDisk(8096, 1000, 1000)); + RegularFile file = + RegularFile.create(0, new FakeFileTimeSource().now(), new HeapDisk(8096, 1000, 1000)); try { file.write(0, new byte[size], 0, size); return file; diff --git a/jimfs/src/test/java/com/google/common/jimfs/UnixAttributeProviderTest.java b/jimfs/src/test/java/com/google/common/jimfs/UnixAttributeProviderTest.java index dc32d202..e0c4608e 100644 --- a/jimfs/src/test/java/com/google/common/jimfs/UnixAttributeProviderTest.java +++ b/jimfs/src/test/java/com/google/common/jimfs/UnixAttributeProviderTest.java @@ -21,7 +21,6 @@ import static com.google.common.truth.Truth.assertThat; import com.google.common.collect.ImmutableSet; -import java.nio.file.attribute.FileTime; import java.nio.file.attribute.PosixFilePermissions; import java.util.Set; import org.junit.Test; @@ -67,7 +66,7 @@ public void testInitialAttributes() { // these have logical origins in attributes from other views assertThat(provider.get(file, "mode")).isEqualTo(0644); // rw-r--r-- - assertThat(provider.get(file, "ctime")).isEqualTo(FileTime.fromMillis(file.getCreationTime())); + assertThat(provider.get(file, "ctime")).isEqualTo(file.getCreationTime()); // this is based on a property this file system does actually have assertThat(provider.get(file, "nlink")).isEqualTo(1);