added failing repo
diff --git a/tests/resources/issue_3020/.cproject b/tests/resources/issue_3020/.cproject
new file mode 100644
index 0000000..31be124
--- /dev/null
+++ b/tests/resources/issue_3020/.cproject
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>

+<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">

+	<storageModule moduleId="org.eclipse.cdt.core.settings">

+		<cconfiguration id="cdt.managedbuild.config.gnu.mingw.exe.debug.1091074575">

+			<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.config.gnu.mingw.exe.debug.1091074575" moduleId="org.eclipse.cdt.core.settings" name="Debug">

+				<externalSettings/>

+				<extensions>

+					<extension id="org.eclipse.cdt.core.PE" point="org.eclipse.cdt.core.BinaryParser"/>

+					<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>

+					<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>

+					<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>

+				</extensions>

+			</storageModule>

+			<storageModule moduleId="cdtBuildSystem" version="4.0.0">

+				<configuration artifactName="${ProjName}" buildArtefactType="org.eclipse.cdt.build.core.buildArtefactType.exe" buildProperties="org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.cdt.build.core.buildArtefactType.exe,org.eclipse.cdt.build.core.buildType=org.eclipse.cdt.build.core.buildType.debug" cleanCommand="rm -rf" description="" id="cdt.managedbuild.config.gnu.mingw.exe.debug.1091074575" name="Debug" parent="cdt.managedbuild.config.gnu.mingw.exe.debug" prebuildStep="windres --language 0x0C09 ../resource.rc -o resource.o">

+					<folderInfo id="cdt.managedbuild.config.gnu.mingw.exe.debug.1091074575." name="/" resourcePath="">

+						<toolChain id="cdt.managedbuild.toolchain.gnu.mingw.exe.debug.1212365933" name="MinGW GCC" superClass="cdt.managedbuild.toolchain.gnu.mingw.exe.debug">

+							<targetPlatform id="cdt.managedbuild.target.gnu.platform.mingw.exe.debug.441467939" name="Debug Platform" superClass="cdt.managedbuild.target.gnu.platform.mingw.exe.debug"/>

+							<builder buildPath="${workspace_loc:/HashCheck}/Debug" id="cdt.managedbuild.tool.gnu.builder.mingw.base.314434993" keepEnvironmentInBuildfile="false" managedBuildOn="true" name="CDT Internal Builder" superClass="cdt.managedbuild.tool.gnu.builder.mingw.base"/>

+							<tool id="cdt.managedbuild.tool.gnu.assembler.mingw.exe.debug.399759892" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.mingw.exe.debug">

+								<inputType id="cdt.managedbuild.tool.gnu.assembler.input.128467239" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>

+							</tool>

+							<tool id="cdt.managedbuild.tool.gnu.archiver.mingw.base.964797068" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.mingw.base"/>

+							<tool id="cdt.managedbuild.tool.gnu.cpp.compiler.mingw.exe.debug.577493578" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.mingw.exe.debug">

+								<option id="gnu.cpp.compiler.mingw.exe.debug.option.optimization.level.1572524904" name="Optimization Level" superClass="gnu.cpp.compiler.mingw.exe.debug.option.optimization.level" useByScannerDiscovery="false" value="gnu.cpp.compiler.optimization.level.none" valueType="enumerated"/>

+								<option id="gnu.cpp.compiler.mingw.exe.debug.option.debugging.level.1751158473" name="Debug Level" superClass="gnu.cpp.compiler.mingw.exe.debug.option.debugging.level" useByScannerDiscovery="false" value="gnu.cpp.compiler.debugging.level.max" valueType="enumerated"/>

+								<option id="gnu.cpp.compiler.option.dialect.std.261474219" name="Language standard" superClass="gnu.cpp.compiler.option.dialect.std" value="gnu.cpp.compiler.dialect.c++11" valueType="enumerated"/>

+								<option id="gnu.cpp.compiler.option.preprocessor.def.426458244" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" valueType="definedSymbols">

+									<listOptionValue builtIn="false" value="WINVER=0x0500"/>

+									<listOptionValue builtIn="false" value="UNICODE"/>

+									<listOptionValue builtIn="false" value="_UNICODE"/>

+								</option>

+								<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.999351736" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>

+							</tool>

+							<tool id="cdt.managedbuild.tool.gnu.c.compiler.mingw.exe.debug.190088868" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.mingw.exe.debug">

+								<option defaultValue="gnu.c.optimization.level.none" id="gnu.c.compiler.mingw.exe.debug.option.optimization.level.2111319354" name="Optimization Level" superClass="gnu.c.compiler.mingw.exe.debug.option.optimization.level" useByScannerDiscovery="false" valueType="enumerated"/>

+								<option id="gnu.c.compiler.mingw.exe.debug.option.debugging.level.1477168937" name="Debug Level" superClass="gnu.c.compiler.mingw.exe.debug.option.debugging.level" useByScannerDiscovery="false" value="gnu.c.debugging.level.max" valueType="enumerated"/>

+								<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.2145578564" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>

+							</tool>

+							<tool id="cdt.managedbuild.tool.gnu.c.linker.mingw.exe.debug.223724342" name="MinGW C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.mingw.exe.debug"/>

+							<tool id="cdt.managedbuild.tool.gnu.cpp.linker.mingw.exe.debug.778390294" name="MinGW C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.mingw.exe.debug">

+								<option id="gnu.cpp.link.option.flags.1379394346" name="Linker flags" superClass="gnu.cpp.link.option.flags" value="-static -mwindows" valueType="string"/>

+								<option id="gnu.cpp.link.option.userobjs.1507009136" name="Other objects" superClass="gnu.cpp.link.option.userobjs" valueType="userObjs">

+									<listOptionValue builtIn="false" value="Debug\resource.o"/>

+								</option>

+								<option id="gnu.cpp.link.option.libs.109316457" name="Libraries (-l)" superClass="gnu.cpp.link.option.libs" valueType="libs">

+									<listOptionValue builtIn="false" value="comctl32"/>

+									<listOptionValue builtIn="false" value="ole32"/>

+								</option>

+								<inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.1851156384" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">

+									<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>

+									<additionalInput kind="additionalinput" paths="$(LIBS)"/>

+								</inputType>

+							</tool>

+						</toolChain>

+					</folderInfo>

+				</configuration>

+			</storageModule>

+			<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>

+		</cconfiguration>

+		<cconfiguration id="cdt.managedbuild.config.gnu.mingw.exe.release.107885674">

+			<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.config.gnu.mingw.exe.release.107885674" moduleId="org.eclipse.cdt.core.settings" name="Release">

+				<externalSettings/>

+				<extensions>

+					<extension id="org.eclipse.cdt.core.PE" point="org.eclipse.cdt.core.BinaryParser"/>

+					<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>

+					<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>

+					<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>

+				</extensions>

+			</storageModule>

+			<storageModule moduleId="cdtBuildSystem" version="4.0.0">

+				<configuration artifactName="${ProjName}" buildArtefactType="org.eclipse.cdt.build.core.buildArtefactType.exe" buildProperties="org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.cdt.build.core.buildArtefactType.exe,org.eclipse.cdt.build.core.buildType=org.eclipse.cdt.build.core.buildType.release" cleanCommand="rm -rf" description="" id="cdt.managedbuild.config.gnu.mingw.exe.release.107885674" name="Release" parent="cdt.managedbuild.config.gnu.mingw.exe.release" postbuildStep="../Tools/upx -9 HashCheck.exe" prebuildStep="windres --language 0x0C09 ../resource.rc -o resource.o">

+					<folderInfo id="cdt.managedbuild.config.gnu.mingw.exe.release.107885674." name="/" resourcePath="">

+						<toolChain id="cdt.managedbuild.toolchain.gnu.mingw.exe.release.730226646" name="MinGW GCC" superClass="cdt.managedbuild.toolchain.gnu.mingw.exe.release">

+							<targetPlatform id="cdt.managedbuild.target.gnu.platform.mingw.exe.release.550680648" name="Debug Platform" superClass="cdt.managedbuild.target.gnu.platform.mingw.exe.release"/>

+							<builder buildPath="${workspace_loc:/HashCheck}/Release" id="cdt.managedbuild.tool.gnu.builder.mingw.base.1824334986" keepEnvironmentInBuildfile="false" managedBuildOn="true" name="CDT Internal Builder" superClass="cdt.managedbuild.tool.gnu.builder.mingw.base"/>

+							<tool id="cdt.managedbuild.tool.gnu.assembler.mingw.exe.release.690093994" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.mingw.exe.release">

+								<inputType id="cdt.managedbuild.tool.gnu.assembler.input.1352127665" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>

+							</tool>

+							<tool id="cdt.managedbuild.tool.gnu.archiver.mingw.base.1854173426" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.mingw.base"/>

+							<tool id="cdt.managedbuild.tool.gnu.cpp.compiler.mingw.exe.release.420350693" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.mingw.exe.release">

+								<option id="gnu.cpp.compiler.mingw.exe.release.option.optimization.level.1631452254" name="Optimization Level" superClass="gnu.cpp.compiler.mingw.exe.release.option.optimization.level" useByScannerDiscovery="false" value="gnu.cpp.compiler.optimization.level.most" valueType="enumerated"/>

+								<option id="gnu.cpp.compiler.mingw.exe.release.option.debugging.level.1439656061" name="Debug Level" superClass="gnu.cpp.compiler.mingw.exe.release.option.debugging.level" useByScannerDiscovery="false" value="gnu.cpp.compiler.debugging.level.none" valueType="enumerated"/>

+								<option id="gnu.cpp.compiler.option.preprocessor.def.404719786" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" useByScannerDiscovery="false" valueType="definedSymbols">

+									<listOptionValue builtIn="false" value="WINVER=0x0500"/>

+									<listOptionValue builtIn="false" value="UNICODE"/>

+									<listOptionValue builtIn="false" value="_UNICODE"/>

+								</option>

+								<option id="gnu.cpp.compiler.option.dialect.std.1173067960" name="Language standard" superClass="gnu.cpp.compiler.option.dialect.std" useByScannerDiscovery="true" value="gnu.cpp.compiler.dialect.c++11" valueType="enumerated"/>

+								<option id="gnu.cpp.compiler.option.other.other.1902866398" name="Other flags" superClass="gnu.cpp.compiler.option.other.other" useByScannerDiscovery="false" value="-c -fmessage-length=0" valueType="string"/>

+								<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1293697913" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>

+							</tool>

+							<tool id="cdt.managedbuild.tool.gnu.c.compiler.mingw.exe.release.685010761" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.mingw.exe.release">

+								<option defaultValue="gnu.c.optimization.level.most" id="gnu.c.compiler.mingw.exe.release.option.optimization.level.61395149" name="Optimization Level" superClass="gnu.c.compiler.mingw.exe.release.option.optimization.level" useByScannerDiscovery="false" valueType="enumerated"/>

+								<option id="gnu.c.compiler.mingw.exe.release.option.debugging.level.953333000" name="Debug Level" superClass="gnu.c.compiler.mingw.exe.release.option.debugging.level" useByScannerDiscovery="false" value="gnu.c.debugging.level.none" valueType="enumerated"/>

+								<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.998887372" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>

+							</tool>

+							<tool id="cdt.managedbuild.tool.gnu.c.linker.mingw.exe.release.221308893" name="MinGW C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.mingw.exe.release"/>

+							<tool id="cdt.managedbuild.tool.gnu.cpp.linker.mingw.exe.release.1671425636" name="MinGW C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.mingw.exe.release">

+								<option id="gnu.cpp.link.option.flags.2092716378" name="Linker flags" superClass="gnu.cpp.link.option.flags" value="-static -mwindows" valueType="string"/>

+								<option id="gnu.cpp.link.option.userobjs.1119100108" name="Other objects" superClass="gnu.cpp.link.option.userobjs" valueType="userObjs">

+									<listOptionValue builtIn="false" value="&quot;Release\resource.o&quot;"/>

+								</option>

+								<option id="gnu.cpp.link.option.strip.1903154219" name="Omit all symbol information (-s)" superClass="gnu.cpp.link.option.strip" value="true" valueType="boolean"/>

+								<inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.206988463" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">

+									<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>

+									<additionalInput kind="additionalinput" paths="$(LIBS)"/>

+								</inputType>

+							</tool>

+						</toolChain>

+					</folderInfo>

+				</configuration>

+			</storageModule>

+			<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>

+		</cconfiguration>

+	</storageModule>

+	<storageModule moduleId="cdtBuildSystem" version="4.0.0">

+		<project id="HashCheck.cdt.managedbuild.target.gnu.mingw.exe.1855875242" name="Executable" projectType="cdt.managedbuild.target.gnu.mingw.exe"/>

+	</storageModule>

+	<storageModule moduleId="scannerConfiguration">

+		<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>

+		<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.mingw.exe.debug.1091074575;cdt.managedbuild.config.gnu.mingw.exe.debug.1091074575.;cdt.managedbuild.tool.gnu.cpp.compiler.mingw.exe.debug.577493578;cdt.managedbuild.tool.gnu.cpp.compiler.input.999351736">

+			<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>

+		</scannerConfigBuildInfo>

+		<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.mingw.exe.debug.1091074575;cdt.managedbuild.config.gnu.mingw.exe.debug.1091074575.;cdt.managedbuild.tool.gnu.c.compiler.mingw.exe.debug.190088868;cdt.managedbuild.tool.gnu.c.compiler.input.2145578564">

+			<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>

+		</scannerConfigBuildInfo>

+		<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.mingw.exe.release.107885674;cdt.managedbuild.config.gnu.mingw.exe.release.107885674.;cdt.managedbuild.tool.gnu.cpp.compiler.mingw.exe.release.420350693;cdt.managedbuild.tool.gnu.cpp.compiler.input.1293697913">

+			<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>

+		</scannerConfigBuildInfo>

+		<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.mingw.exe.release.107885674;cdt.managedbuild.config.gnu.mingw.exe.release.107885674.;cdt.managedbuild.tool.gnu.c.compiler.mingw.exe.release.685010761;cdt.managedbuild.tool.gnu.c.compiler.input.998887372">

+			<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>

+		</scannerConfigBuildInfo>

+	</storageModule>

+	<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>

+	<storageModule moduleId="refreshScope" versionNumber="2">

+		<configuration configurationName="Debug">

+			<resource resourceType="PROJECT" workspacePath="/HashCheck"/>

+		</configuration>

+		<configuration configurationName="Release">

+			<resource resourceType="PROJECT" workspacePath="/HashCheck"/>

+		</configuration>

+	</storageModule>

+	<storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/>

+</cproject>

diff --git a/tests/resources/issue_3020/.gitignore b/tests/resources/issue_3020/.gitignore
new file mode 100644
index 0000000..0bec05e
--- /dev/null
+++ b/tests/resources/issue_3020/.gitignore
@@ -0,0 +1,10 @@
+/Release/

+/Debug/

+/Test/

+*.user

+*.suo

+*.sdf

+*.opensdf

+*.aps

+checksum.*

+scratch.txt

diff --git a/tests/resources/issue_3020/.gitted/FETCH_HEAD b/tests/resources/issue_3020/.gitted/FETCH_HEAD
new file mode 100644
index 0000000..fad9a6c
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/FETCH_HEAD
@@ -0,0 +1 @@
+77e8039fba38e532bea5fba5f4f3be9940ea521b		branch 'master' of https://github.com/macote/HashCheck
diff --git a/tests/resources/issue_3020/.gitted/HEAD b/tests/resources/issue_3020/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/issue_3020/.gitted/config b/tests/resources/issue_3020/.gitted/config
new file mode 100644
index 0000000..5d565d2
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/config
@@ -0,0 +1,8 @@
+[core]
+	repositoryformatversion = 0
+	filemode = false
+	bare = false
+	logallrefupdates = true
+	symlinks = false
+	ignorecase = true
+	hideDotFiles = dotGitOnly
\ No newline at end of file
diff --git a/tests/resources/issue_3020/.gitted/description b/tests/resources/issue_3020/.gitted/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/issue_3020/.gitted/hooks/applypatch-msg.sample b/tests/resources/issue_3020/.gitted/hooks/applypatch-msg.sample
new file mode 100644
index 0000000..8b2a2fe
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/hooks/applypatch-msg.sample
@@ -0,0 +1,15 @@
+#!/bin/sh
+#
+# An example hook script to check the commit log message taken by
+# applypatch from an e-mail message.
+#
+# The hook should exit with non-zero status after issuing an
+# appropriate message if it wants to stop the commit.  The hook is
+# allowed to edit the commit message file.
+#
+# To enable this hook, rename this file to "applypatch-msg".
+
+. git-sh-setup
+test -x "$GIT_DIR/hooks/commit-msg" &&
+	exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"}
+:
diff --git a/tests/resources/issue_3020/.gitted/hooks/commit-msg.sample b/tests/resources/issue_3020/.gitted/hooks/commit-msg.sample
new file mode 100644
index 0000000..b58d118
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/hooks/commit-msg.sample
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+# An example hook script to check the commit log message.
+# Called by "git commit" with one argument, the name of the file
+# that has the commit message.  The hook should exit with non-zero
+# status after issuing an appropriate message if it wants to stop the
+# commit.  The hook is allowed to edit the commit message file.
+#
+# To enable this hook, rename this file to "commit-msg".
+
+# Uncomment the below to add a Signed-off-by line to the message.
+# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
+# hook is more suited to it.
+#
+# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
+# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
+
+# This example catches duplicate Signed-off-by lines.
+
+test "" = "$(grep '^Signed-off-by: ' "$1" |
+	 sort | uniq -c | sed -e '/^[ 	]*1[ 	]/d')" || {
+	echo >&2 Duplicate Signed-off-by lines.
+	exit 1
+}
diff --git a/tests/resources/issue_3020/.gitted/hooks/post-update.sample b/tests/resources/issue_3020/.gitted/hooks/post-update.sample
new file mode 100644
index 0000000..ec17ec1
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/hooks/post-update.sample
@@ -0,0 +1,8 @@
+#!/bin/sh
+#
+# An example hook script to prepare a packed repository for use over
+# dumb transports.
+#
+# To enable this hook, rename this file to "post-update".
+
+exec git update-server-info
diff --git a/tests/resources/issue_3020/.gitted/hooks/pre-applypatch.sample b/tests/resources/issue_3020/.gitted/hooks/pre-applypatch.sample
new file mode 100644
index 0000000..b1f187c
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/hooks/pre-applypatch.sample
@@ -0,0 +1,14 @@
+#!/bin/sh
+#
+# An example hook script to verify what is about to be committed
+# by applypatch from an e-mail message.
+#
+# The hook should exit with non-zero status after issuing an
+# appropriate message if it wants to stop the commit.
+#
+# To enable this hook, rename this file to "pre-applypatch".
+
+. git-sh-setup
+test -x "$GIT_DIR/hooks/pre-commit" &&
+	exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"}
+:
diff --git a/tests/resources/issue_3020/.gitted/hooks/pre-commit.sample b/tests/resources/issue_3020/.gitted/hooks/pre-commit.sample
new file mode 100644
index 0000000..68d62d5
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/hooks/pre-commit.sample
@@ -0,0 +1,49 @@
+#!/bin/sh
+#
+# An example hook script to verify what is about to be committed.
+# Called by "git commit" with no arguments.  The hook should
+# exit with non-zero status after issuing an appropriate message if
+# it wants to stop the commit.
+#
+# To enable this hook, rename this file to "pre-commit".
+
+if git rev-parse --verify HEAD >/dev/null 2>&1
+then
+	against=HEAD
+else
+	# Initial commit: diff against an empty tree object
+	against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
+fi
+
+# If you want to allow non-ASCII filenames set this variable to true.
+allownonascii=$(git config --bool hooks.allownonascii)
+
+# Redirect output to stderr.
+exec 1>&2
+
+# Cross platform projects tend to avoid non-ASCII filenames; prevent
+# them from being added to the repository. We exploit the fact that the
+# printable range starts at the space character and ends with tilde.
+if [ "$allownonascii" != "true" ] &&
+	# Note that the use of brackets around a tr range is ok here, (it's
+	# even required, for portability to Solaris 10's /usr/bin/tr), since
+	# the square bracket bytes happen to fall in the designated range.
+	test $(git diff --cached --name-only --diff-filter=A -z $against |
+	  LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
+then
+	cat <<\EOF
+Error: Attempt to add a non-ASCII file name.
+
+This can cause problems if you want to work with people on other platforms.
+
+To be portable it is advisable to rename the file.
+
+If you know what you are doing you can disable this check using:
+
+  git config hooks.allownonascii true
+EOF
+	exit 1
+fi
+
+# If there are whitespace errors, print the offending file names and fail.
+exec git diff-index --check --cached $against --
diff --git a/tests/resources/issue_3020/.gitted/hooks/pre-push.sample b/tests/resources/issue_3020/.gitted/hooks/pre-push.sample
new file mode 100644
index 0000000..1f3bceb
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/hooks/pre-push.sample
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+# An example hook script to verify what is about to be pushed.  Called by "git
+# push" after it has checked the remote status, but before anything has been
+# pushed.  If this script exits with a non-zero status nothing will be pushed.
+#
+# This hook is called with the following parameters:
+#
+# $1 -- Name of the remote to which the push is being done
+# $2 -- URL to which the push is being done
+#
+# If pushing without using a named remote those arguments will be equal.
+#
+# Information about the commits which are being pushed is supplied as lines to
+# the standard input in the form:
+#
+#   <local ref> <local sha1> <remote ref> <remote sha1>
+#
+# This sample shows how to prevent push of commits where the log message starts
+# with "WIP" (work in progress).
+
+remote="$1"
+url="$2"
+
+z40=0000000000000000000000000000000000000000
+
+IFS=' '
+while read local_ref local_sha remote_ref remote_sha
+do
+	if [ "$local_sha" = $z40 ]
+	then
+		# Handle delete
+		:
+	else
+		if [ "$remote_sha" = $z40 ]
+		then
+			# New branch, examine all commits
+			range="$local_sha"
+		else
+			# Update to existing branch, examine new commits
+			range="$remote_sha..$local_sha"
+		fi
+
+		# Check for WIP commit
+		commit=`git rev-list -n 1 --grep '^WIP' "$range"`
+		if [ -n "$commit" ]
+		then
+			echo "Found WIP commit in $local_ref, not pushing"
+			exit 1
+		fi
+	fi
+done
+
+exit 0
diff --git a/tests/resources/issue_3020/.gitted/hooks/pre-rebase.sample b/tests/resources/issue_3020/.gitted/hooks/pre-rebase.sample
new file mode 100644
index 0000000..9773ed4
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/hooks/pre-rebase.sample
@@ -0,0 +1,169 @@
+#!/bin/sh
+#
+# Copyright (c) 2006, 2008 Junio C Hamano
+#
+# The "pre-rebase" hook is run just before "git rebase" starts doing
+# its job, and can prevent the command from running by exiting with
+# non-zero status.
+#
+# The hook is called with the following parameters:
+#
+# $1 -- the upstream the series was forked from.
+# $2 -- the branch being rebased (or empty when rebasing the current branch).
+#
+# This sample shows how to prevent topic branches that are already
+# merged to 'next' branch from getting rebased, because allowing it
+# would result in rebasing already published history.
+
+publish=next
+basebranch="$1"
+if test "$#" = 2
+then
+	topic="refs/heads/$2"
+else
+	topic=`git symbolic-ref HEAD` ||
+	exit 0 ;# we do not interrupt rebasing detached HEAD
+fi
+
+case "$topic" in
+refs/heads/??/*)
+	;;
+*)
+	exit 0 ;# we do not interrupt others.
+	;;
+esac
+
+# Now we are dealing with a topic branch being rebased
+# on top of master.  Is it OK to rebase it?
+
+# Does the topic really exist?
+git show-ref -q "$topic" || {
+	echo >&2 "No such branch $topic"
+	exit 1
+}
+
+# Is topic fully merged to master?
+not_in_master=`git rev-list --pretty=oneline ^master "$topic"`
+if test -z "$not_in_master"
+then
+	echo >&2 "$topic is fully merged to master; better remove it."
+	exit 1 ;# we could allow it, but there is no point.
+fi
+
+# Is topic ever merged to next?  If so you should not be rebasing it.
+only_next_1=`git rev-list ^master "^$topic" ${publish} | sort`
+only_next_2=`git rev-list ^master           ${publish} | sort`
+if test "$only_next_1" = "$only_next_2"
+then
+	not_in_topic=`git rev-list "^$topic" master`
+	if test -z "$not_in_topic"
+	then
+		echo >&2 "$topic is already up-to-date with master"
+		exit 1 ;# we could allow it, but there is no point.
+	else
+		exit 0
+	fi
+else
+	not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"`
+	/usr/bin/perl -e '
+		my $topic = $ARGV[0];
+		my $msg = "* $topic has commits already merged to public branch:\n";
+		my (%not_in_next) = map {
+			/^([0-9a-f]+) /;
+			($1 => 1);
+		} split(/\n/, $ARGV[1]);
+		for my $elem (map {
+				/^([0-9a-f]+) (.*)$/;
+				[$1 => $2];
+			} split(/\n/, $ARGV[2])) {
+			if (!exists $not_in_next{$elem->[0]}) {
+				if ($msg) {
+					print STDERR $msg;
+					undef $msg;
+				}
+				print STDERR " $elem->[1]\n";
+			}
+		}
+	' "$topic" "$not_in_next" "$not_in_master"
+	exit 1
+fi
+
+exit 0
+
+################################################################
+
+This sample hook safeguards topic branches that have been
+published from being rewound.
+
+The workflow assumed here is:
+
+ * Once a topic branch forks from "master", "master" is never
+   merged into it again (either directly or indirectly).
+
+ * Once a topic branch is fully cooked and merged into "master",
+   it is deleted.  If you need to build on top of it to correct
+   earlier mistakes, a new topic branch is created by forking at
+   the tip of the "master".  This is not strictly necessary, but
+   it makes it easier to keep your history simple.
+
+ * Whenever you need to test or publish your changes to topic
+   branches, merge them into "next" branch.
+
+The script, being an example, hardcodes the publish branch name
+to be "next", but it is trivial to make it configurable via
+$GIT_DIR/config mechanism.
+
+With this workflow, you would want to know:
+
+(1) ... if a topic branch has ever been merged to "next".  Young
+    topic branches can have stupid mistakes you would rather
+    clean up before publishing, and things that have not been
+    merged into other branches can be easily rebased without
+    affecting other people.  But once it is published, you would
+    not want to rewind it.
+
+(2) ... if a topic branch has been fully merged to "master".
+    Then you can delete it.  More importantly, you should not
+    build on top of it -- other people may already want to
+    change things related to the topic as patches against your
+    "master", so if you need further changes, it is better to
+    fork the topic (perhaps with the same name) afresh from the
+    tip of "master".
+
+Let's look at this example:
+
+		   o---o---o---o---o---o---o---o---o---o "next"
+		  /       /           /           /
+		 /   a---a---b A     /           /
+		/   /               /           /
+	       /   /   c---c---c---c B         /
+	      /   /   /             \         /
+	     /   /   /   b---b C     \       /
+	    /   /   /   /             \     /
+    ---o---o---o---o---o---o---o---o---o---o---o "master"
+
+
+A, B and C are topic branches.
+
+ * A has one fix since it was merged up to "next".
+
+ * B has finished.  It has been fully merged up to "master" and "next",
+   and is ready to be deleted.
+
+ * C has not merged to "next" at all.
+
+We would want to allow C to be rebased, refuse A, and encourage
+B to be deleted.
+
+To compute (1):
+
+	git rev-list ^master ^topic next
+	git rev-list ^master        next
+
+	if these match, topic has not merged in next at all.
+
+To compute (2):
+
+	git rev-list master..topic
+
+	if this is empty, it is fully merged to "master".
diff --git a/tests/resources/issue_3020/.gitted/hooks/prepare-commit-msg.sample b/tests/resources/issue_3020/.gitted/hooks/prepare-commit-msg.sample
new file mode 100644
index 0000000..f093a02
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/hooks/prepare-commit-msg.sample
@@ -0,0 +1,36 @@
+#!/bin/sh
+#
+# An example hook script to prepare the commit log message.
+# Called by "git commit" with the name of the file that has the
+# commit message, followed by the description of the commit
+# message's source.  The hook's purpose is to edit the commit
+# message file.  If the hook fails with a non-zero status,
+# the commit is aborted.
+#
+# To enable this hook, rename this file to "prepare-commit-msg".
+
+# This hook includes three examples.  The first comments out the
+# "Conflicts:" part of a merge commit.
+#
+# The second includes the output of "git diff --name-status -r"
+# into the message, just before the "git status" output.  It is
+# commented because it doesn't cope with --amend or with squashed
+# commits.
+#
+# The third example adds a Signed-off-by line to the message, that can
+# still be edited.  This is rarely a good idea.
+
+case "$2,$3" in
+  merge,)
+    /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;;
+
+# ,|template,)
+#   /usr/bin/perl -i.bak -pe '
+#      print "\n" . `git diff --cached --name-status -r`
+#	 if /^#/ && $first++ == 0' "$1" ;;
+
+  *) ;;
+esac
+
+# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
+# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
diff --git a/tests/resources/issue_3020/.gitted/hooks/update.sample b/tests/resources/issue_3020/.gitted/hooks/update.sample
new file mode 100644
index 0000000..d847583
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/hooks/update.sample
@@ -0,0 +1,128 @@
+#!/bin/sh
+#
+# An example hook script to blocks unannotated tags from entering.
+# Called by "git receive-pack" with arguments: refname sha1-old sha1-new
+#
+# To enable this hook, rename this file to "update".
+#
+# Config
+# ------
+# hooks.allowunannotated
+#   This boolean sets whether unannotated tags will be allowed into the
+#   repository.  By default they won't be.
+# hooks.allowdeletetag
+#   This boolean sets whether deleting tags will be allowed in the
+#   repository.  By default they won't be.
+# hooks.allowmodifytag
+#   This boolean sets whether a tag may be modified after creation. By default
+#   it won't be.
+# hooks.allowdeletebranch
+#   This boolean sets whether deleting branches will be allowed in the
+#   repository.  By default they won't be.
+# hooks.denycreatebranch
+#   This boolean sets whether remotely creating branches will be denied
+#   in the repository.  By default this is allowed.
+#
+
+# --- Command line
+refname="$1"
+oldrev="$2"
+newrev="$3"
+
+# --- Safety check
+if [ -z "$GIT_DIR" ]; then
+	echo "Don't run this script from the command line." >&2
+	echo " (if you want, you could supply GIT_DIR then run" >&2
+	echo "  $0 <ref> <oldrev> <newrev>)" >&2
+	exit 1
+fi
+
+if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
+	echo "usage: $0 <ref> <oldrev> <newrev>" >&2
+	exit 1
+fi
+
+# --- Config
+allowunannotated=$(git config --bool hooks.allowunannotated)
+allowdeletebranch=$(git config --bool hooks.allowdeletebranch)
+denycreatebranch=$(git config --bool hooks.denycreatebranch)
+allowdeletetag=$(git config --bool hooks.allowdeletetag)
+allowmodifytag=$(git config --bool hooks.allowmodifytag)
+
+# check for no description
+projectdesc=$(sed -e '1q' "$GIT_DIR/description")
+case "$projectdesc" in
+"Unnamed repository"* | "")
+	echo "*** Project description file hasn't been set" >&2
+	exit 1
+	;;
+esac
+
+# --- Check types
+# if $newrev is 0000...0000, it's a commit to delete a ref.
+zero="0000000000000000000000000000000000000000"
+if [ "$newrev" = "$zero" ]; then
+	newrev_type=delete
+else
+	newrev_type=$(git cat-file -t $newrev)
+fi
+
+case "$refname","$newrev_type" in
+	refs/tags/*,commit)
+		# un-annotated tag
+		short_refname=${refname##refs/tags/}
+		if [ "$allowunannotated" != "true" ]; then
+			echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
+			echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
+			exit 1
+		fi
+		;;
+	refs/tags/*,delete)
+		# delete tag
+		if [ "$allowdeletetag" != "true" ]; then
+			echo "*** Deleting a tag is not allowed in this repository" >&2
+			exit 1
+		fi
+		;;
+	refs/tags/*,tag)
+		# annotated tag
+		if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1
+		then
+			echo "*** Tag '$refname' already exists." >&2
+			echo "*** Modifying a tag is not allowed in this repository." >&2
+			exit 1
+		fi
+		;;
+	refs/heads/*,commit)
+		# branch
+		if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then
+			echo "*** Creating a branch is not allowed in this repository" >&2
+			exit 1
+		fi
+		;;
+	refs/heads/*,delete)
+		# delete branch
+		if [ "$allowdeletebranch" != "true" ]; then
+			echo "*** Deleting a branch is not allowed in this repository" >&2
+			exit 1
+		fi
+		;;
+	refs/remotes/*,commit)
+		# tracking branch
+		;;
+	refs/remotes/*,delete)
+		# delete tracking branch
+		if [ "$allowdeletebranch" != "true" ]; then
+			echo "*** Deleting a tracking branch is not allowed in this repository" >&2
+			exit 1
+		fi
+		;;
+	*)
+		# Anything else (is there anything else?)
+		echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
+		exit 1
+		;;
+esac
+
+# --- Finished
+exit 0
diff --git a/tests/resources/issue_3020/.gitted/index b/tests/resources/issue_3020/.gitted/index
new file mode 100644
index 0000000..a66a2de
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/index
Binary files differ
diff --git a/tests/resources/issue_3020/.gitted/info/exclude b/tests/resources/issue_3020/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/issue_3020/.gitted/logs/HEAD b/tests/resources/issue_3020/.gitted/logs/HEAD
new file mode 100644
index 0000000..9ea36cf
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 77e8039fba38e532bea5fba5f4f3be9940ea521b Brendan Forster <brendan@github.com> 1427606421 +0200	clone: from https://github.com/macote/HashCheck.git
diff --git a/tests/resources/issue_3020/.gitted/logs/refs/heads/master b/tests/resources/issue_3020/.gitted/logs/refs/heads/master
new file mode 100644
index 0000000..9ea36cf
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 77e8039fba38e532bea5fba5f4f3be9940ea521b Brendan Forster <brendan@github.com> 1427606421 +0200	clone: from https://github.com/macote/HashCheck.git
diff --git a/tests/resources/issue_3020/.gitted/logs/refs/remotes/origin/HEAD b/tests/resources/issue_3020/.gitted/logs/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..9ea36cf
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 77e8039fba38e532bea5fba5f4f3be9940ea521b Brendan Forster <brendan@github.com> 1427606421 +0200	clone: from https://github.com/macote/HashCheck.git
diff --git a/tests/resources/issue_3020/.gitted/objects/07/998b2304ff33b23683b63135dc36ba9f68ac57 b/tests/resources/issue_3020/.gitted/objects/07/998b2304ff33b23683b63135dc36ba9f68ac57
new file mode 100644
index 0000000..0d5592b
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/objects/07/998b2304ff33b23683b63135dc36ba9f68ac57
@@ -0,0 +1,2 @@
+xŽÁŽÂ0D÷ܯð±ØI“ÖÕ
+írà?âÆ´jƒBúÿd—?À·yšϜ—åVÁþ£Um"㝸)0©F҈gމD˜YTqÝ=]+ƒŽh9I°£:kDƒkÂ¥>YQæ0$]Øê58µT+œsyT-ð%/ð}¹Õë&Ÿs^Ž@}ÛCh½‡¶ëm+ÿüï廟­æýEW-¡j	óïv‡Wíh†ÉúÉø	ÿOˆO¢V¦
\ No newline at end of file
diff --git a/tests/resources/issue_3020/.gitted/objects/3a/ea65954a4a13370a1f47df920d24cdef74cb49 b/tests/resources/issue_3020/.gitted/objects/3a/ea65954a4a13370a1f47df920d24cdef74cb49
new file mode 100644
index 0000000..841afce
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/objects/3a/ea65954a4a13370a1f47df920d24cdef74cb49
Binary files differ
diff --git a/tests/resources/issue_3020/.gitted/objects/56/0e1db218db90db538be31f8d568465ac3362c2 b/tests/resources/issue_3020/.gitted/objects/56/0e1db218db90db538be31f8d568465ac3362c2
new file mode 100644
index 0000000..50cc0d2
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/objects/56/0e1db218db90db538be31f8d568465ac3362c2
@@ -0,0 +1,2 @@
+xŽAnÃ0{ö+x/Ґ’iKF´9ä¢E%A`+PäÿW…ž–îrç¼,÷
+f>jQg™…Rð¤I#Zœ}L$⽕Q¸{†¢k…qT‡Ö'	Ö)[#¸-œúdE½ï±C҅­ÞrssŰÂ%—WÕß²ƒŸë½Þ6ùšórê[>±M×hkùÿž¿ûÝj>\uÕªF0?¶'ì± =4Hè&ÄiÜÔ¢ÖV¦
\ No newline at end of file
diff --git a/tests/resources/issue_3020/.gitted/objects/76/7dea87ec830714d0199d84deca33aee304735e b/tests/resources/issue_3020/.gitted/objects/76/7dea87ec830714d0199d84deca33aee304735e
new file mode 100644
index 0000000..8ea38fa
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/objects/76/7dea87ec830714d0199d84deca33aee304735e
@@ -0,0 +1 @@
+xŽÁNÄ0D9÷+|G°vR7u…pà?âÆÙ]¡6«þ?YíàÛ<͌g-ÛvmàÂôÔªÌ>“›XyFŠBf‰,¡ÇUR&UQÓ <Übµ½A6£—¬ÑÏÆÞ©Eî‚󘽚Ȉ8Ò!íR*|õTŠ;|—ú۬›>ÀÇùÚ.‡¾®e{ûbžž±ßÐi_y÷ÿ/?|­¼œm·›%иþ7xÔ.€þäää0,양îÄ?£V§
\ No newline at end of file
diff --git a/tests/resources/issue_3020/.gitted/objects/83/f1265b5801a91eed1ed030c9df1bb999beb7b5 b/tests/resources/issue_3020/.gitted/objects/83/f1265b5801a91eed1ed030c9df1bb999beb7b5
new file mode 100644
index 0000000..f86b2cd
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/objects/83/f1265b5801a91eed1ed030c9df1bb999beb7b5
Binary files differ
diff --git a/tests/resources/issue_3020/.gitted/objects/ab/027b9968d0363df52e7b26ac68b3cd69ef4342 b/tests/resources/issue_3020/.gitted/objects/ab/027b9968d0363df52e7b26ac68b3cd69ef4342
new file mode 100644
index 0000000..6c9679f
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/objects/ab/027b9968d0363df52e7b26ac68b3cd69ef4342
Binary files differ
diff --git a/tests/resources/issue_3020/.gitted/objects/d8/91425b7207c6685fdd97ce26ffcb3c64577d82 b/tests/resources/issue_3020/.gitted/objects/d8/91425b7207c6685fdd97ce26ffcb3c64577d82
new file mode 100644
index 0000000..5d4f54e
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/objects/d8/91425b7207c6685fdd97ce26ffcb3c64577d82
Binary files differ
diff --git a/tests/resources/issue_3020/.gitted/objects/pack/pack-00716425456a3c9f631eae470013b68e941d6212.idx b/tests/resources/issue_3020/.gitted/objects/pack/pack-00716425456a3c9f631eae470013b68e941d6212.idx
new file mode 100644
index 0000000..4594f96
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/objects/pack/pack-00716425456a3c9f631eae470013b68e941d6212.idx
Binary files differ
diff --git a/tests/resources/issue_3020/.gitted/objects/pack/pack-00716425456a3c9f631eae470013b68e941d6212.pack b/tests/resources/issue_3020/.gitted/objects/pack/pack-00716425456a3c9f631eae470013b68e941d6212.pack
new file mode 100644
index 0000000..95b1765
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/objects/pack/pack-00716425456a3c9f631eae470013b68e941d6212.pack
Binary files differ
diff --git a/tests/resources/issue_3020/.gitted/packed-refs b/tests/resources/issue_3020/.gitted/packed-refs
new file mode 100644
index 0000000..8fa3d95
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled fully-peeled 
+77e8039fba38e532bea5fba5f4f3be9940ea521b refs/remotes/origin/master
diff --git a/tests/resources/issue_3020/.gitted/refs/heads/master b/tests/resources/issue_3020/.gitted/refs/heads/master
new file mode 100644
index 0000000..4d35026
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/refs/heads/master
@@ -0,0 +1 @@
+77e8039fba38e532bea5fba5f4f3be9940ea521b
diff --git a/tests/resources/issue_3020/.gitted/refs/remotes/origin/HEAD b/tests/resources/issue_3020/.gitted/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6efe28f
--- /dev/null
+++ b/tests/resources/issue_3020/.gitted/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/issue_3020/.project b/tests/resources/issue_3020/.project
new file mode 100644
index 0000000..e3668ca
--- /dev/null
+++ b/tests/resources/issue_3020/.project
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<projectDescription>

+	<name>HashCheck</name>

+	<comment></comment>

+	<projects>

+	</projects>

+	<buildSpec>

+		<buildCommand>

+			<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>

+			<triggers>clean,full,incremental,</triggers>

+			<arguments>

+			</arguments>

+		</buildCommand>

+		<buildCommand>

+			<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>

+			<triggers>full,incremental,</triggers>

+			<arguments>

+			</arguments>

+		</buildCommand>

+	</buildSpec>

+	<natures>

+		<nature>org.eclipse.cdt.core.cnature</nature>

+		<nature>org.eclipse.cdt.core.ccnature</nature>

+		<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>

+		<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>

+	</natures>

+	<filteredResources>

+		<filter>

+			<id>1417569914325</id>

+			<name></name>

+			<type>6</type>

+			<matcher>

+				<id>org.eclipse.ui.ide.multiFilter</id>

+				<arguments>1.0-name-matches-false-false-*.sln</arguments>

+			</matcher>

+		</filter>

+		<filter>

+			<id>1417569914328</id>

+			<name></name>

+			<type>6</type>

+			<matcher>

+				<id>org.eclipse.ui.ide.multiFilter</id>

+				<arguments>1.0-name-matches-false-false-*.vcxproj*</arguments>

+			</matcher>

+		</filter>

+		<filter>

+			<id>1417569914330</id>

+			<name></name>

+			<type>6</type>

+			<matcher>

+				<id>org.eclipse.ui.ide.multiFilter</id>

+				<arguments>1.0-name-matches-false-false-*.*sdf</arguments>

+			</matcher>

+		</filter>

+		<filter>

+			<id>1417569914333</id>

+			<name></name>

+			<type>6</type>

+			<matcher>

+				<id>org.eclipse.ui.ide.multiFilter</id>

+				<arguments>1.0-name-matches-false-false-*.suo</arguments>

+			</matcher>

+		</filter>

+		<filter>

+			<id>1417569914338</id>

+			<name></name>

+			<type>6</type>

+			<matcher>

+				<id>org.eclipse.ui.ide.multiFilter</id>

+				<arguments>1.0-name-matches-false-false-checksum.*</arguments>

+			</matcher>

+		</filter>

+	</filteredResources>

+</projectDescription>

diff --git a/tests/resources/issue_3020/.settings/language.settings.xml b/tests/resources/issue_3020/.settings/language.settings.xml
new file mode 100644
index 0000000..534786b
--- /dev/null
+++ b/tests/resources/issue_3020/.settings/language.settings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>

+<project>

+	<configuration id="cdt.managedbuild.config.gnu.mingw.exe.debug.1091074575" name="Debug">

+		<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">

+			<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>

+			<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>

+			<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>

+			<provider class="org.eclipse.cdt.managedbuilder.internal.language.settings.providers.GCCBuiltinSpecsDetectorMinGW" console="false" env-hash="-682947582912623951" id="org.eclipse.cdt.managedbuilder.core.GCCBuiltinSpecsDetectorMinGW" keep-relative-paths="false" name="CDT GCC Built-in Compiler Settings MinGW" parameter="${COMMAND} ${FLAGS} -E -P -v -dD -std=c++11 &quot;${INPUTS}&quot;">

+				<language-scope id="org.eclipse.cdt.core.gcc"/>

+				<language-scope id="org.eclipse.cdt.core.g++"/>

+			</provider>

+		</extension>

+	</configuration>

+	<configuration id="cdt.managedbuild.config.gnu.mingw.exe.release.107885674" name="Release">

+		<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">

+			<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>

+			<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>

+			<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>

+			<provider class="org.eclipse.cdt.managedbuilder.internal.language.settings.providers.GCCBuiltinSpecsDetectorMinGW" console="false" env-hash="-682947582912623951" id="org.eclipse.cdt.managedbuilder.core.GCCBuiltinSpecsDetectorMinGW" keep-relative-paths="false" name="CDT GCC Built-in Compiler Settings MinGW" parameter="${COMMAND} ${FLAGS} -E -P -v -dD -std=c++11 &quot;${INPUTS}&quot;">

+				<language-scope id="org.eclipse.cdt.core.gcc"/>

+				<language-scope id="org.eclipse.cdt.core.g++"/>

+			</provider>

+		</extension>

+	</configuration>

+</project>

diff --git a/tests/resources/issue_3020/.settings/org.eclipse.cdt.managedbuilder.core.prefs b/tests/resources/issue_3020/.settings/org.eclipse.cdt.managedbuilder.core.prefs
new file mode 100644
index 0000000..b1e46bc
--- /dev/null
+++ b/tests/resources/issue_3020/.settings/org.eclipse.cdt.managedbuilder.core.prefs
@@ -0,0 +1,25 @@
+eclipse.preferences.version=1

+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.1091074575/CPATH/delimiter=;

+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.1091074575/CPATH/operation=remove

+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.1091074575/CPLUS_INCLUDE_PATH/delimiter=;

+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.1091074575/CPLUS_INCLUDE_PATH/operation=remove

+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.1091074575/C_INCLUDE_PATH/delimiter=;

+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.1091074575/C_INCLUDE_PATH/operation=remove

+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.1091074575/append=true

+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.1091074575/appendContributed=true

+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.release.107885674/CPATH/delimiter=;

+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.release.107885674/CPATH/operation=remove

+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.release.107885674/CPLUS_INCLUDE_PATH/delimiter=;

+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.release.107885674/CPLUS_INCLUDE_PATH/operation=remove

+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.release.107885674/C_INCLUDE_PATH/delimiter=;

+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.release.107885674/C_INCLUDE_PATH/operation=remove

+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.release.107885674/append=true

+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.release.107885674/appendContributed=true

+environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.debug.1091074575/LIBRARY_PATH/delimiter=;

+environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.debug.1091074575/LIBRARY_PATH/operation=remove

+environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.debug.1091074575/append=true

+environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.debug.1091074575/appendContributed=true

+environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.release.107885674/LIBRARY_PATH/delimiter=;

+environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.release.107885674/LIBRARY_PATH/operation=remove

+environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.release.107885674/append=true

+environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.release.107885674/appendContributed=true

diff --git a/tests/resources/issue_3020/CRC32FileHash.cpp b/tests/resources/issue_3020/CRC32FileHash.cpp
new file mode 100644
index 0000000..c34a275
--- /dev/null
+++ b/tests/resources/issue_3020/CRC32FileHash.cpp
@@ -0,0 +1,100 @@
+/* Author: macote */

+

+#include "CRC32FileHash.h"

+

+const UINT32 CRC32FileHash::kCRC32Table[] = {

+	0x000000000L, 0x077073096L, 0x0EE0E612CL, 0x0990951BAL,

+	0x0076DC419L, 0x0706AF48FL, 0x0E963A535L, 0x09E6495A3L,

+	0x00EDB8832L, 0x079DCB8A4L, 0x0E0D5E91EL, 0x097D2D988L,

+	0x009B64C2BL, 0x07EB17CBDL, 0x0E7B82D07L, 0x090BF1D91L,

+	0x01DB71064L, 0x06AB020F2L, 0x0F3B97148L, 0x084BE41DEL,

+	0x01ADAD47DL, 0x06DDDE4EBL, 0x0F4D4B551L, 0x083D385C7L,

+	0x0136C9856L, 0x0646BA8C0L, 0x0FD62F97AL, 0x08A65C9ECL,

+	0x014015C4FL, 0x063066CD9L, 0x0FA0F3D63L, 0x08D080DF5L,

+	0x03B6E20C8L, 0x04C69105EL, 0x0D56041E4L, 0x0A2677172L,

+	0x03C03E4D1L, 0x04B04D447L, 0x0D20D85FDL, 0x0A50AB56BL,

+	0x035B5A8FAL, 0x042B2986CL, 0x0DBBBC9D6L, 0x0ACBCF940L,

+	0x032D86CE3L, 0x045DF5C75L, 0x0DCD60DCFL, 0x0ABD13D59L,

+	0x026D930ACL, 0x051DE003AL, 0x0C8D75180L, 0x0BFD06116L,

+	0x021B4F4B5L, 0x056B3C423L, 0x0CFBA9599L, 0x0B8BDA50FL,

+	0x02802B89EL, 0x05F058808L, 0x0C60CD9B2L, 0x0B10BE924L,

+	0x02F6F7C87L, 0x058684C11L, 0x0C1611DABL, 0x0B6662D3DL,

+	0x076DC4190L, 0x001DB7106L, 0x098D220BCL, 0x0EFD5102AL,

+	0x071B18589L, 0x006B6B51FL, 0x09FBFE4A5L, 0x0E8B8D433L,

+	0x07807C9A2L, 0x00F00F934L, 0x09609A88EL, 0x0E10E9818L,

+	0x07F6A0DBBL, 0x0086D3D2DL, 0x091646C97L, 0x0E6635C01L,

+	0x06B6B51F4L, 0x01C6C6162L, 0x0856530D8L, 0x0F262004EL,

+	0x06C0695EDL, 0x01B01A57BL, 0x08208F4C1L, 0x0F50FC457L,

+	0x065B0D9C6L, 0x012B7E950L, 0x08BBEB8EAL, 0x0FCB9887CL,

+	0x062DD1DDFL, 0x015DA2D49L, 0x08CD37CF3L, 0x0FBD44C65L,

+	0x04DB26158L, 0x03AB551CEL, 0x0A3BC0074L, 0x0D4BB30E2L,

+	0x04ADFA541L, 0x03DD895D7L, 0x0A4D1C46DL, 0x0D3D6F4FBL,

+	0x04369E96AL, 0x0346ED9FCL, 0x0AD678846L, 0x0DA60B8D0L,

+	0x044042D73L, 0x033031DE5L, 0x0AA0A4C5FL, 0x0DD0D7CC9L,

+	0x05005713CL, 0x0270241AAL, 0x0BE0B1010L, 0x0C90C2086L,

+	0x05768B525L, 0x0206F85B3L, 0x0B966D409L, 0x0CE61E49FL,

+	0x05EDEF90EL, 0x029D9C998L, 0x0B0D09822L, 0x0C7D7A8B4L,

+	0x059B33D17L, 0x02EB40D81L, 0x0B7BD5C3BL, 0x0C0BA6CADL,

+	0x0EDB88320L, 0x09ABFB3B6L, 0x003B6E20CL, 0x074B1D29AL,

+	0x0EAD54739L, 0x09DD277AFL, 0x004DB2615L, 0x073DC1683L,

+	0x0E3630B12L, 0x094643B84L, 0x00D6D6A3EL, 0x07A6A5AA8L,

+	0x0E40ECF0BL, 0x09309FF9DL, 0x00A00AE27L, 0x07D079EB1L,

+	0x0F00F9344L, 0x08708A3D2L, 0x01E01F268L, 0x06906C2FEL,

+	0x0F762575DL, 0x0806567CBL, 0x0196C3671L, 0x06E6B06E7L,

+	0x0FED41B76L, 0x089D32BE0L, 0x010DA7A5AL, 0x067DD4ACCL,

+	0x0F9B9DF6FL, 0x08EBEEFF9L, 0x017B7BE43L, 0x060B08ED5L,

+	0x0D6D6A3E8L, 0x0A1D1937EL, 0x038D8C2C4L, 0x04FDFF252L,

+	0x0D1BB67F1L, 0x0A6BC5767L, 0x03FB506DDL, 0x048B2364BL,

+	0x0D80D2BDAL, 0x0AF0A1B4CL, 0x036034AF6L, 0x041047A60L,

+	0x0DF60EFC3L, 0x0A867DF55L, 0x0316E8EEFL, 0x04669BE79L,

+	0x0CB61B38CL, 0x0BC66831AL, 0x0256FD2A0L, 0x05268E236L,

+	0x0CC0C7795L, 0x0BB0B4703L, 0x0220216B9L, 0x05505262FL,

+	0x0C5BA3BBEL, 0x0B2BD0B28L, 0x02BB45A92L, 0x05CB36A04L,

+	0x0C2D7FFA7L, 0x0B5D0CF31L, 0x02CD99E8BL, 0x05BDEAE1DL,

+	0x09B64C2B0L, 0x0EC63F226L, 0x0756AA39CL, 0x0026D930AL,

+	0x09C0906A9L, 0x0EB0E363FL, 0x072076785L, 0x005005713L,

+	0x095BF4A82L, 0x0E2B87A14L, 0x07BB12BAEL, 0x00CB61B38L,

+	0x092D28E9BL, 0x0E5D5BE0DL, 0x07CDCEFB7L, 0x00BDBDF21L,

+	0x086D3D2D4L, 0x0F1D4E242L, 0x068DDB3F8L, 0x01FDA836EL,

+	0x081BE16CDL, 0x0F6B9265BL, 0x06FB077E1L, 0x018B74777L,

+	0x088085AE6L, 0x0FF0F6A70L, 0x066063BCAL, 0x011010B5CL,

+	0x08F659EFFL, 0x0F862AE69L, 0x0616BFFD3L, 0x0166CCF45L,

+	0x0A00AE278L, 0x0D70DD2EEL, 0x04E048354L, 0x03903B3C2L,

+	0x0A7672661L, 0x0D06016F7L, 0x04969474DL, 0x03E6E77DBL,

+	0x0AED16A4AL, 0x0D9D65ADCL, 0x040DF0B66L, 0x037D83BF0L,

+	0x0A9BCAE53L, 0x0DEBB9EC5L, 0x047B2CF7FL, 0x030B5FFE9L,

+	0x0BDBDF21CL, 0x0CABAC28AL, 0x053B39330L, 0x024B4A3A6L,

+	0x0BAD03605L, 0x0CDD70693L, 0x054DE5729L, 0x023D967BFL,

+	0x0B3667A2EL, 0x0C4614AB8L, 0x05D681B02L, 0x02A6F2B94L,

+	0x0B40BBE37L, 0x0C30C8EA1L, 0x05A05DF1BL, 0x02D02EF8DL

+};

+

+void CRC32FileHash::Initialize()

+{

+	hash_ = 0;

+	hash_ = ~hash_;

+}

+

+void CRC32FileHash::Update(const UINT32 bytecount)

+{

+	PBYTE buffer = buffer_;

+	UINT32 bytesleft = bytecount;

+	while (bytesleft > 0)

+	{

+		hash_ = kCRC32Table[(hash_ ^ *buffer) & 0xFF] ^ (hash_ >> 8);

+		++buffer;

+		--bytesleft;

+	}

+}

+

+void CRC32FileHash::Finalize()

+{

+	hash_ = ~hash_;

+}

+

+void CRC32FileHash::ConvertHashToDigestString()

+{

+	std::wstringstream wss;

+	wss << std::hex << std::setw(8) << std::setfill(L'0') << std::uppercase << hash_;

+	digest_.append(wss.str());

+}

diff --git a/tests/resources/issue_3020/CRC32FileHash.h b/tests/resources/issue_3020/CRC32FileHash.h
new file mode 100644
index 0000000..37fe12c
--- /dev/null
+++ b/tests/resources/issue_3020/CRC32FileHash.h
@@ -0,0 +1,30 @@
+/* Author: macote */

+

+#ifndef CRC32FILEHASH_H_

+#define CRC32FILEHASH_H_

+

+#include "FileHash.h"

+#include <iomanip>

+#include <sstream>

+#include <string>

+#include <Windows.h>

+

+class CRC32FileHash : public FileHash

+{

+public:

+#if _MSC_VER < 1900

+	CRC32FileHash(const std::wstring& filepath, const DWORD buffersize) : FileHash(filepath, buffersize) { };

+	CRC32FileHash(const std::wstring& filepath) : FileHash(filepath) { };

+#else

+	using FileHash::FileHash;

+#endif

+private:

+	void Initialize();

+	void Update(const UINT32 bytecount);

+	void Finalize();

+	void ConvertHashToDigestString();

+	const static UINT32 kCRC32Table[];

+	UINT32 hash_;

+};

+

+#endif /* CRC32FILEHASH_H_ */

diff --git a/tests/resources/issue_3020/FileHash.cpp b/tests/resources/issue_3020/FileHash.cpp
new file mode 100644
index 0000000..9f6d70c
--- /dev/null
+++ b/tests/resources/issue_3020/FileHash.cpp
@@ -0,0 +1,49 @@
+/* Author: macote */

+

+#include "FileHash.h"

+

+void FileHash::AllocateBuffer()

+{

+	buffer_ = (PBYTE)VirtualAlloc(NULL, buffersize_, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

+}

+

+void FileHash::FreeBuffer()

+{

+	if (buffer_ != NULL)

+	{

+		VirtualFree(buffer_, 0, MEM_RELEASE);

+	}

+}

+

+void FileHash::Compute()

+{

+	Initialize();

+	DWORD bytesread = 0;

+	FileHashBytesProcessedEventArgs fhbpea;

+	fhbpea.bytesprocessed.QuadPart = 0;

+	DWORD runningnotificationblocksize = 0;

+	do

+	{

+		bytesread = filestream_.Read(buffer_, buffersize_);

+		if (bytesread > 0)

+		{

+			Update(bytesread);

+		}

+		if (bytesprocessedevent_ != nullptr)

+		{

+			fhbpea.bytesprocessed.QuadPart += bytesread;

+			runningnotificationblocksize += bytesread;

+			if (runningnotificationblocksize >= bytesprocessednotificationblocksize_ || bytesread == 0)

+			{

+				if (bytesread > 0)

+				{

+					runningnotificationblocksize -= bytesprocessednotificationblocksize_;

+				}

+				bytesprocessedevent_(fhbpea);

+			}

+		}

+	}

+	while (bytesread > 0);

+	Finalize();

+	ConvertHashToDigestString();

+}

diff --git a/tests/resources/issue_3020/FileHash.h b/tests/resources/issue_3020/FileHash.h
new file mode 100644
index 0000000..4867dd9
--- /dev/null
+++ b/tests/resources/issue_3020/FileHash.h
@@ -0,0 +1,61 @@
+/* Author: macote */

+

+#ifndef FILEHASH_H_

+#define FILEHASH_H_

+

+#include "FileStream.h"

+#include <string>

+#include <functional>

+#include <Windows.h>

+

+struct FileHashBytesProcessedEventArgs

+{

+	LARGE_INTEGER bytesprocessed;

+};

+

+class FileHash

+{

+public:

+	static const DWORD kDefaultBufferSize = 32768;

+	static const DWORD kDefaultBytesProcessedNotificationBlockSize = 1048576;

+public:

+	FileHash(const std::wstring& filepath) : FileHash(filepath, kDefaultBufferSize) { };

+	FileHash(const std::wstring& filepath, const DWORD buffersize) 

+		: buffersize_(buffersize), filestream_(FileStream(filepath, FileStream::Mode::OpenNoBuffering, buffersize))

+	{

+		bytesprocessedevent_ = nullptr;

+		AllocateBuffer();

+	}

+	virtual ~FileHash()

+	{

+		FreeBuffer();

+	};

+	void Compute();

+	std::wstring digest() const { return digest_; }

+	void SetBytesProcessedEventHandler(std::function<void(FileHashBytesProcessedEventArgs)> handler)

+	{

+		SetBytesProcessedEventHandler(handler, kDefaultBytesProcessedNotificationBlockSize);

+	}

+	void SetBytesProcessedEventHandler(std::function<void(FileHashBytesProcessedEventArgs)> handler, 

+		const DWORD bytesprocessednotificationblocksize)

+	{

+		bytesprocessedevent_ = handler;

+		bytesprocessednotificationblocksize_ = bytesprocessednotificationblocksize;

+	}

+protected:

+	virtual void Initialize() = 0;

+	virtual void Update(const UINT32 bytes) = 0;

+	virtual void Finalize() = 0;

+	virtual void ConvertHashToDigestString() = 0;

+	PBYTE buffer_ = NULL;

+	std::wstring digest_;

+private:

+	void AllocateBuffer();

+	void FreeBuffer();

+	DWORD buffersize_;

+	FileStream filestream_;

+	DWORD bytesprocessednotificationblocksize_;

+	std::function<void(FileHashBytesProcessedEventArgs)> bytesprocessedevent_;

+};

+

+#endif /* FILEHASH_H_ */

diff --git a/tests/resources/issue_3020/FileHashFactory.h b/tests/resources/issue_3020/FileHashFactory.h
new file mode 100644
index 0000000..90bd27b
--- /dev/null
+++ b/tests/resources/issue_3020/FileHashFactory.h
@@ -0,0 +1,42 @@
+/* Author: macote */

+

+#ifndef FILEHASHFACTORY_H_

+#define FILEHASHFACTORY_H_

+

+#include "FileHash.h"

+#include "HashType.h"

+#include "CRC32FileHash.h"

+#include "MD5FileHash.h"

+#include "SHA1FileHash.h"

+#include <string>

+#include <memory>

+#include <Windows.h>

+

+class FileHashFactory

+{

+public:

+	static std::unique_ptr<FileHash> Create(HashType hashtype, const std::wstring& filepath)

+	{ 

+		if (hashtype == HashType::SHA1)

+		{

+			//return std::make_unique<SHA1FileHash>(filepath);

+			return std::unique_ptr<SHA1FileHash>(new SHA1FileHash(filepath));

+		}

+		else if (hashtype == HashType::MD5)

+		{

+			//return std::make_unique<MD5FileHash>(filepath);

+			return std::unique_ptr<MD5FileHash>(new MD5FileHash(filepath));

+		}

+		else if (hashtype == HashType::CRC32)

+		{

+			//return std::make_unique<CRC32FileHash>(filepath);

+			return std::unique_ptr<CRC32FileHash>(new CRC32FileHash(filepath));

+		}

+		else

+		{

+			throw std::runtime_error("FileHashFactory.Create(): selected hash type is not supported.");

+		}

+	};

+};

+

+#endif /* FILEHASHFACTORY_H_ */

diff --git a/tests/resources/issue_3020/FileStream.cpp b/tests/resources/issue_3020/FileStream.cpp
new file mode 100644
index 0000000..a92e09a
--- /dev/null
+++ b/tests/resources/issue_3020/FileStream.cpp
@@ -0,0 +1,184 @@
+/* Author: macote */

+/* Portions of this code was inspired by dotnet/corefx's Win32FileStream.cs */

+/*

+

+The MIT License (MIT)

+

+Copyright (c) Microsoft Corporation

+

+Permission is hereby granted, free of charge, to any person obtaining a copy

+of this software and associated documentation files (the "Software"), to deal

+in the Software without restriction, including without limitation the rights

+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell

+copies of the Software, and to permit persons to whom the Software is

+furnished to do so, subject to the following conditions:

+

+The above copyright notice and this permission notice shall be included in all

+copies or substantial portions of the Software.

+

+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR

+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,

+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE

+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER

+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,

+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE

+SOFTWARE.

+

+*/

+

+#include "FileStream.h"

+

+void FileStream::AllocateBuffer()

+{

+	buffer_ = (PBYTE)VirtualAlloc(NULL, buffersize_, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

+}

+

+void FileStream::FreeBuffer()

+{

+	if (buffer_ != NULL)

+	{

+		VirtualFree(buffer_, 0, MEM_RELEASE);

+	}

+}

+

+void FileStream::OpenFile()

+{

+	DWORD desiredaccess = (mode_ >= Mode::Create) ? GENERIC_WRITE : GENERIC_READ;

+	DWORD createdisposition;

+	switch (mode_)

+	{

+	case FileStream::Mode::Create:

+		createdisposition = CREATE_NEW;

+		break;

+	case FileStream::Mode::Truncate:

+		createdisposition = CREATE_ALWAYS;

+		break;

+	default:

+		createdisposition = OPEN_EXISTING;

+		break;

+	}

+	DWORD flagsandattributes = FILE_ATTRIBUTE_NORMAL;

+	if (mode_ == Mode::OpenNoBuffering)

+	{

+		flagsandattributes |= FILE_FLAG_NO_BUFFERING;

+	}

+	filehandle_ = CreateFileW(filepath_.c_str(), desiredaccess, FILE_SHARE_READ,

+		NULL, createdisposition, flagsandattributes, NULL);

+	if (filehandle_ == INVALID_HANDLE_VALUE)

+	{

+		std::stringstream ss;

+		ss << "FileStream.Open(CreateFileW()) failed with error ";

+		ss << "0x" << std::hex << std::setw(8) << std::setfill('0') << std::uppercase;

+		ss << GetLastError();

+		throw std::runtime_error(ss.str());

+	}

+}

+

+DWORD FileStream::Read(PBYTE buffer, DWORD offset, DWORD count)

+{

+	DWORD bytesread = 0;

+	ReadFile(filehandle_, buffer + offset, count, &bytesread, NULL);

+	return bytesread;

+}

+

+DWORD FileStream::Write(PBYTE buffer, DWORD offset, DWORD count)

+{

+	DWORD byteswritten = 0;

+	WriteFile(filehandle_, buffer + offset, count, &byteswritten, NULL);

+	return byteswritten;

+}

+

+void FileStream::FlushWrite()

+{

+	Write(buffer_, 0, writeindex_);

+	writeindex_ = 0;

+}

+

+void FileStream::CloseFile()

+{

+	if (filehandle_ != INVALID_HANDLE_VALUE)

+	{

+		CloseHandle(filehandle_);

+		filehandle_ = INVALID_HANDLE_VALUE;

+	}

+}

+

+DWORD FileStream::Read(PBYTE buffer, DWORD count)

+{

+	DWORD bufferbytes = readlength_ - readindex_;

+	BOOL eof = FALSE;

+	if (bufferbytes == 0)

+	{

+		DWORD bytesread;

+		if (count >= buffersize_)

+		{

+			bytesread = Read(buffer, 0, count);

+			readindex_ = readlength_ = 0;

+			return bytesread;

+		}

+		bytesread = Read(buffer_, 0, buffersize_);

+		if (bytesread == 0) return 0;

+		readindex_ = 0;

+		readlength_ = bufferbytes = bytesread;

+		eof = bytesread < buffersize_;

+	}

+	if (bufferbytes > count)

+	{

+		bufferbytes = count;

+	}

+	CopyMemory(buffer, buffer_ + readindex_, bufferbytes);

+	readindex_ += bufferbytes;

+	if (bufferbytes < count && !eof)

+	{

+		DWORD bytesread = Read(buffer, bufferbytes, count - bufferbytes);

+		bufferbytes += bytesread;

+		readindex_ = readlength_ = 0;

+	}

+	return bufferbytes;

+}

+

+void FileStream::Write(PBYTE buffer, DWORD count)

+{

+	DWORD bufferindex = 0;

+	if (writeindex_ > 0)

+	{

+		DWORD bufferbytes = buffersize_ - writeindex_;

+		if (bufferbytes > 0)

+		{

+			if (bufferbytes > count)

+			{

+				bufferbytes = count;

+			}

+			CopyMemory(buffer_ + writeindex_, buffer, bufferbytes);

+			writeindex_ += bufferbytes;

+			if (bufferbytes == count) return;

+			bufferindex = bufferbytes;

+			count -= bufferbytes;

+		}

+		Write(buffer_, 0, writeindex_);

+		writeindex_ = 0;

+	}

+	if (count >= buffersize_)

+	{

+		Write(buffer, 0, count);

+	}

+	else if (count > 0)

+	{

+		CopyMemory(buffer_ + writeindex_, buffer + bufferindex, count);

+		writeindex_ = count;

+	}

+}

+

+void FileStream::Flush()

+{

+	if (writeindex_ > 0)

+	{

+		FlushWrite();

+	}

+}

+

+void FileStream::Close()

+{

+	Flush();

+	CloseFile();

+}

diff --git a/tests/resources/issue_3020/FileStream.h b/tests/resources/issue_3020/FileStream.h
new file mode 100644
index 0000000..baf8c29
--- /dev/null
+++ b/tests/resources/issue_3020/FileStream.h
@@ -0,0 +1,61 @@
+/* Author: macote */

+

+#ifndef FILESTREAM_H_

+#define FILESTREAM_H_

+

+#include <iomanip>

+#include <sstream>

+#include <stdexcept>

+#include <string>

+#include <Windows.h>

+

+class FileStream

+{

+public:

+	static const DWORD kDefaultBufferSize = 32768;

+public:

+	enum class Mode

+	{

+		Open,

+		OpenNoBuffering,

+		Create,

+		Truncate,

+		Append

+	};

+	FileStream(const std::wstring& filepath, Mode mode) : FileStream(filepath, mode, kDefaultBufferSize) { };

+	FileStream(const std::wstring& filepath, Mode mode, const DWORD buffersize) : filepath_(filepath), mode_(mode), buffersize_(buffersize)

+	{

+		AllocateBuffer();

+		OpenFile();

+	};

+	virtual ~FileStream()

+	{

+		Flush();

+		CloseFile();

+		FreeBuffer();

+	};

+	DWORD Read(PBYTE buffer, DWORD count);

+	void Write(PBYTE buffer, DWORD count);

+	void Flush();

+	void Close();

+	DWORD lasterror() const { return lasterror_; }

+private:

+	void AllocateBuffer();

+	void OpenFile();

+	DWORD Read(PBYTE buffer, DWORD offset, DWORD count);

+	DWORD Write(PBYTE buffer, DWORD offset, DWORD count);

+	void FlushWrite();

+	void CloseFile();

+	void FreeBuffer();

+	DWORD readindex_ = 0;

+	DWORD readlength_ = 0;

+	DWORD writeindex_ = 0;

+	PBYTE buffer_ = NULL;

+	const std::wstring filepath_;

+	Mode mode_;

+	const DWORD buffersize_;

+	HANDLE filehandle_ = NULL;

+	DWORD lasterror_ = 0;

+};

+

+#endif /* FILESTREAM_H_ */

diff --git a/tests/resources/issue_3020/FileTree.cpp b/tests/resources/issue_3020/FileTree.cpp
new file mode 100644
index 0000000..85e5021
--- /dev/null
+++ b/tests/resources/issue_3020/FileTree.cpp
@@ -0,0 +1,31 @@
+/* Author: macote */

+

+#include "FileTree.h"

+

+void FileTree::ProcessTree(const std::wstring& path) const 

+{

+	WIN32_FIND_DATAW findfiledata;

+	HANDLE hFind;

+	std::wstring pattern = path + L"*";

+	hFind = FindFirstFileW(pattern.c_str(), &findfiledata);

+	if (hFind != INVALID_HANDLE_VALUE)

+	{

+		do 

+		{

+			if (findfiledata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) 

+			{

+				if (lstrcmpW(findfiledata.cFileName, L".") != 0 && lstrcmpW(findfiledata.cFileName, L"..") != 0) 

+				{

+					std::wstring currentpath(path + findfiledata.cFileName + L"\\");

+					ProcessTree(currentpath);

+				}

+			}

+			else

+			{

+				std::wstring currentfile(path + findfiledata.cFileName);

+				fileaction_.ProcessFile(currentfile);

+			}

+		} while (FindNextFileW(hFind, &findfiledata));

+		FindClose(hFind);

+	}

+}

diff --git a/tests/resources/issue_3020/FileTree.h b/tests/resources/issue_3020/FileTree.h
new file mode 100644
index 0000000..48b7781
--- /dev/null
+++ b/tests/resources/issue_3020/FileTree.h
@@ -0,0 +1,30 @@
+/* Author: macote */

+

+#ifndef FILETREE_H_

+#define FILETREE_H_

+

+#include <string>

+#include <Windows.h>

+

+class IFileTreeAction

+{

+public:

+	virtual ~IFileTreeAction() { }

+	virtual void ProcessFile(const std::wstring& filepath) = 0;

+};

+

+class FileTree

+{

+public:

+	FileTree(const std::wstring basepath, IFileTreeAction& fileaction) : basepath_(basepath), fileaction_(fileaction) { };

+	void Process() const 

+	{

+		ProcessTree(basepath_);

+	}

+private:

+	void ProcessTree(const std::wstring& path) const;

+	const std::wstring basepath_;

+	IFileTreeAction& fileaction_;

+};

+

+#endif /* FILETREE_H_ */

diff --git a/tests/resources/issue_3020/HashCheck.cpp b/tests/resources/issue_3020/HashCheck.cpp
new file mode 100644
index 0000000..9edc029
--- /dev/null
+++ b/tests/resources/issue_3020/HashCheck.cpp
@@ -0,0 +1,259 @@
+/* Author: macote */

+

+#include "HashCheck.h"

+

+LPCWSTR HashCheck::kHashFileBaseName = L"checksum";

+

+void HashCheck::Initialize()

+{

+	silent_ = checking_ = updating_ = skipcheck_ = FALSE;

+	hashtype_ = HashType::Undefined;

+	appfilename_ = GetAppFileName(args_[0].c_str());

+

+	args_.erase(args_.begin());

+	if (args_.size() > 0)

+	{

+		std::vector<std::wstring>::iterator it;

+		it = std::find(args_.begin(), args_.end(), L"-u");

+		if (it != args_.end())

+		{

+			updating_ = TRUE;

+			args_.erase(it);

+		}

+		it = std::find(args_.begin(), args_.end(), L"-sm");

+		if (it != args_.end())

+		{

+			skipcheck_ = TRUE;

+			args_.erase(it);

+		}

+		it = std::find(args_.begin(), args_.end(), L"-sha1");

+		if (it != args_.end())

+		{

+			hashtype_ = HashType::SHA1;

+			args_.erase(it);

+		}

+		it = std::find(args_.begin(), args_.end(), L"-md5");

+		if (it != args_.end())

+		{

+			hashtype_ = HashType::MD5;

+			args_.erase(it);

+		}

+		it = std::find(args_.begin(), args_.end(), L"-crc32");

+		if (it != args_.end())

+		{

+			hashtype_ = HashType::CRC32;

+			args_.erase(it);

+		}

+	}

+

+	WIN32_FIND_DATAW findfiledata;

+	HANDLE hFind;

+

+	if (args_.size() > 0)

+	{

+		std::wstring tmp(args_[0]);

+		if (*(tmp.end() - 1) != L'\\')

+		{

+			tmp += L'\\';

+		}

+		tmp += L"*";

+		hFind = FindFirstFileW(tmp.c_str(), &findfiledata);

+		if (hFind != INVALID_HANDLE_VALUE) {

+			FindClose(hFind);

+			basepath_ = args_[0] + L'\\';

+		}

+		else

+		{

+			silent_ = TRUE;

+		}

+	}

+

+	std::wstring baseFilename = kHashFileBaseName;

+	if (hashtype_ == HashType::SHA1)

+		hashfilename_ = baseFilename + L".sha1";

+	else if (hashtype_ == HashType::MD5)

+		hashfilename_ = baseFilename + L".md5";

+	else if (hashtype_ == HashType::CRC32)

+		hashfilename_ = baseFilename + L".crc32";

+	else

+	{

+		hashfilename_ = baseFilename + L".sha1";

+		hFind = FindFirstFileW(hashfilename_.c_str(), &findfiledata);

+		if (hFind != INVALID_HANDLE_VALUE)

+		{

+			FindClose(hFind);

+			hashtype_ = HashType::SHA1;

+		}

+		else

+		{

+			hashfilename_ = baseFilename + L".md5";

+			hFind = FindFirstFileW(hashfilename_.c_str(), &findfiledata);

+			if (hFind != INVALID_HANDLE_VALUE)

+			{

+				FindClose(hFind);

+				hashtype_ = HashType::MD5;

+			}

+			else

+			{

+				hashfilename_ = baseFilename + L".crc32";

+				hFind = FindFirstFileW(hashfilename_.c_str(), &findfiledata);

+				if (hFind != INVALID_HANDLE_VALUE)

+				{

+					FindClose(hFind);

+					hashtype_ = HashType::CRC32;

+				}

+				else

+				{

+					hashfilename_ = baseFilename + L".sha1";

+					hashtype_ = HashType::SHA1;

+				}

+			}

+		}

+	}

+

+	hFind = FindFirstFileW(hashfilename_.c_str(), &findfiledata);

+	if (hFind != INVALID_HANDLE_VALUE)

+	{

+		FindClose(hFind);

+		if (!(findfiledata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))

+		{

+			checking_ = !updating_;

+		}

+		else

+		{

+			if (!silent_)

+			{

+				std::wstring msg = L"Error: Can't create hash file. Delete '" + hashfilename_ + L"' folder.";

+				MessageBoxW(NULL, msg.c_str(), L"HashCheck", MB_ICONERROR | MB_SYSTEMMODAL);

+			}

+			else

+			{

+				// ...

+			}

+			ExitProcess(0);

+		}

+	}

+	else

+	{

+		updating_ = FALSE;

+	}

+

+}

+

+int HashCheck::Process() const

+{

+	auto mode = HashFileProcessor::Mode::Create;

+	if (checking_)

+	{

+		mode = HashFileProcessor::Mode::Verify;

+	}

+	else if (updating_)

+	{

+		mode = HashFileProcessor::Mode::Update;

+	}

+

+	auto hashtype = HashType::Undefined;

+	switch (hashtype_)

+	{

+	case HashType::CRC32:

+		hashtype = HashType::CRC32;

+		break;

+	case HashType::MD5:

+		hashtype = HashType::MD5;

+		break;

+	case HashType::SHA1:

+		hashtype = HashType::SHA1;

+		break;

+	default:

+		break;

+	}

+

+	HashFileProcessor hashfileprocessor(mode, hashtype, hashfilename_, appfilename_, basepath_);

+ 	auto result = hashfileprocessor.ProcessTree();

+	BOOL viewreport = FALSE;

+	int exitcode = 0;

+	switch (result)

+	{

+	case HashFileProcessor::ProcessResult::FilesAreMissing:

+		if (updating_)

+		{

+			MessageBoxW(NULL, L"Error: Can't update because files are missing.", L"HashCheck", MB_ICONERROR | MB_SYSTEMMODAL);

+		}

+		viewreport = TRUE;

+		exitcode = -1;

+		break;

+	case HashFileProcessor::ProcessResult::ErrorsOccurredWhileProcessing:

+		viewreport = TRUE;

+		exitcode = -2;

+		break;

+	case HashFileProcessor::ProcessResult::CouldNotOpenHashFile:

+		MessageBoxW(NULL, L"Error: Could not open hash file.", L"HashCheck", MB_ICONERROR | MB_SYSTEMMODAL);

+		exitcode = -3;

+		break;

+	case HashFileProcessor::ProcessResult::NoFileToProcess:

+		MessageBoxW(NULL, L"Error: No file to process.", L"HashCheck", MB_ICONERROR | MB_SYSTEMMODAL);

+		exitcode = -4;

+		break;

+	case HashFileProcessor::ProcessResult::NothingToUpdate:

+		MessageBoxW(NULL, L"Error: Nothing to update.", L"HashCheck", MB_ICONERROR | MB_SYSTEMMODAL);

+		exitcode = -5;

+		break;

+	case HashFileProcessor::ProcessResult::Success:

+		if (checking_)

+		{

+			MessageBoxW(NULL, L"All files OK.", L"HashCheck", MB_ICONINFORMATION | MB_SYSTEMMODAL);

+		}

+		else if (updating_)

+		{

+			MessageBoxW(NULL, L"Hash file was updated successfully.", L"HashCheck", MB_ICONINFORMATION | MB_SYSTEMMODAL);

+		}

+		else

+		{

+			MessageBoxW(NULL, L"Hash file was created successfully.", L"HashCheck", MB_ICONINFORMATION | MB_SYSTEMMODAL);

+		}

+		break;

+	default:

+		break;

+	}

+

+	if (viewreport)

+	{

+		WCHAR tempfile[MAX_PATH];

+		WCHAR tempfolder[MAX_PATH];

+		GetTempPathW(MAX_PATH, tempfolder);

+		GetTempFileNameW(tempfolder, L"HashCheck", 0, tempfile);

+		hashfileprocessor.SaveReport(tempfile);

+		ViewReport(tempfile);

+	}

+

+	return exitcode;

+}

+

+std::wstring HashCheck::GetAppFileName(LPCWSTR apptitle) const

+{

+	std::wstring temp = apptitle;

+	auto pos1 = temp.rfind(L"\\") + 1;

+	auto pos2 = temp.rfind(L".") + 4 - pos1;

+	return temp.substr(pos1, pos2);

+}

+

+BOOL HashCheck::ViewReport(LPCWSTR filepath) const

+{

+	WCHAR cmdline[255];

+	lstrcpyW(cmdline, L"notepad.exe ");

+	lstrcatW(cmdline, filepath);

+	STARTUPINFOW si;

+	ZeroMemory(&si, sizeof(si));

+	si.cb = sizeof(si);

+	PROCESS_INFORMATION pi;

+	ZeroMemory(&pi, sizeof(pi));

+	if (CreateProcessW(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))

+	{

+		WaitForSingleObject(pi.hProcess, INFINITE);

+		CloseHandle(pi.hProcess);

+		CloseHandle(pi.hThread);

+		return TRUE;

+	}

+	return FALSE;

+}

+

diff --git a/tests/resources/issue_3020/HashCheck.h b/tests/resources/issue_3020/HashCheck.h
new file mode 100644
index 0000000..91d7869
--- /dev/null
+++ b/tests/resources/issue_3020/HashCheck.h
@@ -0,0 +1,41 @@
+/* Author: macote */

+

+#ifndef HASHCHECK_H_

+#define HASHCHECK_H_

+

+#define _CRT_SECURE_NO_WARNINGS

+

+#include "HashType.h"

+#include "HashFileProcessor.h"

+#include <vector>

+#include <string>

+#include <algorithm>

+#include <Windows.h>

+

+class HashCheck

+{

+private:

+	static LPCWSTR kHashFileBaseName;

+public:

+	HashCheck(std::vector<std::wstring> args) : args_(args)

+	{

+		Initialize();

+	};

+	int Process() const;

+private:

+	void Initialize();

+	std::wstring GetAppFileName(LPCWSTR apptitle) const;

+	BOOL ViewReport(LPCWSTR filepath) const;

+private:

+	std::vector<std::wstring> args_;

+	std::wstring hashfilename_;

+	std::wstring basepath_;

+	std::wstring appfilename_;

+	HashType hashtype_;

+	BOOL silent_;

+	BOOL checking_;

+	BOOL updating_;

+	BOOL skipcheck_;

+};

+

+#endif /* HASHCHECK_H_ */

diff --git a/tests/resources/issue_3020/HashCheck.ico b/tests/resources/issue_3020/HashCheck.ico
new file mode 100644
index 0000000..766cc89
--- /dev/null
+++ b/tests/resources/issue_3020/HashCheck.ico
Binary files differ
diff --git a/tests/resources/issue_3020/HashCheckWindow.cpp b/tests/resources/issue_3020/HashCheckWindow.cpp
new file mode 100644
index 0000000..bedbaa4
--- /dev/null
+++ b/tests/resources/issue_3020/HashCheckWindow.cpp
@@ -0,0 +1,50 @@
+/* Author: macote */

+

+#include "HashCheckWindow.h"

+

+LRESULT HashCheckWindow::OnCreate()

+{

+	return 0;

+}

+

+LRESULT HashCheckWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)

+{

+	switch (uMsg)

+	{

+	case WM_CREATE:

+		return OnCreate();

+	case WM_NCDESTROY:

+		// Death of the root window ends the thread

+		PostQuitMessage(0);

+		break;

+	case WM_SIZE:

+		if (hwndChild_)

+		{

+			SetWindowPos(hwndChild_, NULL, 0, 0, GET_X_LPARAM(lParam), 

+				GET_Y_LPARAM(lParam), SWP_NOZORDER | SWP_NOACTIVATE);

+		}

+		return 0;

+	case WM_SETFOCUS:

+		if (hwndChild_)

+		{

+			SetFocus(hwndChild_);

+		}

+		return 0;

+	}

+	return Window::HandleMessage(uMsg, wParam, lParam);

+}

+

+HashCheckWindow* HashCheckWindow::Create(HINSTANCE hInst)

+{

+	auto self = new HashCheckWindow(hInst);

+	if (self != NULL)

+	{

+		if (self->WinCreateWindow(0, L"HashCheckWindow", WS_OVERLAPPEDWINDOW,

+			CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL))

+		{

+			return self;

+		}

+		delete self;

+	}

+	return NULL;

+}

diff --git a/tests/resources/issue_3020/HashCheckWindow.h b/tests/resources/issue_3020/HashCheckWindow.h
new file mode 100644
index 0000000..f0ea3d6
--- /dev/null
+++ b/tests/resources/issue_3020/HashCheckWindow.h
@@ -0,0 +1,27 @@
+/* Author: macote */

+

+#ifndef HASHCHECKWINDOW_H_

+#define HASHCHECKWINDOW_H_

+

+#include "Window.h"

+#include <Windows.h>

+#include <windowsx.h>

+

+class HashCheckWindow : public Window

+{

+public:

+#if _MSC_VER < 1900

+	HashCheckWindow(HINSTANCE hinst) : Window(hinst) { };

+#else

+	using Window::Window;

+#endif	

+	virtual LPCWSTR ClassName() { return L"HashCheckWindow"; }

+	static HashCheckWindow *Create(HINSTANCE hInst);

+protected:

+	LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);

+	LRESULT OnCreate();

+private:

+	HWND hwndChild_;

+};

+

+#endif /* HASHCHECKWINDOW_H_ */
\ No newline at end of file
diff --git a/tests/resources/issue_3020/HashFile.cpp b/tests/resources/issue_3020/HashFile.cpp
new file mode 100644
index 0000000..c0f2aef
--- /dev/null
+++ b/tests/resources/issue_3020/HashFile.cpp
@@ -0,0 +1,110 @@
+/* Author: macote */

+

+#include "HashFile.h"

+

+const FileEntry HashFile::kFileEntryNull = FileEntry(L"", LARGE_INTEGER(), L"");

+

+void HashFile::Load(const std::wstring& hashfilepath)

+{

+	FileStream hashfile(hashfilepath.c_str(), FileStream::Mode::Open);

+	StreamLineReader hashfilereader(hashfile);

+	WCHAR buffer[2048];

+	std::wstring line, key, sizetemp, filepath, digest;

+	std::wstring::size_type pos1, pos2;

+	LARGE_INTEGER size;

+	// hash line format: <filepath>|<size>|<digest> 

+	do 

+	{

+		line = hashfilereader.ReadLine();

+		if (IsValidHashLine(line))

+		{

+			pos1 = line.find('|', 0);

+			filepath = line.substr(0, pos1);

+			lstrcpyW(buffer, filepath.c_str());

+			key = CharUpperW(buffer);

+			pos2 = line.find('|', pos1 + 1);

+			sizetemp = line.substr(pos1 + 1, pos2 - (pos1 + 1));

+			std::wstringstream wss(sizetemp);

+			wss >> size.QuadPart;

+			digest = line.substr(pos2 + 1, line.size() - pos2);

+			files_.insert(std::pair<std::wstring, FileEntry>(key, FileEntry(filepath, size, digest)));

+		}

+	} while (!hashfilereader.EndOfStream());

+}

+

+void HashFile::Save(const std::wstring& hashfilepath) const

+{

+	FileStream hashfile(hashfilepath, FileStream::Mode::Create);

+	StreamLineWriter hashfilewriter(hashfile);

+	for (auto& item : files_) 

+	{

+		auto fileentry = item.second;

+		hashfilewriter.WriteLine(fileentry.filepath() + L"|" + LargeIntToString(fileentry.size()) + L"|" + fileentry.digest());

+	}

+}

+

+void HashFile::AddFileEntry(const std::wstring filepath, const LARGE_INTEGER size, const std::wstring digest)

+{

+	files_.insert(std::pair<std::wstring, FileEntry>(filepath, FileEntry(filepath, size, digest)));

+}

+

+std::map<std::wstring, FileEntry, std::less<std::wstring>>::const_iterator HashFile::FindEntry(const std::wstring& filepath) const

+{

+	WCHAR key[2048];

+	lstrcpyW(key, filepath.c_str());

+	CharUpperW(key);

+	return files_.find(key);

+}

+

+void HashFile::RemoveFileEntry(const std::wstring& filepath)

+{

+	auto i = FindEntry(filepath);

+	if (i != files_.end())

+	{

+		files_.erase(i);

+	}

+}

+

+BOOL HashFile::ContainsFileEntry(const std::wstring& filepath) const

+{

+	auto i = FindEntry(filepath);

+	return i != files_.end();

+}

+

+const FileEntry& HashFile::GetFileEntry(const std::wstring& filepath) const

+{

+	auto i = FindEntry(filepath);

+	if (i != files_.end())

+	{

+		return (*i).second;

+	}

+	else

+	{

+		return kFileEntryNull;

+	}

+}

+

+std::list<std::wstring> HashFile::GetFilePaths() const

+{

+	std::list<std::wstring> filepaths;

+	for (auto& item : files_)

+	{

+		filepaths.push_back(item.second.filepath());

+	}

+	return filepaths;

+}

+

+BOOL HashFile::IsValidHashLine(const std::wstring& fileentryline) const

+{

+	if (fileentryline.size() == 0) return FALSE;

+	if (fileentryline.size() > 2176) return FALSE;

+	// TODO: implement proper hash line validation

+	return TRUE;

+}

+

+std::wstring HashFile::LargeIntToString(const LARGE_INTEGER& li) const

+{

+	std::wstringstream wss;

+	wss << li.QuadPart;

+	return wss.str();

+}

diff --git a/tests/resources/issue_3020/HashFile.h b/tests/resources/issue_3020/HashFile.h
new file mode 100644
index 0000000..f6bb6b6
--- /dev/null
+++ b/tests/resources/issue_3020/HashFile.h
@@ -0,0 +1,55 @@
+/* Author: macote */

+

+#ifndef HASHFILE_H_

+#define HASHFILE_H_

+

+#include "FileStream.h"

+#include "StreamLineReader.h"

+#include "StreamLineWriter.h"

+#include <sstream>

+#include <list>

+#include <map>

+#include <string>

+#include <Windows.h>

+

+class FileEntry

+{

+public:

+	FileEntry(const std::wstring filepath, const LARGE_INTEGER size, const std::wstring digest) 

+		: filepath_(filepath), size_(size), digest_(digest) { };

+	std::wstring filepath() const { return filepath_; }

+	LARGE_INTEGER size() const { return size_; }

+	std::wstring digest() const { return digest_; }

+private:

+	const std::wstring filepath_;

+	const LARGE_INTEGER size_;

+	const std::wstring digest_;

+};

+

+class HashFile

+{

+public:

+	static const FileEntry kFileEntryNull;

+public:

+	HashFile() { };

+	~HashFile()

+	{

+		files_.clear();

+	}

+	void Save(const std::wstring& hashfilepath) const;

+	void Load(const std::wstring& hashfilepath);

+	void AddFileEntry(const std::wstring filepath, const LARGE_INTEGER li, const std::wstring digest);

+	void RemoveFileEntry(const std::wstring& filepath);

+	BOOL IsEmpty() const { return files_.size() == 0; }

+	BOOL ContainsFileEntry(const std::wstring& filepath) const;

+	const FileEntry& GetFileEntry(const std::wstring& filepath) const;

+	std::list<std::wstring> GetFilePaths() const;

+private:

+	std::map<std::wstring, FileEntry, std::less<std::wstring>>::const_iterator FindEntry(const std::wstring& filepath) const;

+	BOOL IsValidHashLine(const std::wstring& fileentryline) const;

+	std::wstring LargeIntToString(const LARGE_INTEGER& li) const;

+private:

+	std::map<std::wstring, FileEntry, std::less<std::wstring>> files_;

+};

+

+#endif /* HASHFILE_H_ */

diff --git a/tests/resources/issue_3020/HashFileProcessor.cpp b/tests/resources/issue_3020/HashFileProcessor.cpp
new file mode 100644
index 0000000..772f1a9
--- /dev/null
+++ b/tests/resources/issue_3020/HashFileProcessor.cpp
@@ -0,0 +1,157 @@
+/* Author: macote */

+

+#include "HashFileProcessor.h"

+

+HashFileProcessor::ProcessResult HashFileProcessor::ProcessTree()

+{

+	auto result = ProcessResult::Success;

+	newfilesupdated_ = FALSE;

+	if (mode_ == HashFileProcessor::Mode::Verify || mode_ == HashFileProcessor::Mode::Update)

+	{

+		try

+		{

+			hashfile_.Load(hashfilename_);

+		}

+		catch (...)

+		{

+			result = ProcessResult::CouldNotOpenHashFile;

+			return result;

+		}

+	}

+	FileTree filetree(basepath_, *this);

+	filetree.Process();

+	if (mode_ == HashFileProcessor::Mode::Create)

+	{

+		if (hashfile_.IsEmpty())

+		{

+			result = ProcessResult::NoFileToProcess;

+		}

+		else if (!report_.IsEmpty())

+		{

+			result = ProcessResult::ErrorsOccurredWhileProcessing;

+		}

+		else

+		{

+			hashfile_.Save(hashfilename_);

+		}

+	}

+	else if (mode_ == HashFileProcessor::Mode::Verify || mode_ == HashFileProcessor::Mode::Update)

+	{

+		if (!hashfile_.IsEmpty())

+		{

+			for (auto& relativefilepath : hashfile_.GetFilePaths())

+			{

+				// TODO: replace hardcoded text

+				report_.AddLine(L"Missing             : " + relativefilepath);

+			}

+			result = ProcessResult::FilesAreMissing;

+		}

+		else if (!report_.IsEmpty())

+		{

+			result = ProcessResult::ErrorsOccurredWhileProcessing;

+		}

+		else if (mode_ == HashFileProcessor::Mode::Update)

+		{

+			if (!newfilesupdated_)

+			{

+				result = ProcessResult::NothingToUpdate;

+			}

+			else

+			{

+				// replace old hash file

+				DeleteFileW(hashfilename_.c_str());

+				newhashfile_.Save(hashfilename_);

+			}

+		}

+	}

+	return result;

+}

+

+void HashFileProcessor::ProcessFile(const std::wstring& filepath)

+{

+	if (lstrcmpiW(appfilepath_.c_str(), filepath.c_str()) == 0 || lstrcmpiW(hashfilename_.c_str(), filepath.c_str()) == 0)

+	{

+		// skip self and current hash file

+		return;

+	}

+	auto relativefilepath = filepath.substr(basepath_.length(), filepath.length());

+	const FileEntry& fileentry = hashfile_.GetFileEntry(relativefilepath);

+	if (mode_ == HashFileProcessor::Mode::Verify)

+	{

+		if (&fileentry == &HashFile::kFileEntryNull)

+		{

+			report_.AddLine(L"Unknown             : " + relativefilepath);

+			return;

+		}

+	}

+	else if (mode_ == HashFileProcessor::Mode::Update)

+	{

+		if (&fileentry == &HashFile::kFileEntryNull)

+		{

+			newhashfile_.AddFileEntry(fileentry.filepath(), fileentry.size(), fileentry.digest());

+			hashfile_.RemoveFileEntry(relativefilepath);

+			return;

+		}

+	}

+	LARGE_INTEGER size;

+	size.QuadPart = 0;

+	auto file = CreateFileW(filepath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL,

+		OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

+	if (file != INVALID_HANDLE_VALUE)

+	{

+		GetFileSizeEx(file, &size);

+		CloseHandle(file);

+	}

+	else

+	{

+		report_.AddLine(L"Error opening file  : " + relativefilepath);

+		if (mode_ == HashFileProcessor::Mode::Verify)

+		{

+			hashfile_.RemoveFileEntry(relativefilepath);

+		}

+		return;

+	}

+	if (mode_ == HashFileProcessor::Mode::Verify)

+	{

+		if (size.QuadPart != fileentry.size().QuadPart)

+		{

+			report_.AddLine(L"Incorrect file size : " + relativefilepath);

+			hashfile_.RemoveFileEntry(relativefilepath);

+			return;

+		}

+	}

+	auto filehash = FileHashFactory::Create(hashtype_, filepath);

+	if (progressevent_ != nullptr)

+	{

+		hfppea_.bytesprocessed.QuadPart = 0;

+		hfppea_.relativefilepath = relativefilepath;

+		progressevent_(hfppea_);

+		filehash->SetBytesProcessedEventHandler([this](FileHashBytesProcessedEventArgs fhbea) {

+			this->hfppea_.bytesprocessed = fhbea.bytesprocessed;

+			this->progressevent_(hfppea_);

+		}, bytesprocessednotificationblocksize_);

+	}

+	filehash->Compute();

+	std::wstring digest = filehash->digest();

+	if (mode_ == HashFileProcessor::Mode::Create)

+	{

+		hashfile_.AddFileEntry(relativefilepath, size, digest);

+	}

+	else if (mode_ == HashFileProcessor::Mode::Update)

+	{

+		newhashfile_.AddFileEntry(relativefilepath, size, digest);

+		newfilesupdated_ = TRUE;

+	}

+	else if (mode_ == HashFileProcessor::Mode::Verify)

+	{

+		if (size.QuadPart != fileentry.size().QuadPart)

+		{

+			report_.AddLine(L"Incorrect file size : " + relativefilepath);

+		}

+		else if (digest != fileentry.digest())

+		{

+			report_.AddLine(L"Incorrect hash      : " + relativefilepath);

+		}

+		hashfile_.RemoveFileEntry(relativefilepath);

+	}

+}

diff --git a/tests/resources/issue_3020/HashFileProcessor.h b/tests/resources/issue_3020/HashFileProcessor.h
new file mode 100644
index 0000000..98c9967
--- /dev/null
+++ b/tests/resources/issue_3020/HashFileProcessor.h
@@ -0,0 +1,67 @@
+/* Author: macote */

+

+#include "HashFile.h"

+#include "FileTree.h"

+#include "HashType.h"

+#include "FileHashFactory.h"

+#include "Report.h"

+

+struct HashFileProcessorProgressEventArgs

+{

+	std::wstring relativefilepath;

+	LARGE_INTEGER bytesprocessed;

+};

+

+class HashFileProcessor : public IFileTreeAction

+{

+public:

+	static const DWORD kDefaultBytesProcessedNotificationBlockSize = 1048576;

+public:

+	enum class Mode

+	{

+		Create,

+		Update,

+		Verify,

+		Undefined

+	};

+	enum class ProcessResult

+	{

+		FilesAreMissing,

+		NothingToUpdate,

+		CouldNotOpenHashFile,

+		ErrorsOccurredWhileProcessing,

+		NoFileToProcess,

+		Success

+	};

+	HashFileProcessor(Mode mode, HashType hashtype, std::wstring hashfilename, std::wstring appfilepath, std::wstring basepath)

+		: mode_(mode), hashtype_(hashtype), hashfilename_(hashfilename), appfilepath_(appfilepath), basepath_(basepath)

+	{ 

+		progressevent_ = nullptr;

+	};

+	ProcessResult ProcessTree();

+	void ProcessFile(const std::wstring& filepath);

+	void SaveReport(const std::wstring& reportpath) const { report_.Save(reportpath); }

+	void SetProgressEventHandler(std::function<void(HashFileProcessorProgressEventArgs)> handler)

+	{

+		SetProgressEventHandler(handler, kDefaultBytesProcessedNotificationBlockSize);

+	}

+	void SetProgressEventHandler(std::function<void(HashFileProcessorProgressEventArgs)> handler,

+		const DWORD bytesprocessednotificationblocksize)

+	{

+		progressevent_ = handler;

+		bytesprocessednotificationblocksize_ = bytesprocessednotificationblocksize;

+	}

+private:

+	Mode mode_;

+	HashType hashtype_;

+	HashFile hashfile_;

+	HashFile newhashfile_;

+	std::wstring hashfilename_;

+	std::wstring appfilepath_;

+	std::wstring basepath_;

+	BOOL newfilesupdated_ = FALSE;

+	Report report_;

+	HashFileProcessorProgressEventArgs hfppea_;

+	DWORD bytesprocessednotificationblocksize_;

+	std::function<void(HashFileProcessorProgressEventArgs)> progressevent_;

+};

diff --git a/tests/resources/issue_3020/HashType.h b/tests/resources/issue_3020/HashType.h
new file mode 100644
index 0000000..5ee9c51
--- /dev/null
+++ b/tests/resources/issue_3020/HashType.h
@@ -0,0 +1,14 @@
+/* Author: macote */

+

+#ifndef HASHTYPE_H_

+#define HASHTYPE_H_

+

+enum class HashType

+{

+	CRC32,

+	MD5,

+	SHA1,

+	Undefined

+};

+

+#endif /* HASHTYPE_H_ */

diff --git a/tests/resources/issue_3020/MD5FileHash.cpp b/tests/resources/issue_3020/MD5FileHash.cpp
new file mode 100644
index 0000000..3aea659
--- /dev/null
+++ b/tests/resources/issue_3020/MD5FileHash.cpp
@@ -0,0 +1,177 @@
+

+

+void MD5FileHash::Update(const UINT32 bytecount)

+{

+	UINT32 index = context_.count[0];	// update bitcount

+	context_.count[0] = index + (bytecount << 3);

+	if (context_.count[0] < index)

+	{

+		context_.count[1]++;	// carry from low to high

+	}

+	context_.count[1] += bytecount >> 29;

+	index = (index >> 3) & 63;	// bytes already in shsInfo->data

+	UINT32 bytesleft = bytecount;

+	if (index > 0)

+	{

+		// handle any leading odd-sized chunks

+		PBYTE ctxbuffer = (PBYTE)context_.buffer + index;

+		index = 64 - index;

+		if (bytecount < index)

+		{

+			CopyMemory(ctxbuffer, buffer_, bytecount);

+			return;

+		}

+		CopyMemory(ctxbuffer, buffer_, index);

+		Transform(context_.state, (PUINT32)context_.buffer);

+		ctxbuffer += index;

+		bytesleft -= index;

+	}

+	// process data in 64-byte chunks

+	PBYTE buffer = buffer_;

+	while (bytesleft >= 64)

+	{

+		CopyMemory(context_.buffer, buffer, sizeof(context_.buffer));

+		Transform(context_.state, (PUINT32)context_.buffer);

+		buffer += 64;

+		bytesleft -= 64;

+	}

+	// handle any remaining bytes of data.

+	CopyMemory(context_.buffer, buffer, bytesleft);

+}

+

+void MD5FileHash::Finalize()

+{

+	UINT32 index = (context_.count[0] >> 3) & 63;	// compute number of bytes mod 64

+	// set the first char of padding to 0x80.  this is safe since there is

+	// always at least one byte free

+	PBYTE ctxbuffer = context_.buffer + index;

+	*ctxbuffer++ = 0x80;

+	index = 64 - 1 - index;	// bytes of padding needed to make 64 bytes

+	// pad out to 56 mod 64

+	if (index < 8)

+	{

+		// two lots of padding:  pad the first block to 64 bytes

+		ZeroMemory(ctxbuffer, index);

+		Transform(context_.state, (PUINT32)context_.buffer);

+		// now fill the next block with 56 bytes

+		ZeroMemory(context_.buffer, 56);

+	}

+	else

+	{

+		ZeroMemory(ctxbuffer, index - 8);	// pad block to 56 bytes

+	}

+	// append length in bits and transform

+	typedef union

+	{

+		BYTE c[64];

+		UINT32 l[16];

+	} CHAR64LONG16, *PCHAR64LONG16;

+	PCHAR64LONG16 bufferlong = (PCHAR64LONG16)context_.buffer;

+	bufferlong->l[14] = context_.count[0];

+	bufferlong->l[15] = context_.count[1];

+	Transform(context_.state, (PUINT32)context_.buffer);

+	CopyMemory(hash_, context_.state, sizeof(hash_));

+}

+

+void MD5FileHash::ConvertHashToDigestString()

+{

+	std::wstringstream wss;

+	wss << std::hex << std::setw(2) << std::setfill(L'0') << std::uppercase;

+	for (UINT i = 0; i < sizeof(hash_); i++)

+	{

+		wss << hash_[i];

+	}

+	digest_.append(wss.str());

+}

+

+void MD5FileHash::Transform(UINT32 state[4], PUINT32 buffer)

+{

+	register UINT32 a, b, c, d;

+

+	a = state[0];

+	b = state[1];

+	c = state[2];

+	d = state[3];

+

+#define F1(x, y, z) (z ^ (x & (y ^ z)))

+#define F2(x, y, z) F1(z, x, y)

+#define F3(x, y, z) (x ^ y ^ z)

+#define F4(x, y, z) (y ^ (x | ~z))

+

+#define MD5STEP(f, w, x, y, z, data, s) (w += f(x, y, z) + data, w = w << s | w >> (32 - s), w += x)

+

+	MD5STEP(F1, a, b, c, d, buffer[0]  + 0xD76AA478L, 7);

+	MD5STEP(F1, d, a, b, c, buffer[1]  + 0xE8C7B756L, 12);

+	MD5STEP(F1, c, d, a, b, buffer[2]  + 0x242070DBL, 17);

+	MD5STEP(F1, b, c, d, a, buffer[3]  + 0xC1BDCEEEL, 22);

+	MD5STEP(F1, a, b, c, d, buffer[4]  + 0xF57C0FAFL, 7);

+	MD5STEP(F1, d, a, b, c, buffer[5]  + 0x4787C62AL, 12);

+	MD5STEP(F1, c, d, a, b, buffer[6]  + 0xA8304613L, 17);

+	MD5STEP(F1, b, c, d, a, buffer[7]  + 0xFD469501L, 22);

+	MD5STEP(F1, a, b, c, d, buffer[8]  + 0x698098D8L, 7);

+	MD5STEP(F1, d, a, b, c, buffer[9]  + 0x8B44F7AFL, 12);

+	MD5STEP(F1, c, d, a, b, buffer[10] + 0xFFFF5BB1L, 17);

+	MD5STEP(F1, b, c, d, a, buffer[11] + 0x895CD7BEL, 22);

+	MD5STEP(F1, a, b, c, d, buffer[12] + 0x6B901122L, 7);

+	MD5STEP(F1, d, a, b, c, buffer[13] + 0xFD987193L, 12);

+	MD5STEP(F1, c, d, a, b, buffer[14] + 0xA679438EL, 17);

+	MD5STEP(F1, b, c, d, a, buffer[15] + 0x49B40821L, 22);

+

+	MD5STEP(F2, a, b, c, d, buffer[1]  + 0xF61E2562L, 5);

+	MD5STEP(F2, d, a, b, c, buffer[6]  + 0xC040B340L, 9);

+	MD5STEP(F2, c, d, a, b, buffer[11] + 0x265E5A51L, 14);

+	MD5STEP(F2, b, c, d, a, buffer[0]  + 0xE9B6C7AAL, 20);

+	MD5STEP(F2, a, b, c, d, buffer[5]  + 0xD62F105DL, 5);

+	MD5STEP(F2, d, a, b, c, buffer[10] + 0x02441453L, 9);

+	MD5STEP(F2, c, d, a, b, buffer[15] + 0xD8A1E681L, 14);

+	MD5STEP(F2, b, c, d, a, buffer[4]  + 0xE7D3FBC8L, 20);

+	MD5STEP(F2, a, b, c, d, buffer[9]  + 0x21E1CDE6L, 5);

+	MD5STEP(F2, d, a, b, c, buffer[14] + 0xC33707D6L, 9);

+	MD5STEP(F2, c, d, a, b, buffer[3]  + 0xF4D50D87L, 14);

+	MD5STEP(F2, b, c, d, a, buffer[8]  + 0x455A14EDL, 20);

+	MD5STEP(F2, a, b, c, d, buffer[13] + 0xA9E3E905L, 5);

+	MD5STEP(F2, d, a, b, c, buffer[2]  + 0xFCEFA3F8L, 9);

+	MD5STEP(F2, c, d, a, b, buffer[7]  + 0x676F02D9L, 14);

+	MD5STEP(F2, b, c, d, a, buffer[12] + 0x8D2A4C8AL, 20);

+

+	MD5STEP(F3, a, b, c, d, buffer[5]  + 0xFFFA3942L, 4);

+	MD5STEP(F3, d, a, b, c, buffer[8]  + 0x8771F681L, 11);

+	MD5STEP(F3, c, d, a, b, buffer[11] + 0x6D9D6122L, 16);

+	MD5STEP(F3, b, c, d, a, buffer[14] + 0xFDE5380CL, 23);

+	MD5STEP(F3, a, b, c, d, buffer[1]  + 0xA4BEEA44L, 4);

+	MD5STEP(F3, d, a, b, c, buffer[4]  + 0x4BDECFA9L, 11);

+	MD5STEP(F3, c, d, a, b, buffer[7]  + 0xF6BB4B60L, 16);

+	MD5STEP(F3, b, c, d, a, buffer[10] + 0xBEBFBC70L, 23);

+	MD5STEP(F3, a, b, c, d, buffer[13] + 0x289B7EC6L, 4);

+	MD5STEP(F3, d, a, b, c, buffer[0]  + 0xEAA127FAL, 11);

+	MD5STEP(F3, c, d, a, b, buffer[3]  + 0xD4EF3085L, 16);

+	MD5STEP(F3, b, c, d, a, buffer[6]  + 0x04881D05L, 23);

+	MD5STEP(F3, a, b, c, d, buffer[9]  + 0xD9D4D039L, 4);

+	MD5STEP(F3, d, a, b, c, buffer[12] + 0xE6DB99E5L, 11);

+	MD5STEP(F3, c, d, a, b, buffer[15] + 0x1FA27CF8L, 16);

+	MD5STEP(F3, b, c, d, a, buffer[2]  + 0xC4AC5665L, 23);

+

+	MD5STEP(F4, a, b, c, d, buffer[0]  + 0xF4292244L, 6);

+	MD5STEP(F4, d, a, b, c, buffer[7]  + 0x432AFF97L, 10);

+	MD5STEP(F4, c, d, a, b, buffer[14] + 0xAB9423A7L, 15);

+	MD5STEP(F4, b, c, d, a, buffer[5]  + 0xFC93A039L, 21);

+	MD5STEP(F4, a, b, c, d, buffer[12] + 0x655B59C3L, 6);

+	MD5STEP(F4, d, a, b, c, buffer[3]  + 0x8F0CCC92L, 10);

+	MD5STEP(F4, c, d, a, b, buffer[10] + 0xFFEFF47DL, 15);

+	MD5STEP(F4, b, c, d, a, buffer[1]  + 0x85845DD1L, 21);

+	MD5STEP(F4, a, b, c, d, buffer[8]  + 0x6FA87E4FL, 6);

+	MD5STEP(F4, d, a, b, c, buffer[15] + 0xFE2CE6E0L, 10);

+	MD5STEP(F4, c, d, a, b, buffer[6]  + 0xA3014314L, 15);

+	MD5STEP(F4, b, c, d, a, buffer[13] + 0x4E0811A1L, 21);

+	MD5STEP(F4, a, b, c, d, buffer[4]  + 0xF7537E82L, 6);

+	MD5STEP(F4, d, a, b, c, buffer[11] + 0xBD3AF235L, 10);

+	MD5STEP(F4, c, d, a, b, buffer[2]  + 0x2AD7D2BBL, 15);

+	MD5STEP(F4, b, c, d, a, buffer[9]  + 0xEB86D391L, 21);

+

+	state[0] += a;

+	state[1] += b;

+	state[2] += c;

+	state[3] += d;

+

+	a = b = c = d = 0;

+}

diff --git a/tests/resources/issue_3020/MD5FileHash.h b/tests/resources/issue_3020/MD5FileHash.h
new file mode 100644
index 0000000..b008070
--- /dev/null
+++ b/tests/resources/issue_3020/MD5FileHash.h
@@ -0,0 +1,38 @@
+/* Author: macote */

+

+#ifndef MD5FILEHASH_H_

+#define MD5FILEHASH_H_

+

+#include "FileHash.h"

+#include <iomanip>

+#include <sstream>

+#include <string>

+#include <Windows.h>

+

+struct MD5Context

+{

+	UINT32 state[4];	// state (ABCD)

+	UINT32 count[2];	// number of bits, modulo 2^64 (lsb first)

+	BYTE buffer[64];	// input buffer

+};

+

+class MD5FileHash : public FileHash

+{

+public:

+#if _MSC_VER < 1900

+	MD5FileHash(const std::wstring& filepath, const DWORD buffersize) : FileHash(filepath, buffersize) { };

+	MD5FileHash(const std::wstring& filepath) : FileHash(filepath) { };

+#else

+	using FileHash::FileHash;

+#endif

+private:

+	void Initialize();

+	void Update(const UINT32 bytecount);

+	void Finalize();

+	void Transform(UINT32 state[4], PUINT32 buffer);

+	void ConvertHashToDigestString();

+	BYTE hash_[16];

+	MD5Context context_;

+};

+

+#endif /* MD5FILEHASH_H_ */

diff --git a/tests/resources/issue_3020/Program.cpp b/tests/resources/issue_3020/Program.cpp
new file mode 100644
index 0000000..b765e0c
--- /dev/null
+++ b/tests/resources/issue_3020/Program.cpp
@@ -0,0 +1,53 @@
+/* Author: macote */

+

+#ifndef UNICODE

+#define UNICODE

+#endif

+#ifndef _UNICODE

+#define _UNICODE

+#endif

+

+#define WIN32_LEAN_AND_MEAN

+

+#include "HashCheck.h"

+#include "HashCheckWindow.h"

+#include <string>

+#include <vector>

+#include <Windows.h>

+#include <Ole2.h>

+#include <CommCtrl.h>

+#include <shellapi.h>

+

+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)

+{

+	int argscount;

+	auto args = CommandLineToArgvW(GetCommandLineW(), &argscount);

+	std::vector<std::wstring> argsvector;

+	for (int i = 0; i < argscount; ++i)

+	{

+		argsvector.push_back(args[i]);

+	}

+	LocalFree(args);

+

+	//if (SUCCEEDED(CoInitialize(NULL)))

+	//{

+	//	InitCommonControls();

+	//	HashCheckWindow *hcw = HashCheckWindow::Create(hInstance);

+	//	if (hcw)

+	//	{

+	//		ShowWindow(hcw->GetHWND(), nCmdShow);

+	//		MSG msg;

+	//		while (GetMessage(&msg, NULL, 0, 0))

+	//		{

+	//			TranslateMessage(&msg);

+	//			DispatchMessage(&msg);

+	//		}

+	//	}

+	//	CoUninitialize();

+	//}

+

+	HashCheck hc(argsvector);

+	auto result = hc.Process();

+

+	return result;

+}

diff --git a/tests/resources/issue_3020/README.md b/tests/resources/issue_3020/README.md
new file mode 100644
index 0000000..37e3522
--- /dev/null
+++ b/tests/resources/issue_3020/README.md
@@ -0,0 +1,48 @@
+HashCheck for Windows

+=====================

+

+HashCheck is a Windows application that creates and verifies file checksums.

+

+

+How to build

+------------

+

+This project is compatible with:

+- Visual Studio 2013

+- Eclipse Luna CDT with MinGW (GCC 4.8.1)

+

+

+Usage

+-----

+

+Launch the executable to create a checksum file if it does not already exists.

+

+Launch the executable to verify file integrity against a checksum file if it exists.

+ 

+

+License

+-------

+

+Hashing code found on the Internet is in the public domain:

+

+MD5: Colin Plumb (1993) / John Walker (2003)

+

+SHA1: Steve Reid (199?) / ... / Ralph Giles (2002)

+

+FileStream code inspired by dotnet/corefx's Win32FileStream.cs.  See FileStream.cpp for licensing information.

+

+

+FAQ

+---

+

+### Why MinGW?

+

+I wanted to have an executable having the least runtime dependencies possible.

+

+### Why FileStream?

+

+MinGW's fstream can't open WCHAR filenames and I was not interested in changing fstream's implementation.

+

+### What's the coding style?

+

+The coding style is inspired from Google C++ [coding style guide](http://google-styleguide.googlecode.com/svn/trunk/cppguide.html "Google C++ Style Guide").

diff --git a/tests/resources/issue_3020/Report.h b/tests/resources/issue_3020/Report.h
new file mode 100644
index 0000000..98b0b0d
--- /dev/null
+++ b/tests/resources/issue_3020/Report.h
@@ -0,0 +1,30 @@
+/* Author: macote */

+

+#ifndef REPORT_H_

+#define REPORT_H_

+

+#include "FileStream.h"

+#include "StreamLineWriter.h"

+#include <string>

+#include <list>

+#include <Windows.h>

+

+class Report

+{

+public:

+	void AddLine(const std::wstring line) { report_.push_back(line); };

+	void Save(const std::wstring& reportpath) const

+	{

+		FileStream reportfile(reportpath, FileStream::Mode::Truncate);

+		StreamLineWriter reportfilewriter(reportfile);

+		for (auto& item : report_)

+		{

+			reportfilewriter.WriteLine(item);

+		}

+	};

+	BOOL IsEmpty() { return report_.size() == 0; }

+private:

+	std::list<std::wstring> report_;

+};

+

+#endif /* REPORT_H_ */
\ No newline at end of file
diff --git a/tests/resources/issue_3020/SHA1FileHash.cpp b/tests/resources/issue_3020/SHA1FileHash.cpp
new file mode 100644
index 0000000..661e27a
--- /dev/null
+++ b/tests/resources/issue_3020/SHA1FileHash.cpp
@@ -0,0 +1,132 @@
+/* Author: macote */

+

+#include "SHA1FileHash.h"

+

+void SHA1FileHash::Initialize()

+{

+	context_.state[0] = 0x67452301;

+	context_.state[1] = 0xEFCDAB89;

+	context_.state[2] = 0x98BADCFE;

+	context_.state[3] = 0x10325476;

+	context_.state[4] = 0xC3D2E1F0;

+	context_.count[0] = context_.count[1] = 0;

+}

+

+void SHA1FileHash::Update(const UINT32 bytecount)

+{

+	UINT32 i, index = (context_.count[0] >> 3) & 63;

+	if ((context_.count[0] += bytecount << 3) < (bytecount << 3))

+	{

+		context_.count[1]++;

+	}

+	context_.count[1] += (bytecount >> 29);

+	if ((index + bytecount) > 63)

+	{

+		i = 64 - index;

+		CopyMemory(&context_.buffer[index], buffer_, i);

+		Transform(context_.state, (PUINT32)context_.buffer);

+		for (; i + 63 < bytecount; i += 64)

+		{

+			Transform(context_.state, (PUINT32)(buffer_ + i));

+		}

+		index = 0;

+	}

+	else

+	{

+		i = 0;

+	}

+	CopyMemory(&context_.buffer[index], buffer_ + i, bytecount - i);

+}

+

+void SHA1FileHash::Finalize()

+{

+	BYTE finalcount[8];

+	for (UINT i = 0; i < 8; i++)

+	{

+		finalcount[i] = (BYTE)((context_.count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255);

+	}

+	CopyMemory(buffer_, "\200", 1);

+	Update(1);

+	CopyMemory(buffer_, "\0", 1);

+	while ((context_.count[0] & 504) != 448)

+	{

+		Update(1);

+	}

+	CopyMemory(buffer_, finalcount, sizeof(finalcount));

+	Update(8);

+	for (UINT i = 0; i < sizeof(hash_); i++)

+	{

+		hash_[i] = (BYTE)((context_.state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255);

+	}

+}

+

+void SHA1FileHash::ConvertHashToDigestString()

+{

+	std::wstringstream wss;

+	wss << std::hex << std::setw(2) << std::setfill(L'0') << std::uppercase;

+	for (UINT i = 0; i < sizeof(hash_); i++)

+	{

+		wss << hash_[i];

+	}

+	digest_.append(wss.str());

+}

+

+void SHA1FileHash::Transform(UINT32 state[5], PUINT32 buffer)

+{

+	typedef union {

+		BYTE c[64];

+		UINT32 l[16];

+	} CHAR64LONG16, *PCHAR64LONG16;

+	PCHAR64LONG16 block;

+	block = (PCHAR64LONG16)buffer;

+

+	register UINT32 a, b, c, d, e;

+	a = state[0];

+	b = state[1];

+	c = state[2];

+	d = state[3];

+	e = state[4];

+

+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))

+#define blk0(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) \

+	| (rol(block->l[i], 8) & 0x00FF00FF))

+#define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ block->l[(i + 8) & 15] \

+	^ block->l[(i + 2) & 15] ^ block->l[i & 15], 1))

+#define R0(v, w, x, y, z, i) z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); w = rol(w, 30);

+#define R1(v, w, x, y, z, i) z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); w = rol(w, 30);

+#define R2(v, w, x, y, z, i) z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); w = rol(w, 30);

+#define R3(v, w, x, y, z, i) z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); w = rol(w, 30);

+#define R4(v, w, x, y, z, i) z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); w = rol(w, 30);

+

+	R0(a, b, c, d, e, 0); R0(e, a, b, c, d, 1); R0(d, e, a, b, c, 2); R0(c, d, e, a, b, 3);

+	R0(b, c, d, e, a, 4); R0(a, b, c, d, e, 5); R0(e, a, b, c, d, 6); R0(d, e, a, b, c, 7);

+	R0(c, d, e, a, b, 8); R0(b, c, d, e, a, 9); R0(a, b, c, d, e, 10); R0(e, a, b, c, d, 11);

+	R0(d, e, a, b, c, 12); R0(c, d, e, a, b, 13); R0(b, c, d, e, a, 14); R0(a, b, c, d, e, 15);

+	R1(e, a, b, c, d, 16); R1(d, e, a, b, c, 17); R1(c, d, e, a, b, 18); R1(b, c, d, e, a, 19);

+	R2(a, b, c, d, e, 20); R2(e, a, b, c, d, 21); R2(d, e, a, b, c, 22); R2(c, d, e, a, b, 23);

+	R2(b, c, d, e, a, 24); R2(a, b, c, d, e, 25); R2(e, a, b, c, d, 26); R2(d, e, a, b, c, 27);

+	R2(c, d, e, a, b, 28); R2(b, c, d, e, a, 29); R2(a, b, c, d, e, 30); R2(e, a, b, c, d, 31);

+	R2(d, e, a, b, c, 32); R2(c, d, e, a, b, 33); R2(b, c, d, e, a, 34); R2(a, b, c, d, e, 35);

+	R2(e, a, b, c, d, 36); R2(d, e, a, b, c, 37); R2(c, d, e, a, b, 38); R2(b, c, d, e, a, 39);

+	R3(a, b, c, d, e, 40); R3(e, a, b, c, d, 41); R3(d, e, a, b, c, 42); R3(c, d, e, a, b, 43);

+	R3(b, c, d, e, a, 44); R3(a, b, c, d, e, 45); R3(e, a, b, c, d, 46); R3(d, e, a, b, c, 47);

+	R3(c, d, e, a, b, 48); R3(b, c, d, e, a, 49); R3(a, b, c, d, e, 50); R3(e, a, b, c, d, 51);

+	R3(d, e, a, b, c, 52); R3(c, d, e, a, b, 53); R3(b, c, d, e, a, 54); R3(a, b, c, d, e, 55);

+	R3(e, a, b, c, d, 56); R3(d, e, a, b, c, 57); R3(c, d, e, a, b, 58); R3(b, c, d, e, a, 59);

+	R4(a, b, c, d, e, 60); R4(e, a, b, c, d, 61); R4(d, e, a, b, c, 62); R4(c, d, e, a, b, 63);

+	R4(b, c, d, e, a, 64); R4(a, b, c, d, e, 65); R4(e, a, b, c, d, 66); R4(d, e, a, b, c, 67);

+	R4(c, d, e, a, b, 68); R4(b, c, d, e, a, 69); R4(a, b, c, d, e, 70); R4(e, a, b, c, d, 71);

+	R4(d, e, a, b, c, 72); R4(c, d, e, a, b, 73); R4(b, c, d, e, a, 74); R4(a, b, c, d, e, 75);

+	R4(e, a, b, c, d, 76); R4(d, e, a, b, c, 77); R4(c, d, e, a, b, 78); R4(b, c, d, e, a, 79);

+

+	state[0] += a;

+	state[1] += b;

+	state[2] += c;

+	state[3] += d;

+	state[4] += e;

+

+	a = b = c = d = e = 0;

+}

+

+

+

diff --git a/tests/resources/issue_3020/SHA1FileHash.h b/tests/resources/issue_3020/SHA1FileHash.h
new file mode 100644
index 0000000..ff147ae
--- /dev/null
+++ b/tests/resources/issue_3020/SHA1FileHash.h
@@ -0,0 +1,38 @@
+/* Author: macote */

+

+#ifndef SHA1FILEHASH_H_

+#define SHA1FILEHASH_H_

+

+#include "FileHash.h"

+#include <iomanip>

+#include <sstream>

+#include <string>

+#include <Windows.h>

+

+struct SHA1Context

+{

+	UINT32 state[5];	// state (ABCDE)

+	UINT32 count[2];	// bits

+	BYTE buffer[64];	// input buffer

+};

+

+class SHA1FileHash : public FileHash

+{

+public:

+#if _MSC_VER < 1900

+	SHA1FileHash(const std::wstring& filepath, const DWORD buffersize) : FileHash(filepath, buffersize) { };

+	SHA1FileHash(const std::wstring& filepath) : FileHash(filepath) { };

+#else

+	using FileHash::FileHash;

+#endif

+private:

+	void Initialize();

+	void Update(const UINT32 bytecount);

+	void Finalize();

+	void Transform(UINT32 state[5], PUINT32 buffer);

+	void ConvertHashToDigestString();

+	BYTE hash_[20];

+	SHA1Context context_;

+};

+

+#endif /* SHA1FILEHASH_H_ */

diff --git a/tests/resources/issue_3020/StreamLineReader.cpp b/tests/resources/issue_3020/StreamLineReader.cpp
new file mode 100644
index 0000000..fcdcac4
--- /dev/null
+++ b/tests/resources/issue_3020/StreamLineReader.cpp
@@ -0,0 +1,78 @@
+/* Author: macote */

+

+#include "StreamLineReader.h"

+

+void StreamLineReader::AllocateBuffer()

+{

+	buffer_ = (PBYTE)VirtualAlloc(NULL, buffersize_, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

+}

+

+void StreamLineReader::FreeBuffer()

+{

+	if (buffer_ != NULL)

+	{

+		VirtualFree(buffer_, 0, MEM_RELEASE);

+	}

+}

+

+DWORD StreamLineReader::ReadBytes()

+{

+	if (readlength_ - readindex_ == 0)

+	{

+		readindex_ = 0;

+		readlength_ = filestream_.Read(buffer_, buffersize_);

+	}

+	return readlength_;

+}

+

+std::wstring StreamLineReader::ReadLine()

+{

+	BOOL eol = FALSE;

+	std::string raw;

+	while (!eol)

+	{

+		DWORD bufferbytes = readlength_ - readindex_;

+		if (bufferbytes == 0)

+		{

+			bufferbytes = ReadBytes();

+			if (bufferbytes == 0) break;

+		}

+		char* p = (char *)buffer_ + readindex_;

+		while (*p != '\r' && *p != '\n' && p <= (char *)buffer_ + readindex_)

+		{

+			raw.append(p, 1);

+			readindex_++;

+			if (readlength_ - readindex_ == 0) break;

+			p++;

+		}

+		if (*p == '\r' || *p == '\n')

+		{

+			eol = TRUE;

+			readindex_++;

+			if (*p == '\r' && (readlength_ - readindex_ > 0) && *(p + 1) == '\n')

+			{

+				readindex_++;

+			}

+		}

+	}

+	if (raw.size() > 0)

+	{

+		DWORD cchWideChar = MultiByteToWideChar(CP_UTF8, 0, raw.c_str(), -1, NULL, 0);

+		WCHAR* wideChars = (WCHAR *)HeapAlloc(GetProcessHeap(), 0, cchWideChar * sizeof(WCHAR));

+		cchWideChar = MultiByteToWideChar(CP_UTF8, 0, raw.c_str(), -1, wideChars, cchWideChar);

+		std::wstring line(wideChars);

+		HeapFree(GetProcessHeap(), 0, wideChars);

+		return line;

+	}

+	return L"";

+}

+

+BOOL StreamLineReader::EndOfStream()

+{

+	return ReadBytes() == 0;

+}

+

+void StreamLineReader::Close()

+{

+	filestream_.Close();

+}
\ No newline at end of file
diff --git a/tests/resources/issue_3020/StreamLineReader.h b/tests/resources/issue_3020/StreamLineReader.h
new file mode 100644
index 0000000..1312b84
--- /dev/null
+++ b/tests/resources/issue_3020/StreamLineReader.h
@@ -0,0 +1,44 @@
+/* Author: macote */

+

+#ifndef STREAMLINEREADER_H_

+#define STREAMLINEREADER_H_

+

+#include "FileStream.h"

+#include <string>

+#include <Windows.h>

+

+class StreamLineReader

+{

+private:

+	static const DWORD kDefaultBufferSize = 32768;

+public:

+	enum class Encoding

+	{

+		UTF8

+	};

+	StreamLineReader(FileStream& filestream, Encoding encoding, const DWORD buffersize) : filestream_(filestream), encoding_(encoding), buffersize_(buffersize)

+	{

+		AllocateBuffer();

+	}

+	StreamLineReader(FileStream& filestream, Encoding encoding) : StreamLineReader(filestream, Encoding::UTF8, kDefaultBufferSize) { }

+	StreamLineReader(FileStream& filestream) : StreamLineReader(filestream, Encoding::UTF8) { }

+	~StreamLineReader()

+	{

+		FreeBuffer();

+	}

+	std::wstring ReadLine();

+	BOOL EndOfStream();

+	void Close();

+private:

+	void AllocateBuffer();

+	void FreeBuffer();

+	DWORD ReadBytes();

+	FileStream& filestream_;

+	Encoding encoding_;

+	PBYTE buffer_;

+	DWORD buffersize_;

+	DWORD readindex_ = 0;

+	DWORD readlength_ = 0;

+};

+

+#endif /* STREAMLINEREADER_H_ */

diff --git a/tests/resources/issue_3020/StreamLineWriter.cpp b/tests/resources/issue_3020/StreamLineWriter.cpp
new file mode 100644
index 0000000..6fd1a32
--- /dev/null
+++ b/tests/resources/issue_3020/StreamLineWriter.cpp
@@ -0,0 +1,31 @@
+/* Author: macote */

+

+#include "StreamLineWriter.h"

+

+void StreamLineWriter::Write(std::wstring line)

+{

+	if (line.size() > 0)

+	{

+		DWORD cbMultiByte = WideCharToMultiByte(CP_UTF8, 0, line.c_str(), -1, NULL, 0, NULL, NULL);

+		LPSTR bytes = (LPSTR)HeapAlloc(GetProcessHeap(), 0, cbMultiByte);

+		cbMultiByte = WideCharToMultiByte(CP_UTF8, 0, line.c_str(), -1, bytes, cbMultiByte, NULL, NULL);

+		filestream_.Write((PBYTE)bytes, cbMultiByte - 1);

+		HeapFree(GetProcessHeap(), 0, bytes);

+	}

+}

+

+void StreamLineWriter::WriteLine(std::wstring line)

+{

+	Write(line);

+	WriteEOL();

+}

+

+void StreamLineWriter::WriteEOL()

+{

+	filestream_.Write((PBYTE)"\r\n", 2);

+}

+

+void StreamLineWriter::Close()

+{

+	filestream_.Close();

+}
\ No newline at end of file
diff --git a/tests/resources/issue_3020/StreamLineWriter.h b/tests/resources/issue_3020/StreamLineWriter.h
new file mode 100644
index 0000000..310d4ec
--- /dev/null
+++ b/tests/resources/issue_3020/StreamLineWriter.h
@@ -0,0 +1,32 @@
+/* Author: macote */

+

+#ifndef STREAMLINEWRITER_H_

+#define STREAMLINEWRITER_H_

+

+#include "FileStream.h"

+#include <string>

+#include <Windows.h>

+

+class StreamLineWriter

+{

+public:

+	enum class Encoding

+	{

+		UTF8

+	};

+	StreamLineWriter(FileStream& filestream, Encoding encoding) : filestream_(filestream), encoding_(encoding) { }

+	StreamLineWriter(FileStream& filestream) : StreamLineWriter(filestream, Encoding::UTF8) { }

+	~StreamLineWriter()

+	{

+		Close();

+	}

+	void Write(std::wstring line);

+	void WriteLine(std::wstring line);

+	void Close();

+private:

+	void WriteEOL();

+	FileStream& filestream_;

+	Encoding encoding_;

+};

+

+#endif /* STREAMLINEWRITER_H_ */

diff --git a/tests/resources/issue_3020/Window.cpp b/tests/resources/issue_3020/Window.cpp
new file mode 100644
index 0000000..b5da400
--- /dev/null
+++ b/tests/resources/issue_3020/Window.cpp
@@ -0,0 +1,79 @@
+/* Author: macote */

+

+#include "Window.h"

+

+void Window::Register()

+{

+	WNDCLASS wc;

+	wc.style = 0;

+	wc.lpfnWndProc = Window::WndProc;

+	wc.cbClsExtra = 0;

+	wc.cbWndExtra = 0;

+	wc.hInstance = hinst_;

+	wc.hIcon = NULL;

+	wc.hCursor = LoadCursorW(NULL, IDC_ARROW);

+	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

+	wc.lpszMenuName = NULL;

+	wc.lpszClassName = ClassName();

+	WinRegisterClass(&wc);

+}

+

+LRESULT CALLBACK Window::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

+{

+	Window *self;

+	if (uMsg == WM_NCCREATE)

+	{

+		auto lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);

+		self = reinterpret_cast<Window *>(lpcs->lpCreateParams);

+		self->hwnd_ = hwnd;

+		SetWindowLongPtrW(hwnd, GWLP_USERDATA, reinterpret_cast<LPARAM>(self));

+	}

+	else

+	{

+		self = reinterpret_cast<Window *>(GetWindowLongPtrW(hwnd, GWLP_USERDATA));

+	}

+	if (self)

+	{

+		return self->HandleMessage(uMsg, wParam, lParam);

+	}

+	else

+	{

+		return DefWindowProcW(hwnd, uMsg, wParam, lParam);

+	}

+}

+

+LRESULT Window::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)

+{

+	LRESULT lres;

+	switch (uMsg)

+	{

+	case WM_NCDESTROY:

+		lres = DefWindowProcW(hwnd_, uMsg, wParam, lParam);

+		SetWindowLongPtrW(hwnd_, GWLP_USERDATA, 0);

+		delete this;

+		return lres;

+	case WM_PAINT:

+		OnPaint();

+		return 0;

+	case WM_PRINTCLIENT:

+		OnPrintClient(reinterpret_cast<HDC>(wParam));

+		return 0;

+	}

+	return DefWindowProcW(hwnd_, uMsg, wParam, lParam);

+}

+

+void Window::OnPaint()

+{

+	PAINTSTRUCT ps;

+	BeginPaint(hwnd_, &ps);

+	PaintContent(&ps);

+	EndPaint(hwnd_, &ps);

+}

+

+void Window::OnPrintClient(HDC hdc)

+{

+	PAINTSTRUCT ps;

+	ps.hdc = hdc;

+	GetClientRect(hwnd_, &ps.rcPaint);

+	PaintContent(&ps);

+}
\ No newline at end of file
diff --git a/tests/resources/issue_3020/Window.h b/tests/resources/issue_3020/Window.h
new file mode 100644
index 0000000..296246e
--- /dev/null
+++ b/tests/resources/issue_3020/Window.h
@@ -0,0 +1,38 @@
+/* Author: macote */

+

+#ifndef WINDOW_H_

+#define WINDOW_H_

+

+#include <Windows.h>

+

+class Window

+{

+public:

+	HWND GetHWND() const { return hwnd_; }

+	Window(HINSTANCE hinst) : hinst_(hinst) { }

+protected:

+	virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);

+	virtual void PaintContent(PAINTSTRUCT *pps) { }

+	virtual LPCWSTR ClassName() = 0;

+	virtual BOOL WinRegisterClass(WNDCLASS *pwc)

+	{

+		return RegisterClassW(pwc);

+	}

+	virtual ~Window() { }

+	HWND WinCreateWindow(DWORD dwExStyle, LPCWSTR pszName, DWORD dwStyle, 

+		int x, int y, int cx, int cy, HWND hwndParent, HMENU hmenu)

+	{

+		Register();

+		return CreateWindowExW(dwExStyle, ClassName(), pszName, dwStyle,

+			x, y, cx, cy, hwndParent, hmenu, hinst_, this);

+	}

+	HWND hwnd_;

+	HINSTANCE hinst_;

+private:

+	void Register();

+	void OnPaint();

+	void OnPrintClient(HDC hdc);

+	static LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

+};

+

+#endif /* WINDOW_H_ */
\ No newline at end of file
diff --git a/tests/resources/issue_3020/application.manifest b/tests/resources/issue_3020/application.manifest
new file mode 100644
index 0000000..7f607eb
--- /dev/null
+++ b/tests/resources/issue_3020/application.manifest
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">

+  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">

+    <security>

+      <requestedPrivileges>

+        <requestedExecutionLevel level="asInvoker" uiAccess="false"/>

+      </requestedPrivileges>

+    </security>

+  </trustInfo>

+  <dependency>

+    <dependentAssembly>

+      <assemblyIdentity type="Win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0"

+                        processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>

+    </dependentAssembly>

+  </dependency>

+</assembly>
\ No newline at end of file
diff --git a/tests/resources/issue_3020/resource.h b/tests/resources/issue_3020/resource.h
new file mode 100644
index 0000000..b7c1818
--- /dev/null
+++ b/tests/resources/issue_3020/resource.h
@@ -0,0 +1,8 @@
+/* Author: macote */

+

+#ifndef RESOURCE_H_

+#define RESOURCE_H_

+

+#define IDI_ICON1 101

+

+#endif /* RESOURCE_H_ */

diff --git a/tests/resources/issue_3020/resource.rc b/tests/resources/issue_3020/resource.rc
new file mode 100644
index 0000000..aedff65
--- /dev/null
+++ b/tests/resources/issue_3020/resource.rc
@@ -0,0 +1,6 @@
+#include "resource.h"

+

+IDI_ICON1 ICON "HashCheck.ico"

+

+CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "application.manifest"

+