/*
* This file is part of Fim - File Integrity Manager
*
* Copyright (C) 2017 Etienne Vrignaud
*
* Fim is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Fim is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Fim. If not, see <http://www.gnu.org/licenses/>.
*/
package org.fim.internal.hash;
import org.fim.model.Constants;
import org.fim.model.Context;
import org.fim.model.FileHash;
import org.fim.tooling.RepositoryTool;
import org.fim.tooling.StateAssert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import static java.lang.Math.min;
import static java.nio.file.StandardOpenOption.CREATE;
import static org.apache.commons.lang3.time.DurationFormatUtils.formatDuration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.fim.model.HashMode.hashSmallBlock;
import static org.fim.tooling.TestConstants._1_KB;
import static org.fim.util.FileUtil.byteCountToDisplaySize;
import static org.mockito.Mockito.mock;
@Ignore // Don't run it during unit tests
public class FileHasherPerformanceTest extends StateAssert {
public static final int TOTAL_FILE_CONT = 2000;
private static byte contentBytes[];
static {
StringBuilder builder = new StringBuilder();
for (char c = 33; c < 126; c++) {
builder.append(c);
}
contentBytes = builder.toString().getBytes();
}
private long globalSequenceCount = 0;
private HashProgress hashProgress;
private Context context;
private FileHasher cut;
private RepositoryTool tool;
private Path rootDir;
@Before
public void setUp() throws NoSuchAlgorithmException, IOException {
tool = new RepositoryTool(this.getClass(), hashSmallBlock);
rootDir = tool.getRootDir();
context = tool.getContext();
hashProgress = mock(HashProgress.class);
cut = new FileHasher(context, null, hashProgress, null, rootDir.toString());
}
@Test
public void createFiles() throws IOException {
long start = System.currentTimeMillis();
for (int fileCount = 0; fileCount < TOTAL_FILE_CONT; fileCount++) {
createFileWithSize(fileCount, (5 * Constants._1_MB) + 291);
}
long duration = System.currentTimeMillis() - start;
System.out.println("Took: " + formatDuration(duration, "HH:mm:ss"));
}
@Test
public void hashFiles() throws IOException {
long start = System.currentTimeMillis();
List<FileHash> allHash = new ArrayList();
for (int fileCount = 0; fileCount < TOTAL_FILE_CONT; fileCount++) {
Path fileToHash = context.getRepositoryRootDir().resolve("file_" + fileCount);
allHash.add(cut.hashFile(fileToHash, Files.size(fileToHash)));
}
long duration = System.currentTimeMillis() - start;
System.out.println("Took: " + formatDuration(duration, "HH:mm:ss"));
System.out.println("Total bytes hash=" + byteCountToDisplaySize(cut.getTotalBytesHashed()));
}
private Path createFileWithSize(int fileCount, int fileSize) throws IOException {
Path newFile = context.getRepositoryRootDir().resolve("file_" + fileCount);
if (Files.exists(newFile)) {
Files.delete(newFile);
}
if (fileSize == 0) {
Files.createFile(newFile);
return newFile;
}
try (ByteArrayOutputStream out = new ByteArrayOutputStream(fileSize)) {
int contentSize = _1_KB / 4;
int remaining = fileSize;
for (; remaining > 0; globalSequenceCount++) {
int size = min(contentSize, remaining);
byte[] content = generateContent(globalSequenceCount, size);
remaining -= size;
out.write(content);
}
byte[] fileContent = out.toByteArray();
assertThat(fileContent.length).isEqualTo(fileSize);
Files.write(newFile, fileContent, CREATE);
}
return newFile;
}
private byte[] generateContent(long sequenceCount, int contentSize) {
byte[] content = new byte[contentSize];
for (int index = 0; index < contentSize; index += 2) {
content[index] = getContentByte(sequenceCount, false);
if (index + 1 < contentSize) {
content[index + 1] = getContentByte(sequenceCount, true);
}
}
return content;
}
private byte getContentByte(long sequenceCount, boolean fromTheEnd) {
int index = (int) (sequenceCount % contentBytes.length);
if (fromTheEnd) {
index = contentBytes.length - 1 - index;
}
return contentBytes[index];
}
}