public static Snapshot of(final Path path, final boolean parallel) {
        Objects.requireNonNull(path);
        final Path root = path.normalize().toAbsolutePath();

        // Don't include the root dir as we are just comparing contents
        final List<PathWithAttributes> paths = FileUtil.deepListContents(root,
                parallel,
                pathWithAttributes2 ->
                        !root.equals(pathWithAttributes2.path().normalize().toAbsolutePath()));

        Stream<PathWithAttributes> stream = paths.stream();
        if (parallel) {
            //noinspection ReassignedVariable
            stream = stream.parallel();
        }
        final List<PathSnapshot> pathSnapshots = stream
                .map(pathWithAttributes -> {
                    final Set<PathFlag> flags = getPathFlags(pathWithAttributes);
                    final String hash = hashFileContents(pathWithAttributes);
                    final long size = pathWithAttributes.isRegularFile()
                            ? pathWithAttributes.size()
                            : 0;
                    return new PathSnapshot(
                            root.relativize(pathWithAttributes.path().normalize()),
                            hash,
                            size,
                            flags);
                })
                .sorted()
                .toList();

        return new Snapshot(root, pathSnapshots);
    }
Take a deep snapshot of a directory capturing all relative paths, file sizes,
path types and sha256 hashes of each file. The root path
is not included in the output and all paths in the snapshot are relative to it.
<p>
It means you can do something like
<pre>{@code
assertThat(DirectorySnapshot.of(path1))
    .isEqualTo(DirectorySnapshot.of(path2))
}</pre>
</p>

@param path     The root path to create a snapshot of
@param parallel Whether to use multiple threads to scan the dir
stroom-test-common/src/main/java/stroom/test/common/DirectorySnapshot.java