зеркало из https://github.com/microsoft/scalar.git
Delete all virtualization-specific code
* [X] Builds with BuildGVFSforWindows.bat * [X] Passes unit tests with RunUnitTests.bat Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
This commit is contained in:
Родитель
1d9b7ae509
Коммит
d748084d85
|
@ -26,9 +26,7 @@ steps:
|
|||
|
||||
- bash: |
|
||||
chmod +x $(Build.BinariesDirectory)/FunctionalTests_$(platformFriendlyName)_$(configuration)/src/Scripts/Mac/*.sh
|
||||
chmod +x $(Build.BinariesDirectory)/FunctionalTests_$(platformFriendlyName)_$(configuration)/src/ProjFS.Mac/Scripts/*.sh
|
||||
chmod +x $(Build.BinariesDirectory)/FunctionalTests_$(platformFriendlyName)_$(configuration)/Publish/*
|
||||
chmod +x $(Build.BinariesDirectory)/FunctionalTests_$(platformFriendlyName)_$(configuration)/BuildOutput/ProjFS.Mac/Native/$(configuration)/prjfs-log
|
||||
displayName: Ensure tests assets are executable
|
||||
|
||||
- bash: $(Build.BinariesDirectory)/FunctionalTests_$(platformFriendlyName)_$(configuration)/src/Scripts/Mac/CleanupFunctionalTests.sh
|
||||
|
@ -37,11 +35,6 @@ steps:
|
|||
- bash: $(Build.BinariesDirectory)/FunctionalTests_$(platformFriendlyName)_$(configuration)/src/Scripts/Mac/PrepFunctionalTests.sh
|
||||
displayName: Prep functional tests
|
||||
|
||||
- bash: |
|
||||
$BUILD_BINARIESDIRECTORY/FunctionalTests_$(platformFriendlyName)_$(configuration)/src/ProjFS.Mac/Scripts/LoadPrjFSKext.sh $(configuration)
|
||||
$BUILD_BINARIESDIRECTORY/FunctionalTests_$(platformFriendlyName)_$(configuration)/BuildOutput/ProjFS.Mac/Native/$(configuration)/prjfs-log > $BUILD_ARTIFACTSTAGINGDIRECTORY/kext.log 2>&1 &
|
||||
displayName: Enable kext logging
|
||||
|
||||
- bash: $(Build.BinariesDirectory)/FunctionalTests_$(platformFriendlyName)_$(configuration)/src/Scripts/Mac/RunFunctionalTests.sh $(configuration)
|
||||
displayName: Run functional tests
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ steps:
|
|||
- script: git config --global credential.interactive never
|
||||
displayName: Disable interactive auth
|
||||
|
||||
- script: $(Build.BinariesDirectory)/FunctionalTests_$(platformFriendlyName)_$(configuration)/src/Scripts/RunFunctionalTests.bat $(configuration) --test-gvfs-on-path --replace-inbox-projfs
|
||||
- script: $(Build.BinariesDirectory)/FunctionalTests_$(platformFriendlyName)_$(configuration)/src/Scripts/RunFunctionalTests.bat $(configuration) --test-gvfs-on-path
|
||||
displayName: Run functional tests
|
||||
|
||||
- task: PublishTestResults@2
|
||||
|
|
|
@ -226,6 +226,3 @@ ModelManifest.xml
|
|||
|
||||
# VS Code private directory
|
||||
.vscode/
|
||||
|
||||
# ProjFS Kext Unit Test coverage results
|
||||
ProjFS.Mac/CoverageResult.txt
|
|
@ -41,7 +41,7 @@ The design review process is as follows:
|
|||
|
||||
- *Platform specific code, and only platform specific code, should go in `GVFSPlatform`*
|
||||
|
||||
When platform specific code is required, it should be placed in `GVFSPlatform` or one of the platforms it contains (e.g. `IKernelDriver`)
|
||||
When platform specific code is required, it should be placed in `GVFSPlatform` or one of the platforms it contains.
|
||||
|
||||
## Tracing and Logging
|
||||
|
||||
|
|
133
GVFS.sln
133
GVFS.sln
|
@ -19,18 +19,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GVFS", "GVFS", "{2EF2EC94-3
|
|||
GVFS\ProjectedFSLib.NativeBinaries.props = GVFS\ProjectedFSLib.NativeBinaries.props
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.GVFlt", "GVFS\GVFS.GVFlt\GVFS.GVFlt.csproj", "{1118B427-7063-422F-83B9-5023C8EC5A7A}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{A4984251-840E-4622-AD0C-66DFCE2B2574} = {A4984251-840E-4622-AD0C-66DFCE2B2574}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Common", "GVFS\GVFS.Common\GVFS.Common.csproj", "{374BF1E5-0B2D-4D4A-BD5E-4212299DEF09}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{A4984251-840E-4622-AD0C-66DFCE2B2574} = {A4984251-840E-4622-AD0C-66DFCE2B2574}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastFetch", "GVFS\FastFetch\FastFetch.csproj", "{07F2A520-2AB7-46DD-97C0-75D8E988D55B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Tests", "GVFS\GVFS.Tests\GVFS.Tests.csproj", "{72701BC3-5DA9-4C7A-BF10-9E98C9FC8EAC}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{A4984251-840E-4622-AD0C-66DFCE2B2574} = {A4984251-840E-4622-AD0C-66DFCE2B2574}
|
||||
|
@ -40,11 +33,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GVFS Tests", "GVFS Tests",
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.UnitTests.Windows", "GVFS\GVFS.UnitTests.Windows\GVFS.UnitTests.Windows.csproj", "{8E0D0989-21F6-4DD8-946C-39F992523CC6}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GVFS.NativeTests", "GVFS\GVFS.NativeTests\GVFS.NativeTests.vcxproj", "{3771C555-B5C1-45E2-B8B7-2CEF1619CDC5}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{A4984251-840E-4622-AD0C-66DFCE2B2574} = {A4984251-840E-4622-AD0C-66DFCE2B2574}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.Hooks.Windows", "GVFS\GVFS.Hooks\GVFS.Hooks.Windows.csproj", "{BDA91EE5-C684-4FC5-A90A-B7D677421917}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{A4984251-840E-4622-AD0C-66DFCE2B2574} = {A4984251-840E-4622-AD0C-66DFCE2B2574}
|
||||
|
@ -52,10 +40,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.Hooks.Windows", "GVFS\
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.Service.Windows", "GVFS\GVFS.Service\GVFS.Service.Windows.csproj", "{B8C1DFBA-CAFD-4F7E-A1A3-E11907B5467B}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{2D23AB54-541F-4ABC-8DCA-08C199E97ABB} = {2D23AB54-541F-4ABC-8DCA-08C199E97ABB}
|
||||
{5A6656D5-81C7-472C-9DC8-32D071CB2258} = {5A6656D5-81C7-472C-9DC8-32D071CB2258}
|
||||
{BDA91EE5-C684-4FC5-A90A-B7D677421917} = {BDA91EE5-C684-4FC5-A90A-B7D677421917}
|
||||
{24D161E9-D1F0-4299-BBD3-5D940BEDD535} = {24D161E9-D1F0-4299-BBD3-5D940BEDD535}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Service.Mac", "GVFS\GVFS.Service\GVFS.Service.Mac.csproj", "{03769A07-F216-456B-886B-E07CAF6C5E81}"
|
||||
|
@ -74,8 +60,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scripts", "Scripts", "{2867
|
|||
Scripts\RunUnitTests.bat = Scripts\RunUnitTests.bat
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.PerfProfiling", "GVFS\GVFS.PerfProfiling\GVFS.PerfProfiling.csproj", "{C5D3CA26-562F-4CA4-A378-B93E97A730E3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.Service.UI", "GVFS\GVFS.Service.UI\GVFS.Service.UI.csproj", "{93B403FD-DAFB-46C5-9636-B122792A548A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.PreBuild", "GVFS\GVFS.Build\GVFS.PreBuild.csproj", "{A4984251-840E-4622-AD0C-66DFCE2B2574}"
|
||||
|
@ -98,9 +82,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Installer.Mac", "GVFS\
|
|||
{4CC2A90D-D240-4382-B4BF-5E175515E492} = {4CC2A90D-D240-4382-B4BF-5E175515E492}
|
||||
{28939122-7263-41E7-A7E2-CBFB01AD6A04} = {28939122-7263-41E7-A7E2-CBFB01AD6A04}
|
||||
{A4984251-840E-4622-AD0C-66DFCE2B2574} = {A4984251-840E-4622-AD0C-66DFCE2B2574}
|
||||
{F468B05A-95E5-46BC-8C67-B80A78527B7D} = {F468B05A-95E5-46BC-8C67-B80A78527B7D}
|
||||
{1DAC3DA6-3D21-4917-B9A8-D60C8712252A} = {1DAC3DA6-3D21-4917-B9A8-D60C8712252A}
|
||||
{FAC6EFC5-A890-4CB2-8C80-6358E358C637} = {FAC6EFC5-A890-4CB2-8C80-6358E358C637}
|
||||
{374BF1E5-0B2D-4D4A-BD5E-4212299DEF09} = {374BF1E5-0B2D-4D4A-BD5E-4212299DEF09}
|
||||
{35CA4DFB-1320-4055-B8F6-F12E0F252FF0} = {35CA4DFB-1320-4055-B8F6-F12E0F252FF0}
|
||||
{AECEC217-2499-403D-B0BB-2962B9BE5970} = {AECEC217-2499-403D-B0BB-2962B9BE5970}
|
||||
|
@ -109,9 +91,6 @@ EndProject
|
|||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.SignFiles", "GVFS\GVFS.SignFiles\GVFS.SignFiles.csproj", "{2F63B22B-EE26-4266-BF17-28A9146483A1}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{17498502-AEFF-4E70-90CC-1D0B56A8ADF5} = {17498502-AEFF-4E70-90CC-1D0B56A8ADF5}
|
||||
{1118B427-7063-422F-83B9-5023C8EC5A7A} = {1118B427-7063-422F-83B9-5023C8EC5A7A}
|
||||
{2D23AB54-541F-4ABC-8DCA-08C199E97ABB} = {2D23AB54-541F-4ABC-8DCA-08C199E97ABB}
|
||||
{F468B05A-95E5-46BC-8C67-B80A78527B7D} = {F468B05A-95E5-46BC-8C67-B80A78527B7D}
|
||||
{32220664-594C-4425-B9A0-88E0BE2F3D2A} = {32220664-594C-4425-B9A0-88E0BE2F3D2A}
|
||||
{798DE293-6EDA-4DC4-9395-BE7A71C563E3} = {798DE293-6EDA-4DC4-9395-BE7A71C563E3}
|
||||
{B8C1DFBA-CAFD-4F7E-A1A3-E11907B5467B} = {B8C1DFBA-CAFD-4F7E-A1A3-E11907B5467B}
|
||||
|
@ -119,20 +98,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.SignFiles", "GVFS\GVFS
|
|||
{BDA91EE5-C684-4FC5-A90A-B7D677421917} = {BDA91EE5-C684-4FC5-A90A-B7D677421917}
|
||||
{374BF1E5-0B2D-4D4A-BD5E-4212299DEF09} = {374BF1E5-0B2D-4D4A-BD5E-4212299DEF09}
|
||||
{4CE404E7-D3FC-471C-993C-64615861EA63} = {4CE404E7-D3FC-471C-993C-64615861EA63}
|
||||
{24D161E9-D1F0-4299-BBD3-5D940BEDD535} = {24D161E9-D1F0-4299-BBD3-5D940BEDD535}
|
||||
{93B403FD-DAFB-46C5-9636-B122792A548A} = {93B403FD-DAFB-46C5-9636-B122792A548A}
|
||||
{AECEC217-2499-403D-B0BB-2962B9BE5970} = {AECEC217-2499-403D-B0BB-2962B9BE5970}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GVFS.VirtualFileSystemHook.Windows", "GVFS\GVFS.VirtualFileSystemHook\GVFS.VirtualFileSystemHook.Windows.vcxproj", "{2D23AB54-541F-4ABC-8DCA-08C199E97ABB}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{A4984251-840E-4622-AD0C-66DFCE2B2574} = {A4984251-840E-4622-AD0C-66DFCE2B2574}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Virtualization", "GVFS\GVFS.Virtualization\GVFS.Virtualization.csproj", "{F468B05A-95E5-46BC-8C67-B80A78527B7D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PrjFSLib.Mac.Managed", "ProjFS.Mac\PrjFSLib.Mac.Managed\PrjFSLib.Mac.Managed.csproj", "{FAC6EFC5-A890-4CB2-8C80-6358E358C637}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Platform.Mac", "GVFS\GVFS.Platform.Mac\GVFS.Platform.Mac.csproj", "{1DAC3DA6-3D21-4917-B9A8-D60C8712252A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Platform.POSIX", "GVFS\GVFS.Platform.POSIX\GVFS.Platform.POSIX.csproj", "{15FAE44C-0D21-4312-9FD3-28F05A5AB7A6}"
|
||||
|
@ -145,48 +114,29 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Mac", "GVFS\GVFS\GVFS.
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.Windows", "GVFS\GVFS\GVFS.Windows.csproj", "{32220664-594C-4425-B9A0-88E0BE2F3D2A}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{2D23AB54-541F-4ABC-8DCA-08C199E97ABB} = {2D23AB54-541F-4ABC-8DCA-08C199E97ABB}
|
||||
{798DE293-6EDA-4DC4-9395-BE7A71C563E3} = {798DE293-6EDA-4DC4-9395-BE7A71C563E3}
|
||||
{5A6656D5-81C7-472C-9DC8-32D071CB2258} = {5A6656D5-81C7-472C-9DC8-32D071CB2258}
|
||||
{BDA91EE5-C684-4FC5-A90A-B7D677421917} = {BDA91EE5-C684-4FC5-A90A-B7D677421917}
|
||||
{24D161E9-D1F0-4299-BBD3-5D940BEDD535} = {24D161E9-D1F0-4299-BBD3-5D940BEDD535}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.Mount.Windows", "GVFS\GVFS.Mount\GVFS.Mount.Windows.csproj", "{17498502-AEFF-4E70-90CC-1D0B56A8ADF5}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{2D23AB54-541F-4ABC-8DCA-08C199E97ABB} = {2D23AB54-541F-4ABC-8DCA-08C199E97ABB}
|
||||
{5A6656D5-81C7-472C-9DC8-32D071CB2258} = {5A6656D5-81C7-472C-9DC8-32D071CB2258}
|
||||
{BDA91EE5-C684-4FC5-A90A-B7D677421917} = {BDA91EE5-C684-4FC5-A90A-B7D677421917}
|
||||
{24D161E9-D1F0-4299-BBD3-5D940BEDD535} = {24D161E9-D1F0-4299-BBD3-5D940BEDD535}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Mount.Mac", "GVFS\GVFS.Mount\GVFS.Mount.Mac.csproj", "{35CA4DFB-1320-4055-B8F6-F12E0F252FF0}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.FunctionalTests.Windows", "GVFS\GVFS.FunctionalTests.Windows\GVFS.FunctionalTests.Windows.csproj", "{0F0A008E-AB12-40EC-A671-37A541B08C7F}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{17498502-AEFF-4E70-90CC-1D0B56A8ADF5} = {17498502-AEFF-4E70-90CC-1D0B56A8ADF5}
|
||||
{07F2A520-2AB7-46DD-97C0-75D8E988D55B} = {07F2A520-2AB7-46DD-97C0-75D8E988D55B}
|
||||
{3771C555-B5C1-45E2-B8B7-2CEF1619CDC5} = {3771C555-B5C1-45E2-B8B7-2CEF1619CDC5}
|
||||
{FA273F69-5762-43D8-AEA1-B4F08090D624} = {FA273F69-5762-43D8-AEA1-B4F08090D624}
|
||||
{B8C1DFBA-CAFD-4F7E-A1A3-E11907B5467B} = {B8C1DFBA-CAFD-4F7E-A1A3-E11907B5467B}
|
||||
{BDA91EE5-C684-4FC5-A90A-B7D677421917} = {BDA91EE5-C684-4FC5-A90A-B7D677421917}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.FunctionalTests", "GVFS\GVFS.FunctionalTests\GVFS.FunctionalTests.csproj", "{BD7C5776-82F2-40C6-AF01-B3CC1E2D83AF}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{17498502-AEFF-4E70-90CC-1D0B56A8ADF5} = {17498502-AEFF-4E70-90CC-1D0B56A8ADF5}
|
||||
{07F2A520-2AB7-46DD-97C0-75D8E988D55B} = {07F2A520-2AB7-46DD-97C0-75D8E988D55B}
|
||||
{28939122-7263-41E7-A7E2-CBFB01AD6A04} = {28939122-7263-41E7-A7E2-CBFB01AD6A04}
|
||||
{3771C555-B5C1-45E2-B8B7-2CEF1619CDC5} = {3771C555-B5C1-45E2-B8B7-2CEF1619CDC5}
|
||||
{32220664-594C-4425-B9A0-88E0BE2F3D2A} = {32220664-594C-4425-B9A0-88E0BE2F3D2A}
|
||||
{FA273F69-5762-43D8-AEA1-B4F08090D624} = {FA273F69-5762-43D8-AEA1-B4F08090D624}
|
||||
{B8C1DFBA-CAFD-4F7E-A1A3-E11907B5467B} = {B8C1DFBA-CAFD-4F7E-A1A3-E11907B5467B}
|
||||
{BDA91EE5-C684-4FC5-A90A-B7D677421917} = {BDA91EE5-C684-4FC5-A90A-B7D677421917}
|
||||
{35CA4DFB-1320-4055-B8F6-F12E0F252FF0} = {35CA4DFB-1320-4055-B8F6-F12E0F252FF0}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.FunctionalTests.LockHolder", "GVFS\GVFS.FunctionalTests.LockHolder\GVFS.FunctionalTests.LockHolder.csproj", "{FA273F69-5762-43D8-AEA1-B4F08090D624}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Hooks.Mac", "GVFS\GVFS.Hooks\GVFS.Hooks.Mac.csproj", "{4CC2A90D-D240-4382-B4BF-5E175515E492}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{A4984251-840E-4622-AD0C-66DFCE2B2574} = {A4984251-840E-4622-AD0C-66DFCE2B2574}
|
||||
|
@ -194,11 +144,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Hooks.Mac", "GVFS\GVFS
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Upgrader", "GVFS\GVFS.Upgrader\GVFS.Upgrader.csproj", "{AECEC217-2499-403D-B0BB-2962B9BE5970}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GVFS.PostIndexChangedHook.Windows", "GVFS\GVFS.PostIndexChangedHook\GVFS.PostIndexChangedHook.Windows.vcxproj", "{24D161E9-D1F0-4299-BBD3-5D940BEDD535}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{A4984251-840E-4622-AD0C-66DFCE2B2574} = {A4984251-840E-4622-AD0C-66DFCE2B2574}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug.Mac|x64 = Debug.Mac|x64
|
||||
|
@ -207,12 +152,6 @@ Global
|
|||
Release.Windows|x64 = Release.Windows|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{1118B427-7063-422F-83B9-5023C8EC5A7A}.Debug.Mac|x64.ActiveCfg = Debug|x64
|
||||
{1118B427-7063-422F-83B9-5023C8EC5A7A}.Debug.Windows|x64.ActiveCfg = Debug|x64
|
||||
{1118B427-7063-422F-83B9-5023C8EC5A7A}.Debug.Windows|x64.Build.0 = Debug|x64
|
||||
{1118B427-7063-422F-83B9-5023C8EC5A7A}.Release.Mac|x64.ActiveCfg = Release|x64
|
||||
{1118B427-7063-422F-83B9-5023C8EC5A7A}.Release.Windows|x64.ActiveCfg = Release|x64
|
||||
{1118B427-7063-422F-83B9-5023C8EC5A7A}.Release.Windows|x64.Build.0 = Release|x64
|
||||
{374BF1E5-0B2D-4D4A-BD5E-4212299DEF09}.Debug.Mac|x64.ActiveCfg = Debug|x64
|
||||
{374BF1E5-0B2D-4D4A-BD5E-4212299DEF09}.Debug.Mac|x64.Build.0 = Debug|x64
|
||||
{374BF1E5-0B2D-4D4A-BD5E-4212299DEF09}.Debug.Windows|x64.ActiveCfg = Debug|x64
|
||||
|
@ -221,14 +160,6 @@ Global
|
|||
{374BF1E5-0B2D-4D4A-BD5E-4212299DEF09}.Release.Mac|x64.Build.0 = Release|x64
|
||||
{374BF1E5-0B2D-4D4A-BD5E-4212299DEF09}.Release.Windows|x64.ActiveCfg = Release|x64
|
||||
{374BF1E5-0B2D-4D4A-BD5E-4212299DEF09}.Release.Windows|x64.Build.0 = Release|x64
|
||||
{07F2A520-2AB7-46DD-97C0-75D8E988D55B}.Debug.Mac|x64.ActiveCfg = Debug|x64
|
||||
{07F2A520-2AB7-46DD-97C0-75D8E988D55B}.Debug.Mac|x64.Build.0 = Debug|x64
|
||||
{07F2A520-2AB7-46DD-97C0-75D8E988D55B}.Debug.Windows|x64.ActiveCfg = Debug|x64
|
||||
{07F2A520-2AB7-46DD-97C0-75D8E988D55B}.Debug.Windows|x64.Build.0 = Debug|x64
|
||||
{07F2A520-2AB7-46DD-97C0-75D8E988D55B}.Release.Mac|x64.ActiveCfg = Release|x64
|
||||
{07F2A520-2AB7-46DD-97C0-75D8E988D55B}.Release.Mac|x64.Build.0 = Release|x64
|
||||
{07F2A520-2AB7-46DD-97C0-75D8E988D55B}.Release.Windows|x64.ActiveCfg = Release|x64
|
||||
{07F2A520-2AB7-46DD-97C0-75D8E988D55B}.Release.Windows|x64.Build.0 = Release|x64
|
||||
{72701BC3-5DA9-4C7A-BF10-9E98C9FC8EAC}.Debug.Mac|x64.ActiveCfg = Debug|x64
|
||||
{72701BC3-5DA9-4C7A-BF10-9E98C9FC8EAC}.Debug.Mac|x64.Build.0 = Debug|x64
|
||||
{72701BC3-5DA9-4C7A-BF10-9E98C9FC8EAC}.Debug.Windows|x64.ActiveCfg = Debug|x64
|
||||
|
@ -243,12 +174,6 @@ Global
|
|||
{8E0D0989-21F6-4DD8-946C-39F992523CC6}.Release.Mac|x64.ActiveCfg = Release|x64
|
||||
{8E0D0989-21F6-4DD8-946C-39F992523CC6}.Release.Windows|x64.ActiveCfg = Release|x64
|
||||
{8E0D0989-21F6-4DD8-946C-39F992523CC6}.Release.Windows|x64.Build.0 = Release|x64
|
||||
{3771C555-B5C1-45E2-B8B7-2CEF1619CDC5}.Debug.Mac|x64.ActiveCfg = Debug|x64
|
||||
{3771C555-B5C1-45E2-B8B7-2CEF1619CDC5}.Debug.Windows|x64.ActiveCfg = Debug|x64
|
||||
{3771C555-B5C1-45E2-B8B7-2CEF1619CDC5}.Debug.Windows|x64.Build.0 = Debug|x64
|
||||
{3771C555-B5C1-45E2-B8B7-2CEF1619CDC5}.Release.Mac|x64.ActiveCfg = Release|x64
|
||||
{3771C555-B5C1-45E2-B8B7-2CEF1619CDC5}.Release.Windows|x64.ActiveCfg = Release|x64
|
||||
{3771C555-B5C1-45E2-B8B7-2CEF1619CDC5}.Release.Windows|x64.Build.0 = Release|x64
|
||||
{BDA91EE5-C684-4FC5-A90A-B7D677421917}.Debug.Mac|x64.ActiveCfg = Debug|x64
|
||||
{BDA91EE5-C684-4FC5-A90A-B7D677421917}.Debug.Windows|x64.ActiveCfg = Debug|x64
|
||||
{BDA91EE5-C684-4FC5-A90A-B7D677421917}.Debug.Windows|x64.Build.0 = Debug|x64
|
||||
|
@ -275,12 +200,6 @@ Global
|
|||
{5A6656D5-81C7-472C-9DC8-32D071CB2258}.Release.Mac|x64.ActiveCfg = Release|x64
|
||||
{5A6656D5-81C7-472C-9DC8-32D071CB2258}.Release.Windows|x64.ActiveCfg = Release|x64
|
||||
{5A6656D5-81C7-472C-9DC8-32D071CB2258}.Release.Windows|x64.Build.0 = Release|x64
|
||||
{C5D3CA26-562F-4CA4-A378-B93E97A730E3}.Debug.Mac|x64.ActiveCfg = Debug|x64
|
||||
{C5D3CA26-562F-4CA4-A378-B93E97A730E3}.Debug.Windows|x64.ActiveCfg = Debug|x64
|
||||
{C5D3CA26-562F-4CA4-A378-B93E97A730E3}.Debug.Windows|x64.Build.0 = Debug|x64
|
||||
{C5D3CA26-562F-4CA4-A378-B93E97A730E3}.Release.Mac|x64.ActiveCfg = Release|x64
|
||||
{C5D3CA26-562F-4CA4-A378-B93E97A730E3}.Release.Windows|x64.ActiveCfg = Release|x64
|
||||
{C5D3CA26-562F-4CA4-A378-B93E97A730E3}.Release.Windows|x64.Build.0 = Release|x64
|
||||
{93B403FD-DAFB-46C5-9636-B122792A548A}.Debug.Mac|x64.ActiveCfg = Debug|x64
|
||||
{93B403FD-DAFB-46C5-9636-B122792A548A}.Debug.Windows|x64.ActiveCfg = Debug|x64
|
||||
{93B403FD-DAFB-46C5-9636-B122792A548A}.Debug.Windows|x64.Build.0 = Debug|x64
|
||||
|
@ -317,28 +236,6 @@ Global
|
|||
{2F63B22B-EE26-4266-BF17-28A9146483A1}.Release.Mac|x64.ActiveCfg = Release|x64
|
||||
{2F63B22B-EE26-4266-BF17-28A9146483A1}.Release.Windows|x64.ActiveCfg = Release|x64
|
||||
{2F63B22B-EE26-4266-BF17-28A9146483A1}.Release.Windows|x64.Build.0 = Release|x64
|
||||
{2D23AB54-541F-4ABC-8DCA-08C199E97ABB}.Debug.Mac|x64.ActiveCfg = Debug|x64
|
||||
{2D23AB54-541F-4ABC-8DCA-08C199E97ABB}.Debug.Windows|x64.ActiveCfg = Debug|x64
|
||||
{2D23AB54-541F-4ABC-8DCA-08C199E97ABB}.Debug.Windows|x64.Build.0 = Debug|x64
|
||||
{2D23AB54-541F-4ABC-8DCA-08C199E97ABB}.Release.Mac|x64.ActiveCfg = Release|x64
|
||||
{2D23AB54-541F-4ABC-8DCA-08C199E97ABB}.Release.Windows|x64.ActiveCfg = Release|x64
|
||||
{2D23AB54-541F-4ABC-8DCA-08C199E97ABB}.Release.Windows|x64.Build.0 = Release|x64
|
||||
{F468B05A-95E5-46BC-8C67-B80A78527B7D}.Debug.Mac|x64.ActiveCfg = Debug|x64
|
||||
{F468B05A-95E5-46BC-8C67-B80A78527B7D}.Debug.Mac|x64.Build.0 = Debug|x64
|
||||
{F468B05A-95E5-46BC-8C67-B80A78527B7D}.Debug.Windows|x64.ActiveCfg = Debug|x64
|
||||
{F468B05A-95E5-46BC-8C67-B80A78527B7D}.Debug.Windows|x64.Build.0 = Debug|x64
|
||||
{F468B05A-95E5-46BC-8C67-B80A78527B7D}.Release.Mac|x64.ActiveCfg = Release|x64
|
||||
{F468B05A-95E5-46BC-8C67-B80A78527B7D}.Release.Mac|x64.Build.0 = Release|x64
|
||||
{F468B05A-95E5-46BC-8C67-B80A78527B7D}.Release.Windows|x64.ActiveCfg = Release|x64
|
||||
{F468B05A-95E5-46BC-8C67-B80A78527B7D}.Release.Windows|x64.Build.0 = Release|x64
|
||||
{FAC6EFC5-A890-4CB2-8C80-6358E358C637}.Debug.Mac|x64.ActiveCfg = Debug|x64
|
||||
{FAC6EFC5-A890-4CB2-8C80-6358E358C637}.Debug.Mac|x64.Build.0 = Debug|x64
|
||||
{FAC6EFC5-A890-4CB2-8C80-6358E358C637}.Debug.Windows|x64.ActiveCfg = Debug|x64
|
||||
{FAC6EFC5-A890-4CB2-8C80-6358E358C637}.Debug.Windows|x64.Build.0 = Debug|x64
|
||||
{FAC6EFC5-A890-4CB2-8C80-6358E358C637}.Release.Mac|x64.ActiveCfg = Release|x64
|
||||
{FAC6EFC5-A890-4CB2-8C80-6358E358C637}.Release.Mac|x64.Build.0 = Release|x64
|
||||
{FAC6EFC5-A890-4CB2-8C80-6358E358C637}.Release.Windows|x64.ActiveCfg = Release|x64
|
||||
{FAC6EFC5-A890-4CB2-8C80-6358E358C637}.Release.Windows|x64.Build.0 = Release|x64
|
||||
{1DAC3DA6-3D21-4917-B9A8-D60C8712252A}.Debug.Mac|x64.ActiveCfg = Debug|x64
|
||||
{1DAC3DA6-3D21-4917-B9A8-D60C8712252A}.Debug.Mac|x64.Build.0 = Debug|x64
|
||||
{1DAC3DA6-3D21-4917-B9A8-D60C8712252A}.Debug.Windows|x64.ActiveCfg = Debug|x64
|
||||
|
@ -397,12 +294,6 @@ Global
|
|||
{35CA4DFB-1320-4055-B8F6-F12E0F252FF0}.Release.Mac|x64.Build.0 = Release|x64
|
||||
{35CA4DFB-1320-4055-B8F6-F12E0F252FF0}.Release.Windows|x64.ActiveCfg = Release|x64
|
||||
{35CA4DFB-1320-4055-B8F6-F12E0F252FF0}.Release.Windows|x64.Build.0 = Release|x64
|
||||
{0F0A008E-AB12-40EC-A671-37A541B08C7F}.Debug.Mac|x64.ActiveCfg = Debug|x64
|
||||
{0F0A008E-AB12-40EC-A671-37A541B08C7F}.Debug.Windows|x64.ActiveCfg = Debug|x64
|
||||
{0F0A008E-AB12-40EC-A671-37A541B08C7F}.Debug.Windows|x64.Build.0 = Debug|x64
|
||||
{0F0A008E-AB12-40EC-A671-37A541B08C7F}.Release.Mac|x64.ActiveCfg = Release|x64
|
||||
{0F0A008E-AB12-40EC-A671-37A541B08C7F}.Release.Windows|x64.ActiveCfg = Release|x64
|
||||
{0F0A008E-AB12-40EC-A671-37A541B08C7F}.Release.Windows|x64.Build.0 = Release|x64
|
||||
{BD7C5776-82F2-40C6-AF01-B3CC1E2D83AF}.Debug.Mac|x64.ActiveCfg = Debug|x64
|
||||
{BD7C5776-82F2-40C6-AF01-B3CC1E2D83AF}.Debug.Mac|x64.Build.0 = Debug|x64
|
||||
{BD7C5776-82F2-40C6-AF01-B3CC1E2D83AF}.Debug.Windows|x64.ActiveCfg = Debug|x64
|
||||
|
@ -411,14 +302,6 @@ Global
|
|||
{BD7C5776-82F2-40C6-AF01-B3CC1E2D83AF}.Release.Mac|x64.Build.0 = Release|x64
|
||||
{BD7C5776-82F2-40C6-AF01-B3CC1E2D83AF}.Release.Windows|x64.ActiveCfg = Release|x64
|
||||
{BD7C5776-82F2-40C6-AF01-B3CC1E2D83AF}.Release.Windows|x64.Build.0 = Release|x64
|
||||
{FA273F69-5762-43D8-AEA1-B4F08090D624}.Debug.Mac|x64.ActiveCfg = Debug|x64
|
||||
{FA273F69-5762-43D8-AEA1-B4F08090D624}.Debug.Mac|x64.Build.0 = Debug|x64
|
||||
{FA273F69-5762-43D8-AEA1-B4F08090D624}.Debug.Windows|x64.ActiveCfg = Debug|x64
|
||||
{FA273F69-5762-43D8-AEA1-B4F08090D624}.Debug.Windows|x64.Build.0 = Debug|x64
|
||||
{FA273F69-5762-43D8-AEA1-B4F08090D624}.Release.Mac|x64.ActiveCfg = Release|x64
|
||||
{FA273F69-5762-43D8-AEA1-B4F08090D624}.Release.Mac|x64.Build.0 = Release|x64
|
||||
{FA273F69-5762-43D8-AEA1-B4F08090D624}.Release.Windows|x64.ActiveCfg = Release|x64
|
||||
{FA273F69-5762-43D8-AEA1-B4F08090D624}.Release.Windows|x64.Build.0 = Release|x64
|
||||
{4CC2A90D-D240-4382-B4BF-5E175515E492}.Debug.Mac|x64.ActiveCfg = Debug|x64
|
||||
{4CC2A90D-D240-4382-B4BF-5E175515E492}.Debug.Mac|x64.Build.0 = Debug|x64
|
||||
{4CC2A90D-D240-4382-B4BF-5E175515E492}.Debug.Windows|x64.ActiveCfg = Debug|x64
|
||||
|
@ -435,37 +318,24 @@ Global
|
|||
{AECEC217-2499-403D-B0BB-2962B9BE5970}.Release.Windows|x64.Build.0 = Release|x64
|
||||
{AECEC217-2499-403D-B0BB-2962B9BE5970}.Debug.Mac|x64.Build.0 = Debug|x64
|
||||
{AECEC217-2499-403D-B0BB-2962B9BE5970}.Release.Mac|x64.Build.0 = Release|x64
|
||||
{24D161E9-D1F0-4299-BBD3-5D940BEDD535}.Debug.Mac|x64.ActiveCfg = Debug|x64
|
||||
{24D161E9-D1F0-4299-BBD3-5D940BEDD535}.Debug.Windows|x64.ActiveCfg = Debug|x64
|
||||
{24D161E9-D1F0-4299-BBD3-5D940BEDD535}.Debug.Windows|x64.Build.0 = Debug|x64
|
||||
{24D161E9-D1F0-4299-BBD3-5D940BEDD535}.Release.Mac|x64.ActiveCfg = Release|x64
|
||||
{24D161E9-D1F0-4299-BBD3-5D940BEDD535}.Release.Windows|x64.ActiveCfg = Release|x64
|
||||
{24D161E9-D1F0-4299-BBD3-5D940BEDD535}.Release.Windows|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{1118B427-7063-422F-83B9-5023C8EC5A7A} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C}
|
||||
{374BF1E5-0B2D-4D4A-BD5E-4212299DEF09} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C}
|
||||
{07F2A520-2AB7-46DD-97C0-75D8E988D55B} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C}
|
||||
{72701BC3-5DA9-4C7A-BF10-9E98C9FC8EAC} = {C41F10F9-1163-4CFA-A465-EA728F75E9FA}
|
||||
{8E0D0989-21F6-4DD8-946C-39F992523CC6} = {C41F10F9-1163-4CFA-A465-EA728F75E9FA}
|
||||
{3771C555-B5C1-45E2-B8B7-2CEF1619CDC5} = {C41F10F9-1163-4CFA-A465-EA728F75E9FA}
|
||||
{BDA91EE5-C684-4FC5-A90A-B7D677421917} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C}
|
||||
{B8C1DFBA-CAFD-4F7E-A1A3-E11907B5467B} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C}
|
||||
{03769A07-F216-456B-886B-E07CAF6C5E81} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C}
|
||||
{5A6656D5-81C7-472C-9DC8-32D071CB2258} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C}
|
||||
{28674A4B-1223-4633-A460-C8CC39B09318} = {DCE11095-DA5F-4878-B58D-2702765560F5}
|
||||
{C5D3CA26-562F-4CA4-A378-B93E97A730E3} = {C41F10F9-1163-4CFA-A465-EA728F75E9FA}
|
||||
{93B403FD-DAFB-46C5-9636-B122792A548A} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C}
|
||||
{A4984251-840E-4622-AD0C-66DFCE2B2574} = {AB0D9230-3893-4486-8899-F9C871FB5D5F}
|
||||
{3AB4FB1F-9E23-4CD8-BFAC-8A2221C8F893} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C}
|
||||
{25229A04-6554-49B1-A95A-3F3B76C5B0C8} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C}
|
||||
{2F63B22B-EE26-4266-BF17-28A9146483A1} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C}
|
||||
{2D23AB54-541F-4ABC-8DCA-08C199E97ABB} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C}
|
||||
{F468B05A-95E5-46BC-8C67-B80A78527B7D} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C}
|
||||
{FAC6EFC5-A890-4CB2-8C80-6358E358C637} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C}
|
||||
{1DAC3DA6-3D21-4917-B9A8-D60C8712252A} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C}
|
||||
{15FAE44C-0D21-4312-9FD3-28F05A5AB7A6} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C}
|
||||
{0D434FA7-6D8C-481E-B0CE-779B59EAEF53} = {C41F10F9-1163-4CFA-A465-EA728F75E9FA}
|
||||
|
@ -474,12 +344,9 @@ Global
|
|||
{32220664-594C-4425-B9A0-88E0BE2F3D2A} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C}
|
||||
{17498502-AEFF-4E70-90CC-1D0B56A8ADF5} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C}
|
||||
{35CA4DFB-1320-4055-B8F6-F12E0F252FF0} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C}
|
||||
{0F0A008E-AB12-40EC-A671-37A541B08C7F} = {C41F10F9-1163-4CFA-A465-EA728F75E9FA}
|
||||
{BD7C5776-82F2-40C6-AF01-B3CC1E2D83AF} = {C41F10F9-1163-4CFA-A465-EA728F75E9FA}
|
||||
{FA273F69-5762-43D8-AEA1-B4F08090D624} = {C41F10F9-1163-4CFA-A465-EA728F75E9FA}
|
||||
{4CC2A90D-D240-4382-B4BF-5E175515E492} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C}
|
||||
{AECEC217-2499-403D-B0BB-2962B9BE5970} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C}
|
||||
{24D161E9-D1F0-4299-BBD3-5D940BEDD535} = {2EF2EC94-3A68-4ED7-9A58-B7057ADBA01C}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {A025908B-DAB1-46CB-83A3-56F3B863D8FA}
|
||||
|
|
|
@ -1,228 +0,0 @@
|
|||
using GVFS.Common;
|
||||
using GVFS.Common.Git;
|
||||
using GVFS.Common.Http;
|
||||
using GVFS.Common.Prefetch;
|
||||
using GVFS.Common.Prefetch.Git;
|
||||
using GVFS.Common.Prefetch.Pipeline;
|
||||
using GVFS.Common.Tracing;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace FastFetch
|
||||
{
|
||||
public class CheckoutPrefetcher : BlobPrefetcher
|
||||
{
|
||||
private readonly int checkoutThreadCount;
|
||||
private readonly bool allowIndexMetadataUpdateFromWorkingTree;
|
||||
private readonly bool forceCheckout;
|
||||
|
||||
public CheckoutPrefetcher(
|
||||
ITracer tracer,
|
||||
Enlistment enlistment,
|
||||
GitObjectsHttpRequestor objectRequestor,
|
||||
int chunkSize,
|
||||
int searchThreadCount,
|
||||
int downloadThreadCount,
|
||||
int indexThreadCount,
|
||||
int checkoutThreadCount,
|
||||
bool allowIndexMetadataUpdateFromWorkingTree,
|
||||
bool forceCheckout)
|
||||
: base(
|
||||
tracer,
|
||||
enlistment,
|
||||
objectRequestor,
|
||||
chunkSize,
|
||||
searchThreadCount,
|
||||
downloadThreadCount,
|
||||
indexThreadCount)
|
||||
{
|
||||
this.checkoutThreadCount = checkoutThreadCount;
|
||||
this.allowIndexMetadataUpdateFromWorkingTree = allowIndexMetadataUpdateFromWorkingTree;
|
||||
this.forceCheckout = forceCheckout;
|
||||
}
|
||||
|
||||
/// <param name="branchOrCommit">A specific branch to filter for, or null for all branches returned from info/refs</param>
|
||||
public override void Prefetch(string branchOrCommit, bool isBranch)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(branchOrCommit))
|
||||
{
|
||||
throw new FetchException("Must specify branch or commit to fetch");
|
||||
}
|
||||
|
||||
GitRefs refs = null;
|
||||
string commitToFetch;
|
||||
if (isBranch)
|
||||
{
|
||||
refs = this.ObjectRequestor.QueryInfoRefs(branchOrCommit);
|
||||
if (refs == null)
|
||||
{
|
||||
throw new FetchException("Could not query info/refs from: {0}", this.Enlistment.RepoUrl);
|
||||
}
|
||||
else if (refs.Count == 0)
|
||||
{
|
||||
throw new FetchException("Could not find branch {0} in info/refs from: {1}", branchOrCommit, this.Enlistment.RepoUrl);
|
||||
}
|
||||
|
||||
commitToFetch = refs.GetTipCommitId(branchOrCommit);
|
||||
}
|
||||
else
|
||||
{
|
||||
commitToFetch = branchOrCommit;
|
||||
}
|
||||
|
||||
this.DownloadMissingCommit(commitToFetch, this.GitObjects);
|
||||
|
||||
// Configure pipeline
|
||||
// Checkout uses DiffHelper when running checkout.Start(), which we use instead of LsTreeHelper
|
||||
// Checkout diff output => FindBlobs => BatchDownload => IndexPack => Checkout available blobs
|
||||
CheckoutStage checkout = new CheckoutStage(this.checkoutThreadCount, this.FolderList, commitToFetch, this.Tracer, this.Enlistment, this.forceCheckout);
|
||||
FindBlobsStage blobFinder = new FindBlobsStage(this.SearchThreadCount, checkout.RequiredBlobs, checkout.AvailableBlobShas, this.Tracer, this.Enlistment);
|
||||
BatchObjectDownloadStage downloader = new BatchObjectDownloadStage(this.DownloadThreadCount, this.ChunkSize, blobFinder.MissingBlobs, checkout.AvailableBlobShas, this.Tracer, this.Enlistment, this.ObjectRequestor, this.GitObjects);
|
||||
IndexPackStage packIndexer = new IndexPackStage(this.IndexThreadCount, downloader.AvailablePacks, checkout.AvailableBlobShas, this.Tracer, this.GitObjects);
|
||||
|
||||
// Start pipeline
|
||||
downloader.Start();
|
||||
blobFinder.Start();
|
||||
checkout.Start();
|
||||
|
||||
blobFinder.WaitForCompletion();
|
||||
this.HasFailures |= blobFinder.HasFailures;
|
||||
|
||||
// Delay indexing. It interferes with FindMissingBlobs, and doesn't help Bootstrapping.
|
||||
packIndexer.Start();
|
||||
|
||||
downloader.WaitForCompletion();
|
||||
this.HasFailures |= downloader.HasFailures;
|
||||
|
||||
packIndexer.WaitForCompletion();
|
||||
this.HasFailures |= packIndexer.HasFailures;
|
||||
|
||||
// Since pack indexer is the last to finish before checkout finishes, it should propagate completion.
|
||||
// This prevents availableObjects from completing before packIndexer can push its objects through this link.
|
||||
checkout.AvailableBlobShas.CompleteAdding();
|
||||
checkout.WaitForCompletion();
|
||||
this.HasFailures |= checkout.HasFailures;
|
||||
|
||||
if (!this.SkipConfigUpdate && !this.HasFailures)
|
||||
{
|
||||
this.UpdateRefs(branchOrCommit, isBranch, refs);
|
||||
|
||||
if (isBranch)
|
||||
{
|
||||
// Update the refspec before setting the upstream or git will complain the remote branch doesn't exist
|
||||
this.HasFailures |= !this.UpdateRefSpec(this.Tracer, this.Enlistment, branchOrCommit, refs);
|
||||
|
||||
using (ITracer activity = this.Tracer.StartActivity("SetUpstream", EventLevel.Informational))
|
||||
{
|
||||
string remoteBranch = refs.GetBranchRefPairs().Single().Key;
|
||||
GitProcess git = new GitProcess(this.Enlistment);
|
||||
GitProcess.Result result = git.SetUpstream(branchOrCommit, remoteBranch);
|
||||
if (result.ExitCodeIsFailure)
|
||||
{
|
||||
activity.RelatedError("Could not set upstream for {0} to {1}: {2}", branchOrCommit, remoteBranch, result.Errors);
|
||||
this.HasFailures = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool shouldSignIndex = !this.GetIsIndexSigningOff();
|
||||
|
||||
// Update the index
|
||||
EventMetadata updateIndexMetadata = new EventMetadata();
|
||||
updateIndexMetadata.Add("IndexSigningIsOff", shouldSignIndex);
|
||||
using (ITracer activity = this.Tracer.StartActivity("UpdateIndex", EventLevel.Informational, Keywords.Telemetry, updateIndexMetadata))
|
||||
{
|
||||
Index sourceIndex = this.GetSourceIndex();
|
||||
GitIndexGenerator indexGen = new GitIndexGenerator(this.Tracer, this.Enlistment, shouldSignIndex);
|
||||
indexGen.CreateFromHeadTree(indexVersion: 2);
|
||||
this.HasFailures |= indexGen.HasFailures;
|
||||
|
||||
if (!indexGen.HasFailures)
|
||||
{
|
||||
Index newIndex = new Index(
|
||||
this.Enlistment.EnlistmentRoot,
|
||||
this.Tracer,
|
||||
Path.Combine(this.Enlistment.DotGitRoot, GVFSConstants.DotGit.IndexName),
|
||||
readOnly: false);
|
||||
|
||||
// Update from disk only if the caller says it is ok via command line
|
||||
// or if we updated the whole tree and know that all files are up to date
|
||||
bool allowIndexMetadataUpdateFromWorkingTree = this.allowIndexMetadataUpdateFromWorkingTree || checkout.UpdatedWholeTree;
|
||||
newIndex.UpdateFileSizesAndTimes(checkout.AddedOrEditedLocalFiles, allowIndexMetadataUpdateFromWorkingTree, shouldSignIndex, sourceIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// * Updates local branch (N/A for checkout to detached HEAD)
|
||||
/// * Updates HEAD
|
||||
/// * Calls base to update shallow file and remote branch.
|
||||
/// </summary>
|
||||
protected override void UpdateRefs(string branchOrCommit, bool isBranch, GitRefs refs)
|
||||
{
|
||||
if (isBranch)
|
||||
{
|
||||
KeyValuePair<string, string> remoteRef = refs.GetBranchRefPairs().Single();
|
||||
string remoteBranch = remoteRef.Key;
|
||||
|
||||
string fullLocalBranchName = branchOrCommit.StartsWith(RefsHeadsGitPath) ? branchOrCommit : (RefsHeadsGitPath + branchOrCommit);
|
||||
this.HasFailures |= !this.UpdateRef(this.Tracer, fullLocalBranchName, remoteRef.Value);
|
||||
this.HasFailures |= !this.UpdateRef(this.Tracer, "HEAD", fullLocalBranchName);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.HasFailures |= !this.UpdateRef(this.Tracer, "HEAD", branchOrCommit);
|
||||
}
|
||||
|
||||
base.UpdateRefs(branchOrCommit, isBranch, refs);
|
||||
}
|
||||
|
||||
private Index GetSourceIndex()
|
||||
{
|
||||
string indexPath = Path.Combine(this.Enlistment.DotGitRoot, GVFSConstants.DotGit.IndexName);
|
||||
string backupIndexPath = Path.Combine(this.Enlistment.DotGitRoot, GVFSConstants.DotGit.IndexName + ".backup");
|
||||
|
||||
if (File.Exists(indexPath))
|
||||
{
|
||||
// Note that this moves the current index, leaving nothing behind
|
||||
// This is intentional as we only need it for the purpose of updating the
|
||||
// new index and leaving it behind can make updating slower.
|
||||
this.Tracer.RelatedEvent(EventLevel.Informational, "CreateBackup", new EventMetadata() { { "BackupIndexName", backupIndexPath } });
|
||||
File.Delete(backupIndexPath);
|
||||
File.Move(indexPath, backupIndexPath);
|
||||
|
||||
Index output = new Index(this.Enlistment.EnlistmentRoot, this.Tracer, backupIndexPath, readOnly: true);
|
||||
output.Parse();
|
||||
return output;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool GetIsIndexSigningOff()
|
||||
{
|
||||
// The first bit of core.gvfs is set if index signing is turned off.
|
||||
const uint CoreGvfsUnsignedIndexFlag = 1;
|
||||
|
||||
GitProcess git = new GitProcess(this.Enlistment);
|
||||
GitProcess.ConfigResult configCoreGvfs = git.GetFromConfig("core.gvfs");
|
||||
string coreGvfs;
|
||||
string error;
|
||||
if (!configCoreGvfs.TryParseAsString(out coreGvfs, out error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint valueCoreGvfs;
|
||||
|
||||
// No errors getting the configuration and it is either "true" or numeric with the right bit set.
|
||||
return !string.IsNullOrEmpty(coreGvfs) &&
|
||||
(coreGvfs.Equals("true", StringComparison.OrdinalIgnoreCase) ||
|
||||
(uint.TryParse(coreGvfs, out valueCoreGvfs) &&
|
||||
((valueCoreGvfs & CoreGvfsUnsignedIndexFlag) == CoreGvfsUnsignedIndexFlag)));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,308 +0,0 @@
|
|||
using GVFS.Common;
|
||||
using GVFS.Common.FileSystem;
|
||||
using GVFS.Common.Git;
|
||||
using GVFS.Common.Prefetch.Git;
|
||||
using GVFS.Common.Prefetch.Pipeline;
|
||||
using GVFS.Common.Tracing;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FastFetch
|
||||
{
|
||||
public class CheckoutStage : PrefetchPipelineStage
|
||||
{
|
||||
private const string AreaPath = nameof(CheckoutStage);
|
||||
private const int NumOperationsPerStatus = 10000;
|
||||
|
||||
private ITracer tracer;
|
||||
private Enlistment enlistment;
|
||||
private PhysicalFileSystem fileSystem;
|
||||
private string targetCommitSha;
|
||||
private bool forceCheckout;
|
||||
|
||||
private DiffHelper diff;
|
||||
|
||||
private int directoryOpCount = 0;
|
||||
private int fileDeleteCount = 0;
|
||||
private int fileWriteCount = 0;
|
||||
private long bytesWritten = 0;
|
||||
private long shasReceived = 0;
|
||||
|
||||
// Checkout requires synchronization between the delete/directory/add stages, so control the parallelization
|
||||
private int maxParallel;
|
||||
|
||||
public CheckoutStage(int maxParallel, IEnumerable<string> folderList, string targetCommitSha, ITracer tracer, Enlistment enlistment, bool forceCheckout)
|
||||
: base(maxParallel: 1)
|
||||
{
|
||||
this.tracer = tracer.StartActivity(AreaPath, EventLevel.Informational, Keywords.Telemetry, metadata: null);
|
||||
this.enlistment = enlistment;
|
||||
this.fileSystem = new PhysicalFileSystem();
|
||||
this.diff = new DiffHelper(tracer, enlistment, new string[0], folderList, includeSymLinks: true);
|
||||
this.targetCommitSha = targetCommitSha;
|
||||
this.forceCheckout = forceCheckout;
|
||||
this.AvailableBlobShas = new BlockingCollection<string>();
|
||||
|
||||
// Keep track of how parallel we're expected to be later during DoWork
|
||||
// Note that '1' is passed to the base object, forcing DoWork to be single threaded
|
||||
// This allows us to control the synchronization between stages by doing the parallization ourselves
|
||||
this.maxParallel = maxParallel;
|
||||
}
|
||||
|
||||
public BlockingCollection<string> RequiredBlobs
|
||||
{
|
||||
get { return this.diff.RequiredBlobs; }
|
||||
}
|
||||
|
||||
public BlockingCollection<string> AvailableBlobShas { get; }
|
||||
|
||||
public bool UpdatedWholeTree
|
||||
{
|
||||
get { return this.diff.UpdatedWholeTree; }
|
||||
}
|
||||
|
||||
public BlockingCollection<string> AddedOrEditedLocalFiles { get; } = new BlockingCollection<string>();
|
||||
|
||||
protected override void DoBeforeWork()
|
||||
{
|
||||
if (this.forceCheckout)
|
||||
{
|
||||
// Force search the entire tree by treating the repo as if it were brand new.
|
||||
this.diff.PerformDiff(sourceTreeSha: null, targetTreeSha: this.targetCommitSha);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Let the diff find the sourceTreeSha on its own.
|
||||
this.diff.PerformDiff(this.targetCommitSha);
|
||||
}
|
||||
|
||||
this.HasFailures = this.diff.HasFailures;
|
||||
}
|
||||
|
||||
protected override void DoWork()
|
||||
{
|
||||
// Do the delete operations first as they can't have dependencies on other work
|
||||
using (ITracer activity = this.tracer.StartActivity(
|
||||
nameof(this.HandleAllFileDeleteOperations),
|
||||
EventLevel.Informational,
|
||||
Keywords.Telemetry,
|
||||
metadata: null))
|
||||
{
|
||||
Parallel.For(0, this.maxParallel, (i) => { this.HandleAllFileDeleteOperations(); });
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("FilesDeleted", this.fileDeleteCount);
|
||||
activity.Stop(metadata);
|
||||
}
|
||||
|
||||
// Do directory operations after deletes in case a file delete must be done first
|
||||
using (ITracer activity = this.tracer.StartActivity(
|
||||
nameof(this.HandleAllDirectoryOperations),
|
||||
EventLevel.Informational,
|
||||
Keywords.Telemetry,
|
||||
metadata: null))
|
||||
{
|
||||
Parallel.For(0, this.maxParallel, (i) => { this.HandleAllDirectoryOperations(); });
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("DirectoryOperationsCompleted", this.directoryOpCount);
|
||||
activity.Stop(metadata);
|
||||
}
|
||||
|
||||
// Do add operations last, after all deletes and directories have been created
|
||||
using (ITracer activity = this.tracer.StartActivity(
|
||||
nameof(this.HandleAllFileAddOperations),
|
||||
EventLevel.Informational,
|
||||
Keywords.Telemetry,
|
||||
metadata: null))
|
||||
{
|
||||
Parallel.For(0, this.maxParallel, (i) => { this.HandleAllFileAddOperations(); });
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("FilesWritten", this.fileWriteCount);
|
||||
activity.Stop(metadata);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void DoAfterWork()
|
||||
{
|
||||
// If for some reason a blob doesn't become available,
|
||||
// checkout might complete with file writes still left undone.
|
||||
if (this.diff.FileAddOperations.Count > 0)
|
||||
{
|
||||
this.HasFailures = true;
|
||||
EventMetadata errorMetadata = new EventMetadata();
|
||||
if (this.diff.FileAddOperations.Count < 10)
|
||||
{
|
||||
errorMetadata.Add("RemainingShas", string.Join(",", this.diff.FileAddOperations.Keys));
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMetadata.Add("RemainingShaCount", this.diff.FileAddOperations.Count);
|
||||
}
|
||||
|
||||
this.tracer.RelatedError(errorMetadata, "Not all file writes were completed");
|
||||
}
|
||||
|
||||
this.AddedOrEditedLocalFiles.CompleteAdding();
|
||||
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("DirectoryOperations", this.directoryOpCount);
|
||||
metadata.Add("FileDeletes", this.fileDeleteCount);
|
||||
metadata.Add("FileWrites", this.fileWriteCount);
|
||||
metadata.Add("BytesWritten", this.bytesWritten);
|
||||
metadata.Add("ShasReceived", this.shasReceived);
|
||||
this.tracer.Stop(metadata);
|
||||
}
|
||||
|
||||
private void HandleAllDirectoryOperations()
|
||||
{
|
||||
DiffTreeResult treeOp;
|
||||
while (this.diff.DirectoryOperations.TryDequeue(out treeOp))
|
||||
{
|
||||
string absoluteTargetPath = Path.Combine(this.enlistment.WorkingDirectoryBackingRoot, treeOp.TargetPath);
|
||||
|
||||
if (this.HasFailures)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (treeOp.Operation)
|
||||
{
|
||||
case DiffTreeResult.Operations.Modify:
|
||||
case DiffTreeResult.Operations.Add:
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(absoluteTargetPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("Operation", "CreateDirectory");
|
||||
metadata.Add(nameof(treeOp.TargetPath), absoluteTargetPath);
|
||||
this.tracer.RelatedError(metadata, ex.Message);
|
||||
this.HasFailures = true;
|
||||
}
|
||||
|
||||
break;
|
||||
case DiffTreeResult.Operations.Delete:
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(absoluteTargetPath))
|
||||
{
|
||||
this.fileSystem.DeleteDirectory(absoluteTargetPath);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// We are deleting directories and subdirectories in parallel
|
||||
if (Directory.Exists(absoluteTargetPath))
|
||||
{
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("Operation", "DeleteDirectory");
|
||||
metadata.Add(nameof(treeOp.TargetPath), absoluteTargetPath);
|
||||
this.tracer.RelatedError(metadata, ex.Message);
|
||||
this.HasFailures = true;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
this.tracer.RelatedError("Ignoring unexpected Tree Operation {0}: {1}", absoluteTargetPath, treeOp.Operation);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Interlocked.Increment(ref this.directoryOpCount) % NumOperationsPerStatus == 0)
|
||||
{
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("DirectoryOperationsQueued", this.diff.DirectoryOperations.Count);
|
||||
metadata.Add("DirectoryOperationsCompleted", this.directoryOpCount);
|
||||
this.tracer.RelatedEvent(EventLevel.Informational, "CheckoutStatus", metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleAllFileDeleteOperations()
|
||||
{
|
||||
string path;
|
||||
while (this.diff.FileDeleteOperations.TryDequeue(out path))
|
||||
{
|
||||
if (this.HasFailures)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
|
||||
Interlocked.Increment(ref this.fileDeleteCount);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("Operation", "DeleteFile");
|
||||
metadata.Add("Path", path);
|
||||
this.tracer.RelatedError(metadata, ex.Message);
|
||||
this.HasFailures = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleAllFileAddOperations()
|
||||
{
|
||||
using (FastFetchLibGit2Repo repo = new FastFetchLibGit2Repo(this.tracer, this.enlistment.WorkingDirectoryBackingRoot))
|
||||
{
|
||||
string availableBlob;
|
||||
while (this.AvailableBlobShas.TryTake(out availableBlob, Timeout.Infinite))
|
||||
{
|
||||
if (this.HasFailures)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Interlocked.Increment(ref this.shasReceived);
|
||||
|
||||
HashSet<PathWithMode> paths;
|
||||
if (this.diff.FileAddOperations.TryRemove(availableBlob, out paths))
|
||||
{
|
||||
try
|
||||
{
|
||||
long written;
|
||||
if (!repo.TryCopyBlobToFile(availableBlob, paths, out written))
|
||||
{
|
||||
// TryCopyBlobTo emits an error event.
|
||||
this.HasFailures = true;
|
||||
}
|
||||
|
||||
Interlocked.Add(ref this.bytesWritten, written);
|
||||
|
||||
foreach (PathWithMode modeAndPath in paths)
|
||||
{
|
||||
this.AddedOrEditedLocalFiles.Add(modeAndPath.Path);
|
||||
|
||||
if (Interlocked.Increment(ref this.fileWriteCount) % NumOperationsPerStatus == 0)
|
||||
{
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("AvailableBlobsQueued", this.AvailableBlobShas.Count);
|
||||
metadata.Add("NumberBlobsNeeded", this.diff.FileAddOperations.Count);
|
||||
this.tracer.RelatedEvent(EventLevel.Informational, "CheckoutStatus", metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
EventMetadata errorData = new EventMetadata();
|
||||
errorData.Add("Operation", "WriteFile");
|
||||
this.tracer.RelatedError(errorData, ex.ToString());
|
||||
this.HasFailures = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\GVFS.Build\GVFS.cs.props" />
|
||||
<Import Project="..\LibGit2Sharp.NativeBinaries.props" />
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AssemblyName>FastFetch</AssemblyName>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<Platforms>x64</Platforms>
|
||||
<RunTimeIdentifiers>osx-x64;win-x64</RunTimeIdentifiers>
|
||||
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Version>$(GVFSVersion)</Version>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Version>$(GVFSVersion)</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GVFS.Common\GVFS.Common.csproj" />
|
||||
<ProjectReference Include="..\GVFS.Virtualization\GVFS.Virtualization.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- ItemGroup Conditions are not supported on VS for Mac and Choose/When must be
|
||||
Used instead, see https://github.com/mono/monodevelop/issues/7417 -->
|
||||
<Choose>
|
||||
<When Condition="'$(OS)' == 'Windows_NT'">
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GVFS.Platform.Windows\GVFS.Platform.Windows.csproj" />
|
||||
<Compile Include="..\GVFS.PlatformLoader\PlatformLoader.Windows.cs">
|
||||
<Link>PlatformLoader.Windows.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
</When>
|
||||
<Otherwise>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GVFS.Platform.Mac\GVFS.Platform.Mac.csproj" />
|
||||
<Compile Include="..\GVFS.PlatformLoader\PlatformLoader.Mac.cs">
|
||||
<Link>PlatformLoader.Mac.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
</Otherwise>
|
||||
</Choose>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommandLineParser" Version="2.1.1-beta" />
|
||||
<PackageReference Include="LibGit2Sharp.NativeBinaries" Version="2.0.278" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,61 +0,0 @@
|
|||
using GVFS.Common.Git;
|
||||
using GVFS.Common.Prefetch.Git;
|
||||
using GVFS.Common.Tracing;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace FastFetch
|
||||
{
|
||||
public class FastFetchLibGit2Repo : LibGit2Repo
|
||||
{
|
||||
public FastFetchLibGit2Repo(ITracer tracer, string repoPath)
|
||||
: base(tracer, repoPath)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual bool TryCopyBlobToFile(string sha, IEnumerable<PathWithMode> destinations, out long bytesWritten)
|
||||
{
|
||||
IntPtr objHandle;
|
||||
if (Native.RevParseSingle(out objHandle, this.RepoHandle, sha) != Native.SuccessCode)
|
||||
{
|
||||
bytesWritten = 0;
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("ObjectSha", sha);
|
||||
this.Tracer.RelatedError(metadata, "Couldn't find object");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Avoid marshalling raw content by using byte* and native writes
|
||||
unsafe
|
||||
{
|
||||
switch (Native.Object.GetType(objHandle))
|
||||
{
|
||||
case Native.ObjectTypes.Blob:
|
||||
byte* originalData = Native.Blob.GetRawContent(objHandle);
|
||||
long originalSize = Native.Blob.GetRawSize(objHandle);
|
||||
|
||||
foreach (PathWithMode destination in destinations)
|
||||
{
|
||||
NativeMethods.WriteFile(this.Tracer, originalData, originalSize, destination.Path, destination.Mode);
|
||||
}
|
||||
|
||||
bytesWritten = originalSize * destinations.Count();
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException("Copying object types other than blobs is not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Native.Object.Free(objHandle);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,359 +0,0 @@
|
|||
using CommandLine;
|
||||
using GVFS.Common;
|
||||
using GVFS.Common.Git;
|
||||
using GVFS.Common.Http;
|
||||
using GVFS.Common.Prefetch;
|
||||
using GVFS.Common.Tracing;
|
||||
using System;
|
||||
|
||||
namespace FastFetch
|
||||
{
|
||||
[Verb("fastfetch", HelpText = "Fast-fetch a branch")]
|
||||
public class FastFetchVerb
|
||||
{
|
||||
// Testing has shown that more than 16 download threads does not improve
|
||||
// performance even with 56 core machines with 40G NICs. More threads does
|
||||
// create more load on the servers as they have to handle extra connections.
|
||||
private const int MaxDefaultDownloadThreads = 16;
|
||||
|
||||
private const int ExitFailure = 1;
|
||||
private const int ExitSuccess = 0;
|
||||
|
||||
[Option(
|
||||
'c',
|
||||
"commit",
|
||||
Required = false,
|
||||
HelpText = "Commit to fetch")]
|
||||
public string Commit { get; set; }
|
||||
|
||||
[Option(
|
||||
'b',
|
||||
"branch",
|
||||
Required = false,
|
||||
HelpText = "Branch to fetch")]
|
||||
public string Branch { get; set; }
|
||||
|
||||
[Option(
|
||||
"cache-server-url",
|
||||
Required = false,
|
||||
Default = "",
|
||||
HelpText = "Defines the url of the cache server")]
|
||||
public string CacheServerUrl { get; set; }
|
||||
|
||||
[Option(
|
||||
"chunk-size",
|
||||
Required = false,
|
||||
Default = 4000,
|
||||
HelpText = "Sets the number of objects to be downloaded in a single pack")]
|
||||
public int ChunkSize { get; set; }
|
||||
|
||||
[Option(
|
||||
"checkout",
|
||||
Required = false,
|
||||
Default = false,
|
||||
HelpText = "Checkout the target commit into the working directory after fetching")]
|
||||
public bool Checkout { get; set; }
|
||||
|
||||
[Option(
|
||||
"force-checkout",
|
||||
Required = false,
|
||||
Default = false,
|
||||
HelpText = "Force FastFetch to checkout content as if the current repo had just been initialized." +
|
||||
"This allows you to include more folders from the repo that were not originally checked out." +
|
||||
"Can only be used with the --checkout option.")]
|
||||
public bool ForceCheckout { get; set; }
|
||||
|
||||
[Option(
|
||||
"search-thread-count",
|
||||
Required = false,
|
||||
Default = 0,
|
||||
HelpText = "Sets the number of threads to use for finding missing blobs. (0 for number of logical cores)")]
|
||||
public int SearchThreadCount { get; set; }
|
||||
|
||||
[Option(
|
||||
"download-thread-count",
|
||||
Required = false,
|
||||
Default = 0,
|
||||
HelpText = "Sets the number of threads to use for downloading. (0 for number of logical cores)")]
|
||||
public int DownloadThreadCount { get; set; }
|
||||
|
||||
[Option(
|
||||
"index-thread-count",
|
||||
Required = false,
|
||||
Default = 0,
|
||||
HelpText = "Sets the number of threads to use for indexing. (0 for number of logical cores)")]
|
||||
public int IndexThreadCount { get; set; }
|
||||
|
||||
[Option(
|
||||
"checkout-thread-count",
|
||||
Required = false,
|
||||
Default = 0,
|
||||
HelpText = "Sets the number of threads to use for checkout. (0 for number of logical cores)")]
|
||||
public int CheckoutThreadCount { get; set; }
|
||||
|
||||
[Option(
|
||||
'r',
|
||||
"max-retries",
|
||||
Required = false,
|
||||
Default = 10,
|
||||
HelpText = "Sets the maximum number of attempts for downloading a pack")]
|
||||
|
||||
public int MaxAttempts { get; set; }
|
||||
|
||||
[Option(
|
||||
"git-path",
|
||||
Default = "",
|
||||
Required = false,
|
||||
HelpText = "Sets the path and filename for git.exe if it isn't expected to be on %PATH%.")]
|
||||
public string GitBinPath { get; set; }
|
||||
|
||||
[Option(
|
||||
"folders",
|
||||
Required = false,
|
||||
Default = "",
|
||||
HelpText = "A semicolon-delimited list of folders to fetch")]
|
||||
public string FolderList { get; set; }
|
||||
|
||||
[Option(
|
||||
"folders-list",
|
||||
Required = false,
|
||||
Default = "",
|
||||
HelpText = "A file containing line-delimited list of folders to fetch")]
|
||||
public string FolderListFile { get; set; }
|
||||
|
||||
[Option(
|
||||
"Allow-index-metadata-update-from-working-tree",
|
||||
Required = false,
|
||||
Default = false,
|
||||
HelpText = "When specified, index metadata (file times and sizes) is updated from disk if not already in the index. " +
|
||||
"This flag should only be used when the working tree is known to be in a good state. " +
|
||||
"Do not use this flag if the working tree is not 100% known to be good as it would cause 'git status' to misreport.")]
|
||||
public bool AllowIndexMetadataUpdateFromWorkingTree { get; set; }
|
||||
|
||||
[Option(
|
||||
"verbose",
|
||||
Required = false,
|
||||
Default = false,
|
||||
HelpText = "Show all outputs on the console in addition to writing them to a log file")]
|
||||
public bool Verbose { get; set; }
|
||||
|
||||
[Option(
|
||||
"parent-activity-id",
|
||||
Required = false,
|
||||
Default = "",
|
||||
HelpText = "The GUID of the caller - used for telemetry purposes.")]
|
||||
public string ParentActivityId { get; set; }
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
Environment.ExitCode = this.ExecuteWithExitCode();
|
||||
}
|
||||
|
||||
private int ExecuteWithExitCode()
|
||||
{
|
||||
// CmdParser doesn't strip quotes, and Path.Combine will throw
|
||||
this.GitBinPath = this.GitBinPath.Replace("\"", string.Empty);
|
||||
if (!GVFSPlatform.Instance.GitInstallation.GitExists(this.GitBinPath))
|
||||
{
|
||||
Console.WriteLine(
|
||||
"Could not find git.exe {0}",
|
||||
!string.IsNullOrWhiteSpace(this.GitBinPath) ? "at " + this.GitBinPath : "on %PATH%");
|
||||
return ExitFailure;
|
||||
}
|
||||
|
||||
if (this.Commit != null && this.Branch != null)
|
||||
{
|
||||
Console.WriteLine("Cannot specify both a commit sha and a branch name.");
|
||||
return ExitFailure;
|
||||
}
|
||||
|
||||
if (this.ForceCheckout && !this.Checkout)
|
||||
{
|
||||
Console.WriteLine("Cannot use --force-checkout option without --checkout option.");
|
||||
return ExitFailure;
|
||||
}
|
||||
|
||||
this.SearchThreadCount = this.SearchThreadCount > 0 ? this.SearchThreadCount : Environment.ProcessorCount;
|
||||
this.DownloadThreadCount = this.DownloadThreadCount > 0 ? this.DownloadThreadCount : Math.Min(Environment.ProcessorCount, MaxDefaultDownloadThreads);
|
||||
this.IndexThreadCount = this.IndexThreadCount > 0 ? this.IndexThreadCount : Environment.ProcessorCount;
|
||||
this.CheckoutThreadCount = this.CheckoutThreadCount > 0 ? this.CheckoutThreadCount : Environment.ProcessorCount;
|
||||
|
||||
this.GitBinPath = !string.IsNullOrWhiteSpace(this.GitBinPath) ? this.GitBinPath : GVFSPlatform.Instance.GitInstallation.GetInstalledGitBinPath();
|
||||
|
||||
GitEnlistment enlistment = GitEnlistment.CreateFromCurrentDirectory(this.GitBinPath);
|
||||
if (enlistment == null)
|
||||
{
|
||||
Console.WriteLine("Must be run within a git repo");
|
||||
return ExitFailure;
|
||||
}
|
||||
|
||||
string commitish = this.Commit ?? this.Branch;
|
||||
if (string.IsNullOrWhiteSpace(commitish))
|
||||
{
|
||||
GitProcess.Result result = new GitProcess(enlistment).GetCurrentBranchName();
|
||||
if (result.ExitCodeIsFailure || string.IsNullOrWhiteSpace(result.Output))
|
||||
{
|
||||
Console.WriteLine("Could not retrieve current branch name: " + result.Errors);
|
||||
return ExitFailure;
|
||||
}
|
||||
|
||||
commitish = result.Output.Trim();
|
||||
}
|
||||
|
||||
Guid parentActivityId = Guid.Empty;
|
||||
if (!string.IsNullOrWhiteSpace(this.ParentActivityId) && !Guid.TryParse(this.ParentActivityId, out parentActivityId))
|
||||
{
|
||||
Console.WriteLine("The ParentActivityId provided (" + this.ParentActivityId + ") is not a valid GUID.");
|
||||
}
|
||||
|
||||
using (JsonTracer tracer = new JsonTracer("Microsoft.Git.FastFetch", parentActivityId, "FastFetch", enlistmentId: null, mountId: null, disableTelemetry: true))
|
||||
{
|
||||
if (this.Verbose)
|
||||
{
|
||||
tracer.AddDiagnosticConsoleEventListener(EventLevel.Informational, Keywords.Any);
|
||||
}
|
||||
else
|
||||
{
|
||||
tracer.AddPrettyConsoleEventListener(EventLevel.Error, Keywords.Any);
|
||||
}
|
||||
|
||||
string fastfetchLogFile = Enlistment.GetNewLogFileName(enlistment.FastFetchLogRoot, "fastfetch");
|
||||
tracer.AddLogFileEventListener(fastfetchLogFile, EventLevel.Informational, Keywords.Any);
|
||||
|
||||
CacheServerInfo cacheServer = new CacheServerInfo(this.GetRemoteUrl(enlistment), null);
|
||||
|
||||
tracer.WriteStartEvent(
|
||||
enlistment.EnlistmentRoot,
|
||||
enlistment.RepoUrl,
|
||||
cacheServer.Url,
|
||||
new EventMetadata
|
||||
{
|
||||
{ "TargetCommitish", commitish },
|
||||
{ "Checkout", this.Checkout },
|
||||
});
|
||||
|
||||
string error;
|
||||
if (!enlistment.Authentication.TryInitialize(tracer, enlistment, out error))
|
||||
{
|
||||
tracer.RelatedError(error);
|
||||
Console.WriteLine(error);
|
||||
return ExitFailure;
|
||||
}
|
||||
|
||||
RetryConfig retryConfig = new RetryConfig(this.MaxAttempts, TimeSpan.FromMinutes(RetryConfig.FetchAndCloneTimeoutMinutes));
|
||||
BlobPrefetcher prefetcher = this.GetFolderPrefetcher(tracer, enlistment, cacheServer, retryConfig);
|
||||
if (!BlobPrefetcher.TryLoadFolderList(enlistment, this.FolderList, this.FolderListFile, prefetcher.FolderList, readListFromStdIn: false, error: out error))
|
||||
{
|
||||
tracer.RelatedError(error);
|
||||
Console.WriteLine(error);
|
||||
return ExitFailure;
|
||||
}
|
||||
|
||||
bool isSuccess;
|
||||
|
||||
try
|
||||
{
|
||||
Func<bool> doPrefetch =
|
||||
() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
bool isBranch = this.Commit == null;
|
||||
prefetcher.Prefetch(commitish, isBranch);
|
||||
return !prefetcher.HasFailures;
|
||||
}
|
||||
catch (BlobPrefetcher.FetchException e)
|
||||
{
|
||||
tracer.RelatedError(e.Message);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
if (this.Verbose)
|
||||
{
|
||||
isSuccess = doPrefetch();
|
||||
}
|
||||
else
|
||||
{
|
||||
isSuccess = ConsoleHelper.ShowStatusWhileRunning(
|
||||
doPrefetch,
|
||||
"Fetching",
|
||||
output: Console.Out,
|
||||
showSpinner: !Console.IsOutputRedirected,
|
||||
gvfsLogEnlistmentRoot: null);
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("See the full log at " + fastfetchLogFile);
|
||||
}
|
||||
|
||||
isSuccess &= !prefetcher.HasFailures;
|
||||
}
|
||||
catch (AggregateException e)
|
||||
{
|
||||
isSuccess = false;
|
||||
foreach (Exception ex in e.Flatten().InnerExceptions)
|
||||
{
|
||||
tracer.RelatedError(ex.ToString());
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
isSuccess = false;
|
||||
tracer.RelatedError(e.ToString());
|
||||
}
|
||||
|
||||
EventMetadata stopMetadata = new EventMetadata();
|
||||
stopMetadata.Add("Success", isSuccess);
|
||||
tracer.Stop(stopMetadata);
|
||||
|
||||
return isSuccess ? ExitSuccess : ExitFailure;
|
||||
}
|
||||
}
|
||||
|
||||
private string GetRemoteUrl(Enlistment enlistment)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(this.CacheServerUrl))
|
||||
{
|
||||
return this.CacheServerUrl;
|
||||
}
|
||||
|
||||
string configuredUrl = CacheServerResolver.GetUrlFromConfig(enlistment);
|
||||
if (!string.IsNullOrWhiteSpace(configuredUrl))
|
||||
{
|
||||
return configuredUrl;
|
||||
}
|
||||
|
||||
return enlistment.RepoUrl;
|
||||
}
|
||||
|
||||
private BlobPrefetcher GetFolderPrefetcher(ITracer tracer, Enlistment enlistment, CacheServerInfo cacheServer, RetryConfig retryConfig)
|
||||
{
|
||||
GitObjectsHttpRequestor objectRequestor = new GitObjectsHttpRequestor(tracer, enlistment, cacheServer, retryConfig);
|
||||
|
||||
if (this.Checkout)
|
||||
{
|
||||
return new CheckoutPrefetcher(
|
||||
tracer,
|
||||
enlistment,
|
||||
objectRequestor,
|
||||
this.ChunkSize,
|
||||
this.SearchThreadCount,
|
||||
this.DownloadThreadCount,
|
||||
this.IndexThreadCount,
|
||||
this.CheckoutThreadCount,
|
||||
this.AllowIndexMetadataUpdateFromWorkingTree,
|
||||
this.ForceCheckout);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new BlobPrefetcher(
|
||||
tracer,
|
||||
enlistment,
|
||||
objectRequestor,
|
||||
this.ChunkSize,
|
||||
this.SearchThreadCount,
|
||||
this.DownloadThreadCount,
|
||||
this.IndexThreadCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
using GVFS.Common;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace FastFetch
|
||||
{
|
||||
public class GitEnlistment : Enlistment
|
||||
{
|
||||
private GitEnlistment(string repoRoot, string gitBinPath)
|
||||
: base(
|
||||
repoRoot,
|
||||
repoRoot,
|
||||
repoRoot,
|
||||
null,
|
||||
gitBinPath,
|
||||
flushFileBuffersForPacks: false,
|
||||
authentication: null)
|
||||
{
|
||||
this.GitObjectsRoot = Path.Combine(repoRoot, GVFSConstants.DotGit.Objects.Root);
|
||||
this.LocalObjectsRoot = this.GitObjectsRoot;
|
||||
this.GitPackRoot = Path.Combine(this.GitObjectsRoot, GVFSConstants.DotGit.Objects.Pack.Name);
|
||||
}
|
||||
|
||||
public override string GitObjectsRoot { get; protected set; }
|
||||
|
||||
public override string LocalObjectsRoot { get; protected set; }
|
||||
|
||||
public override string GitPackRoot { get; protected set; }
|
||||
|
||||
public string FastFetchLogRoot
|
||||
{
|
||||
get { return Path.Combine(this.EnlistmentRoot, GVFSConstants.DotGit.Root, ".fastfetch"); }
|
||||
}
|
||||
|
||||
public static GitEnlistment CreateFromCurrentDirectory(string gitBinPath)
|
||||
{
|
||||
string root = Paths.GetGitEnlistmentRoot(Environment.CurrentDirectory);
|
||||
if (root != null)
|
||||
{
|
||||
return new GitEnlistment(root, gitBinPath);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,739 +0,0 @@
|
|||
using GVFS.Common;
|
||||
using GVFS.Common.Git;
|
||||
using GVFS.Common.Tracing;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.MemoryMappedFiles;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FastFetch
|
||||
{
|
||||
public class Index
|
||||
{
|
||||
// This versioning number lets us track compatibility with previous
|
||||
// versions of FastFetch regarding the index. This should be bumped
|
||||
// when the index older versions of fastfetch created may not be compatible
|
||||
private const int CurrentFastFetchIndexVersion = 1;
|
||||
|
||||
// Constants used for parsing an index entry
|
||||
private const ushort ExtendedBit = 0x4000;
|
||||
private const ushort SkipWorktreeBit = 0x4000;
|
||||
private const int BaseEntryLength = 62;
|
||||
|
||||
// Buffer used to get path from index entry
|
||||
private const int MaxPathBufferSize = 4096;
|
||||
|
||||
// Index default names
|
||||
private const string UpdatedIndexName = "index.updated";
|
||||
|
||||
private static readonly byte[] MagicSignature = new byte[] { (byte)'D', (byte)'I', (byte)'R', (byte)'C' };
|
||||
|
||||
// Location of the version marker file
|
||||
private readonly string versionMarkerFile;
|
||||
|
||||
private readonly bool readOnly;
|
||||
|
||||
// Index paths
|
||||
private readonly string indexPath;
|
||||
private readonly string updatedIndexPath;
|
||||
|
||||
private readonly ITracer tracer;
|
||||
private readonly string repoRoot;
|
||||
|
||||
private Dictionary<string, long> indexEntryOffsets;
|
||||
private uint entryCount;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Index object to parse the specified index file
|
||||
/// </summary>
|
||||
public Index(
|
||||
string repoRoot,
|
||||
ITracer tracer,
|
||||
string indexFullPath,
|
||||
bool readOnly)
|
||||
{
|
||||
this.tracer = tracer;
|
||||
this.repoRoot = repoRoot;
|
||||
this.indexPath = indexFullPath;
|
||||
this.readOnly = readOnly;
|
||||
|
||||
if (this.readOnly)
|
||||
{
|
||||
this.updatedIndexPath = this.indexPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.updatedIndexPath = Path.Combine(repoRoot, GVFSConstants.DotGit.Root, UpdatedIndexName);
|
||||
}
|
||||
|
||||
this.versionMarkerFile = Path.Combine(this.repoRoot, GVFSConstants.DotGit.Root, ".fastfetch", "VersionMarker");
|
||||
}
|
||||
|
||||
public uint IndexVersion { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Updates entries in the current index with file sizes and times
|
||||
/// Algorithm:
|
||||
/// 1) If there was an index in place when this object was constructed, then:
|
||||
/// a) Copy all valid entries (below) from the previous index to the new index
|
||||
/// b) Conditionally (below) get times/sizes from the working tree for files not updated from the previous index
|
||||
///
|
||||
/// 2) If there was no index in place, conditionally populate all entries from disk
|
||||
///
|
||||
/// Conditions:
|
||||
/// - Working tree is only searched if allowUpdateFromWorkingTree is specified
|
||||
/// - A valid entry is an entry that exist and has a non-zero creation time (ctime)
|
||||
/// </summary>
|
||||
/// <param name="addedOrEditedLocalFiles">A collection of added or edited files</param>
|
||||
/// <param name="allowUpdateFromWorkingTree">Set to true if the working tree is known good and can be used during the update.</param>
|
||||
/// <param name="backupIndex">An optional index to source entry values from</param>
|
||||
public void UpdateFileSizesAndTimes(BlockingCollection<string> addedOrEditedLocalFiles, bool allowUpdateFromWorkingTree, bool shouldSignIndex, Index backupIndex = null)
|
||||
{
|
||||
if (this.readOnly)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot update a readonly index.");
|
||||
}
|
||||
|
||||
using (ITracer activity = this.tracer.StartActivity("UpdateFileSizesAndTimes", EventLevel.Informational, Keywords.Telemetry, null))
|
||||
{
|
||||
File.Copy(this.indexPath, this.updatedIndexPath, overwrite: true);
|
||||
|
||||
this.Parse();
|
||||
|
||||
bool anyEntriesUpdated = false;
|
||||
|
||||
using (MemoryMappedFile mmf = this.GetMemoryMappedFile())
|
||||
using (MemoryMappedViewAccessor indexView = mmf.CreateViewAccessor())
|
||||
{
|
||||
// Only populate from the previous index if we believe it's good to populate from
|
||||
// For now, a current FastFetch version marker is the only criteria
|
||||
if (backupIndex != null)
|
||||
{
|
||||
if (this.IsFastFetchVersionMarkerCurrent())
|
||||
{
|
||||
using (this.tracer.StartActivity("UpdateFileInformationFromPreviousIndex", EventLevel.Informational, Keywords.Telemetry, null))
|
||||
{
|
||||
anyEntriesUpdated |= this.UpdateFileInformationForAllEntries(indexView, backupIndex, allowUpdateFromWorkingTree);
|
||||
}
|
||||
|
||||
if (addedOrEditedLocalFiles != null)
|
||||
{
|
||||
// always update these files from disk or the index won't have good information
|
||||
// for them and they'll show as modified even those not actually modified.
|
||||
anyEntriesUpdated |= this.UpdateFileInformationFromDiskForFiles(indexView, addedOrEditedLocalFiles);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (allowUpdateFromWorkingTree)
|
||||
{
|
||||
// If we didn't update from a previous index, update from the working tree if allowed.
|
||||
anyEntriesUpdated |= this.UpdateFileInformationFromWorkingTree(indexView);
|
||||
}
|
||||
|
||||
indexView.Flush();
|
||||
}
|
||||
|
||||
if (anyEntriesUpdated)
|
||||
{
|
||||
this.MoveUpdatedIndexToFinalLocation(shouldSignIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
File.Delete(this.updatedIndexPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Parse()
|
||||
{
|
||||
using (ITracer activity = this.tracer.StartActivity("ParseIndex", EventLevel.Informational, Keywords.Telemetry, new EventMetadata() { { "Index", this.updatedIndexPath } }))
|
||||
{
|
||||
using (Stream indexStream = new FileStream(this.updatedIndexPath, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
this.ParseIndex(indexStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string FromDotnetFullPathToGitRelativePath(string path, string repoRoot)
|
||||
{
|
||||
return path.Substring(repoRoot.Length).TrimStart(Path.DirectorySeparatorChar).Replace(Path.DirectorySeparatorChar, GVFSConstants.GitPathSeparator);
|
||||
}
|
||||
|
||||
private static string FromGitRelativePathToDotnetFullPath(string path, string repoRoot)
|
||||
{
|
||||
return Path.Combine(repoRoot, path.Replace(GVFSConstants.GitPathSeparator, Path.DirectorySeparatorChar));
|
||||
}
|
||||
|
||||
private MemoryMappedFile GetMemoryMappedFile()
|
||||
{
|
||||
return MemoryMappedFile.CreateFromFile(this.updatedIndexPath, FileMode.Open);
|
||||
}
|
||||
|
||||
private bool UpdateFileInformationFromWorkingTree(MemoryMappedViewAccessor indexView)
|
||||
{
|
||||
long updatedEntries = 0;
|
||||
|
||||
using (ITracer activity = this.tracer.StartActivity("UpdateFileInformationFromWorkingTree", EventLevel.Informational, Keywords.Telemetry, null))
|
||||
{
|
||||
WorkingTree.ForAllFiles(
|
||||
this.repoRoot,
|
||||
(path, files) =>
|
||||
{
|
||||
foreach (FileInfo file in files)
|
||||
{
|
||||
string gitPath = FromDotnetFullPathToGitRelativePath(file.FullName, this.repoRoot);
|
||||
long offset;
|
||||
if (this.indexEntryOffsets.TryGetValue(gitPath, out offset))
|
||||
{
|
||||
if (NativeMethods.TryStatFileAndUpdateIndex(this.tracer, gitPath, indexView, offset))
|
||||
{
|
||||
Interlocked.Increment(ref updatedEntries);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return updatedEntries > 0;
|
||||
}
|
||||
|
||||
private bool UpdateFileInformationFromDiskForFiles(MemoryMappedViewAccessor indexView, BlockingCollection<string> addedOrEditedLocalFiles)
|
||||
{
|
||||
long updatedEntriesFromDisk = 0;
|
||||
using (ITracer activity = this.tracer.StartActivity("UpdateDownloadedFiles", EventLevel.Informational, Keywords.Telemetry, null))
|
||||
{
|
||||
Parallel.ForEach(
|
||||
addedOrEditedLocalFiles,
|
||||
(localPath) =>
|
||||
{
|
||||
string gitPath = localPath.Replace(Path.DirectorySeparatorChar, GVFSConstants.GitPathSeparator);
|
||||
long offset;
|
||||
if (this.indexEntryOffsets.TryGetValue(gitPath, out offset))
|
||||
{
|
||||
if (NativeMethods.TryStatFileAndUpdateIndex(this.tracer, gitPath, indexView, offset))
|
||||
{
|
||||
Interlocked.Increment(ref updatedEntriesFromDisk);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.tracer.RelatedError($"{nameof(this.UpdateFileInformationFromDiskForFiles)}: Failed to update file information from disk for file {0}", gitPath);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.tracer.RelatedEvent(EventLevel.Informational, "UpdateIndexFileInformation", new EventMetadata() { { "UpdatedFromDisk", updatedEntriesFromDisk } }, Keywords.Telemetry);
|
||||
return updatedEntriesFromDisk > 0;
|
||||
}
|
||||
|
||||
private bool UpdateFileInformationForAllEntries(MemoryMappedViewAccessor indexView, Index otherIndex, bool shouldAlsoTryPopulateFromDisk)
|
||||
{
|
||||
long updatedEntriesFromOtherIndex = 0;
|
||||
long updatedEntriesFromDisk = 0;
|
||||
|
||||
using (MemoryMappedFile mmf = otherIndex.GetMemoryMappedFile())
|
||||
using (MemoryMappedViewAccessor otherIndexView = mmf.CreateViewAccessor())
|
||||
{
|
||||
Parallel.ForEach(
|
||||
this.indexEntryOffsets,
|
||||
entry =>
|
||||
{
|
||||
string currentIndexFilename = entry.Key;
|
||||
long currentIndexOffset = entry.Value;
|
||||
if (!IndexEntry.HasInitializedCTimeEntry(indexView, currentIndexOffset))
|
||||
{
|
||||
long otherIndexOffset;
|
||||
if (otherIndex.indexEntryOffsets.TryGetValue(currentIndexFilename, out otherIndexOffset))
|
||||
{
|
||||
if (IndexEntry.HasInitializedCTimeEntry(otherIndexView, otherIndexOffset))
|
||||
{
|
||||
IndexEntry currentIndexEntry = new IndexEntry(indexView, currentIndexOffset);
|
||||
IndexEntry otherIndexEntry = new IndexEntry(otherIndexView, otherIndexOffset);
|
||||
|
||||
currentIndexEntry.CtimeSeconds = otherIndexEntry.CtimeSeconds;
|
||||
currentIndexEntry.CtimeNanosecondFraction = otherIndexEntry.CtimeNanosecondFraction;
|
||||
currentIndexEntry.MtimeSeconds = otherIndexEntry.MtimeSeconds;
|
||||
currentIndexEntry.MtimeNanosecondFraction = otherIndexEntry.MtimeNanosecondFraction;
|
||||
currentIndexEntry.Dev = otherIndexEntry.Dev;
|
||||
currentIndexEntry.Ino = otherIndexEntry.Ino;
|
||||
currentIndexEntry.Uid = otherIndexEntry.Uid;
|
||||
currentIndexEntry.Gid = otherIndexEntry.Gid;
|
||||
currentIndexEntry.Size = otherIndexEntry.Size;
|
||||
|
||||
Interlocked.Increment(ref updatedEntriesFromOtherIndex);
|
||||
}
|
||||
}
|
||||
else if (shouldAlsoTryPopulateFromDisk)
|
||||
{
|
||||
string localPath = FromGitRelativePathToDotnetFullPath(currentIndexFilename, this.repoRoot);
|
||||
|
||||
if (NativeMethods.TryStatFileAndUpdateIndex(this.tracer, localPath, indexView, entry.Value))
|
||||
{
|
||||
Interlocked.Increment(ref updatedEntriesFromDisk);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.tracer.RelatedEvent(
|
||||
EventLevel.Informational,
|
||||
"UpdateIndexFileInformation",
|
||||
new EventMetadata()
|
||||
{
|
||||
{ "UpdatedFromOtherIndex", updatedEntriesFromOtherIndex },
|
||||
{ "UpdatedFromDisk", updatedEntriesFromDisk }
|
||||
},
|
||||
Keywords.Telemetry);
|
||||
|
||||
return (updatedEntriesFromOtherIndex > 0) || (updatedEntriesFromDisk > 0);
|
||||
}
|
||||
|
||||
private void MoveUpdatedIndexToFinalLocation(bool shouldSignIndex)
|
||||
{
|
||||
if (shouldSignIndex)
|
||||
{
|
||||
using (ITracer activity = this.tracer.StartActivity("SignIndex", EventLevel.Informational, Keywords.Telemetry, metadata: null))
|
||||
{
|
||||
using (FileStream fs = File.Open(this.updatedIndexPath, FileMode.Open, FileAccess.ReadWrite))
|
||||
{
|
||||
// Truncate the old hash off. The Index class is expected to preserve any existing hash.
|
||||
fs.SetLength(fs.Length - 20);
|
||||
using (HashingStream hashStream = new HashingStream(fs))
|
||||
{
|
||||
fs.Position = 0;
|
||||
hashStream.CopyTo(Stream.Null);
|
||||
byte[] hash = hashStream.Hash;
|
||||
|
||||
// The fs pointer is now where the old hash used to be. Perfect. :)
|
||||
fs.Write(hash, 0, hash.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.tracer.RelatedEvent(EventLevel.Informational, "MoveUpdatedIndexToFinalLocation", new EventMetadata() { { "UpdatedIndex", this.updatedIndexPath }, { "Index", this.indexPath } });
|
||||
File.Delete(this.indexPath);
|
||||
File.Move(this.updatedIndexPath, this.indexPath);
|
||||
|
||||
this.WriteFastFetchIndexVersionMarker();
|
||||
}
|
||||
|
||||
private void WriteFastFetchIndexVersionMarker()
|
||||
{
|
||||
if (File.Exists(this.versionMarkerFile))
|
||||
{
|
||||
File.SetAttributes(this.versionMarkerFile, FileAttributes.Normal);
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(this.versionMarkerFile));
|
||||
File.WriteAllText(this.versionMarkerFile, CurrentFastFetchIndexVersion.ToString(), Encoding.ASCII);
|
||||
File.SetAttributes(this.versionMarkerFile, FileAttributes.ReadOnly);
|
||||
this.tracer.RelatedEvent(EventLevel.Informational, "MarkerWritten", new EventMetadata() { { "Version", CurrentFastFetchIndexVersion } });
|
||||
}
|
||||
|
||||
private bool IsFastFetchVersionMarkerCurrent()
|
||||
{
|
||||
if (File.Exists(this.versionMarkerFile))
|
||||
{
|
||||
int version;
|
||||
string marker = File.ReadAllText(this.versionMarkerFile, Encoding.ASCII);
|
||||
bool isMarkerCurrent = int.TryParse(marker, out version) && (version == CurrentFastFetchIndexVersion);
|
||||
this.tracer.RelatedEvent(EventLevel.Informational, "PreviousMarker", new EventMetadata() { { "Content", marker }, { "IsCurrent", isMarkerCurrent } }, Keywords.Telemetry);
|
||||
return isMarkerCurrent;
|
||||
}
|
||||
|
||||
this.tracer.RelatedEvent(EventLevel.Informational, "NoPreviousMarkerFound", null, Keywords.Telemetry);
|
||||
return false;
|
||||
}
|
||||
|
||||
private void ParseIndex(Stream indexStream)
|
||||
{
|
||||
byte[] buffer = new byte[40];
|
||||
indexStream.Position = 0;
|
||||
|
||||
byte[] signature = new byte[4];
|
||||
indexStream.Read(signature, 0, 4);
|
||||
if (!Enumerable.SequenceEqual(MagicSignature, signature))
|
||||
{
|
||||
throw new InvalidDataException("Incorrect magic signature for index: " + string.Join(string.Empty, signature.Select(c => (char)c)));
|
||||
}
|
||||
|
||||
this.IndexVersion = this.ReadUInt32(buffer, indexStream);
|
||||
|
||||
if (this.IndexVersion < 2 || this.IndexVersion > 4)
|
||||
{
|
||||
throw new InvalidDataException("Unsupported index version: " + this.IndexVersion);
|
||||
}
|
||||
|
||||
this.entryCount = this.ReadUInt32(buffer, indexStream);
|
||||
|
||||
this.tracer.RelatedEvent(EventLevel.Informational, "IndexData", new EventMetadata() { { "Index", this.updatedIndexPath }, { "Version", this.IndexVersion }, { "entryCount", this.entryCount } }, Keywords.Telemetry);
|
||||
|
||||
this.indexEntryOffsets = new Dictionary<string, long>((int)this.entryCount, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
int previousPathLength = 0;
|
||||
byte[] pathBuffer = new byte[MaxPathBufferSize];
|
||||
for (int i = 0; i < this.entryCount; i++)
|
||||
{
|
||||
// See https://github.com/git/git/blob/867b1c1bf68363bcfd17667d6d4b9031fa6a1300/Documentation/technical/index-format.txt#L38
|
||||
long entryOffset = indexStream.Position;
|
||||
|
||||
int entryLength = BaseEntryLength;
|
||||
|
||||
// Skip the next 60 bytes.
|
||||
// 40 bytes encapsulated by IndexEntry but not needed now.
|
||||
// 20 bytes of sha
|
||||
indexStream.Position += 60;
|
||||
|
||||
ushort flags = this.ReadUInt16(buffer, indexStream);
|
||||
bool isExtended = (flags & ExtendedBit) == ExtendedBit;
|
||||
ushort pathLength = (ushort)(flags & 0xFFF);
|
||||
entryLength += pathLength;
|
||||
|
||||
bool skipWorktree = false;
|
||||
if (isExtended && (this.IndexVersion > 2))
|
||||
{
|
||||
ushort extendedFlags = this.ReadUInt16(buffer, indexStream);
|
||||
skipWorktree = (extendedFlags & SkipWorktreeBit) == SkipWorktreeBit;
|
||||
entryLength += 2;
|
||||
}
|
||||
|
||||
if (this.IndexVersion == 4)
|
||||
{
|
||||
int replaceLength = this.ReadReplaceLength(indexStream);
|
||||
int replaceIndex = previousPathLength - replaceLength;
|
||||
indexStream.Read(pathBuffer, replaceIndex, pathLength - replaceIndex + 1);
|
||||
previousPathLength = pathLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Simple paths but 1 - 8 nul bytes as necessary to pad the entry to a multiple of eight bytes
|
||||
int numNulBytes = 8 - (entryLength % 8);
|
||||
indexStream.Read(pathBuffer, 0, pathLength + numNulBytes);
|
||||
}
|
||||
|
||||
if (!skipWorktree)
|
||||
{
|
||||
// Examine only the things we're not skipping...
|
||||
// Potential Future Perf Optimization: Perform this work on multiple threads. If we take the first byte and % by number of threads,
|
||||
// we can ensure that all entries for a given folder end up in the same dictionary
|
||||
string path = Encoding.UTF8.GetString(pathBuffer, 0, pathLength);
|
||||
this.indexEntryOffsets[path] = entryOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the length of the replacement string. For definition of data, see:
|
||||
/// https://github.com/git/git/blob/867b1c1bf68363bcfd17667d6d4b9031fa6a1300/Documentation/technical/index-format.txt#L38
|
||||
/// </summary>
|
||||
/// <param name="stream">stream to read bytes from</param>
|
||||
/// <returns></returns>
|
||||
private int ReadReplaceLength(Stream stream)
|
||||
{
|
||||
int headerByte = stream.ReadByte();
|
||||
int offset = headerByte & 0x7f;
|
||||
|
||||
// Terminate the loop when the high bit is no longer set.
|
||||
for (int i = 0; (headerByte & 0x80) != 0; i++)
|
||||
{
|
||||
headerByte = stream.ReadByte();
|
||||
if (headerByte < 0)
|
||||
{
|
||||
throw new EndOfStreamException("Index file has been truncated.");
|
||||
}
|
||||
|
||||
offset += 1;
|
||||
offset = (offset << 7) + (headerByte & 0x7f);
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
private uint ReadUInt32(byte[] buffer, Stream stream)
|
||||
{
|
||||
buffer[3] = (byte)stream.ReadByte();
|
||||
buffer[2] = (byte)stream.ReadByte();
|
||||
buffer[1] = (byte)stream.ReadByte();
|
||||
buffer[0] = (byte)stream.ReadByte();
|
||||
|
||||
return BitConverter.ToUInt32(buffer, 0);
|
||||
}
|
||||
|
||||
private ushort ReadUInt16(byte[] buffer, Stream stream)
|
||||
{
|
||||
buffer[1] = (byte)stream.ReadByte();
|
||||
buffer[0] = (byte)stream.ReadByte();
|
||||
|
||||
// (ushort)BitConverter.ToInt16 avoids the running the duplicated checks in ToUInt16
|
||||
return (ushort)BitConverter.ToInt16(buffer, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Private helper class to read/write specific values from a Git Index entry based on offset in a view.
|
||||
/// </summary>
|
||||
internal class IndexEntry
|
||||
{
|
||||
private const long UnixEpochMilliseconds = 116444736000000000;
|
||||
private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
private MemoryMappedViewAccessor indexView;
|
||||
|
||||
public IndexEntry(MemoryMappedViewAccessor indexView, long offset)
|
||||
{
|
||||
this.indexView = indexView;
|
||||
this.Offset = offset;
|
||||
}
|
||||
|
||||
// EntryOffsets is the offset from the start of a index entry where specific data exists
|
||||
// For more information about the layout of git index entries, see:
|
||||
// https://github.com/git/git/blob/867b1c1bf68363bcfd17667d6d4b9031fa6a1300/Documentation/technical/index-format.txt#L38
|
||||
private enum EntryOffsets
|
||||
{
|
||||
ctimeSeconds = 0,
|
||||
ctimeNanoseconds = 4,
|
||||
mtimeSeconds = 8,
|
||||
mtimeNanoseconds = 12,
|
||||
dev = 16,
|
||||
ino = 20,
|
||||
uid = 28,
|
||||
gid = 32,
|
||||
filesize = 36,
|
||||
flags = 80,
|
||||
extendedFlags = 82,
|
||||
}
|
||||
|
||||
public long Offset { get; set; }
|
||||
|
||||
public uint CtimeSeconds
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.ReadUInt32(EntryOffsets.ctimeSeconds);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.WriteUInt32(EntryOffsets.ctimeSeconds, value);
|
||||
}
|
||||
}
|
||||
|
||||
public uint CtimeNanosecondFraction
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.ReadUInt32(EntryOffsets.ctimeNanoseconds);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.WriteUInt32(EntryOffsets.ctimeNanoseconds, value);
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime Ctime
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.ToDotnetTime(this.CtimeSeconds, this.CtimeNanosecondFraction);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
IndexEntryTime time = this.ToGitTime(value);
|
||||
this.CtimeSeconds = time.Seconds;
|
||||
this.CtimeNanosecondFraction = time.NanosecondFraction;
|
||||
}
|
||||
}
|
||||
|
||||
public uint MtimeSeconds
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.ReadUInt32(EntryOffsets.mtimeSeconds);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.WriteUInt32(EntryOffsets.mtimeSeconds, value);
|
||||
}
|
||||
}
|
||||
|
||||
public uint MtimeNanosecondFraction
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.ReadUInt32(EntryOffsets.mtimeNanoseconds);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.WriteUInt32(EntryOffsets.mtimeNanoseconds, value);
|
||||
}
|
||||
}
|
||||
|
||||
public DateTime Mtime
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.ToDotnetTime(this.MtimeSeconds, this.MtimeNanosecondFraction);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
IndexEntryTime times = this.ToGitTime(value);
|
||||
this.MtimeSeconds = times.Seconds;
|
||||
this.MtimeNanosecondFraction = times.NanosecondFraction;
|
||||
}
|
||||
}
|
||||
|
||||
public uint Size
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.ReadUInt32(EntryOffsets.filesize);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.WriteUInt32(EntryOffsets.filesize, value);
|
||||
}
|
||||
}
|
||||
|
||||
public uint Dev
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.ReadUInt32(EntryOffsets.dev);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.WriteUInt32(EntryOffsets.dev, value);
|
||||
}
|
||||
}
|
||||
|
||||
public uint Ino
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.ReadUInt32(EntryOffsets.ino);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.WriteUInt32(EntryOffsets.ino, value);
|
||||
}
|
||||
}
|
||||
|
||||
public uint Uid
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.ReadUInt32(EntryOffsets.uid);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.WriteUInt32(EntryOffsets.uid, value);
|
||||
}
|
||||
}
|
||||
|
||||
public uint Gid
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.ReadUInt32(EntryOffsets.gid);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.WriteUInt32(EntryOffsets.gid, value);
|
||||
}
|
||||
}
|
||||
|
||||
public ushort Flags
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.ReadUInt16(EntryOffsets.flags);
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.WriteUInt16(EntryOffsets.flags, value);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsExtended
|
||||
{
|
||||
get
|
||||
{
|
||||
return (this.Flags & Index.ExtendedBit) == Index.ExtendedBit;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool HasInitializedCTimeEntry(MemoryMappedViewAccessor indexView, long offset)
|
||||
{
|
||||
return EndianHelper.Swap(indexView.ReadUInt32(offset + (long)EntryOffsets.ctimeSeconds)) != 0;
|
||||
}
|
||||
|
||||
private uint ReadUInt32(EntryOffsets fromOffset)
|
||||
{
|
||||
return EndianHelper.Swap(this.indexView.ReadUInt32(this.Offset + (long)fromOffset));
|
||||
}
|
||||
|
||||
private void WriteUInt32(EntryOffsets fromOffset, uint data)
|
||||
{
|
||||
this.indexView.Write(this.Offset + (long)fromOffset, EndianHelper.Swap(data));
|
||||
}
|
||||
|
||||
private ushort ReadUInt16(EntryOffsets fromOffset)
|
||||
{
|
||||
return EndianHelper.Swap(this.indexView.ReadUInt16(this.Offset + (long)fromOffset));
|
||||
}
|
||||
|
||||
private void WriteUInt16(EntryOffsets fromOffset, ushort data)
|
||||
{
|
||||
this.indexView.Write(this.Offset + (long)fromOffset, EndianHelper.Swap(data));
|
||||
}
|
||||
|
||||
private IndexEntryTime ToGitTime(DateTime datetime)
|
||||
{
|
||||
if (datetime > UnixEpoch)
|
||||
{
|
||||
// Using the same FileTime -> Unix time conversion that Git uses.
|
||||
long unixEpochRelativeNanoseconds = datetime.ToFileTime() - IndexEntry.UnixEpochMilliseconds;
|
||||
uint wholeSeconds = (uint)(unixEpochRelativeNanoseconds / (long)10000000);
|
||||
uint nanosecondFraction = (uint)((unixEpochRelativeNanoseconds % 10000000) * 100);
|
||||
|
||||
return new IndexEntryTime() { Seconds = wholeSeconds, NanosecondFraction = nanosecondFraction };
|
||||
}
|
||||
else
|
||||
{
|
||||
return new IndexEntryTime() { Seconds = 0, NanosecondFraction = 0 };
|
||||
}
|
||||
}
|
||||
|
||||
private DateTime ToDotnetTime(uint seconds, uint nanosecondFraction)
|
||||
{
|
||||
DateTime time = UnixEpoch.AddSeconds(seconds).AddMilliseconds(nanosecondFraction / 1000000);
|
||||
return time;
|
||||
}
|
||||
|
||||
private class IndexEntryTime
|
||||
{
|
||||
public uint Seconds { get; set; }
|
||||
public uint NanosecondFraction { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
using GVFS.Common.Tracing;
|
||||
using System.IO.MemoryMappedFiles;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace FastFetch
|
||||
{
|
||||
internal static class NativeMethods
|
||||
{
|
||||
public static bool isUnixOS = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
|
||||
|
||||
public static unsafe void WriteFile(ITracer tracer, byte* originalData, long originalSize, string destination, ushort mode)
|
||||
{
|
||||
if (isUnixOS)
|
||||
{
|
||||
NativeUnixMethods.WriteFile(tracer, originalData, originalSize, destination, mode);
|
||||
}
|
||||
else
|
||||
{
|
||||
NativeWindowsMethods.WriteFile(tracer, originalData, originalSize, destination);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryStatFileAndUpdateIndex(ITracer tracer, string path, MemoryMappedViewAccessor indexView, long offset)
|
||||
{
|
||||
if (isUnixOS)
|
||||
{
|
||||
return NativeUnixMethods.TryStatFileAndUpdateIndex(tracer, path, indexView, offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NativeWindowsMethods.TryStatFileAndUpdateIndex(tracer, path, indexView, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,153 +0,0 @@
|
|||
using GVFS.Common.Tracing;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO.MemoryMappedFiles;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace FastFetch
|
||||
{
|
||||
public class NativeUnixMethods
|
||||
{
|
||||
public const int ReadOnly = 0x0000;
|
||||
public const int WriteOnly = 0x0001;
|
||||
|
||||
public const int Create = 0x0200;
|
||||
public const int Truncate = 0x0400;
|
||||
private const int InvalidFileDescriptor = -1;
|
||||
private const ushort SymLinkMode = 0xA000;
|
||||
|
||||
public static unsafe void WriteFile(ITracer tracer, byte* originalData, long originalSize, string destination, ushort mode)
|
||||
{
|
||||
int fileDescriptor = InvalidFileDescriptor;
|
||||
try
|
||||
{
|
||||
if (mode == SymLinkMode)
|
||||
{
|
||||
string linkTarget = Marshal.PtrToStringUTF8(new IntPtr(originalData));
|
||||
int result = CreateSymLink(linkTarget, destination);
|
||||
if (result == -1)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error(), $"Failed to create symlink({linkTarget}, {destination}).");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fileDescriptor = Open(destination, WriteOnly | Create | Truncate, mode);
|
||||
if (fileDescriptor == InvalidFileDescriptor)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error(), $"Failed to open({destination}.)");
|
||||
}
|
||||
|
||||
IntPtr result = Write(fileDescriptor, originalData, (IntPtr)originalSize);
|
||||
if (result.ToInt32() == -1)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error(), $"Failed to write contents into {destination}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Win32Exception e)
|
||||
{
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("filemode", mode);
|
||||
metadata.Add("destination", destination);
|
||||
metadata.Add("exception", e.ToString());
|
||||
tracer.RelatedError(metadata, $"Failed to properly create {destination}");
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Close(fileDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryStatFileAndUpdateIndex(ITracer tracer, string path, MemoryMappedViewAccessor indexView, long offset)
|
||||
{
|
||||
try
|
||||
{
|
||||
NativeStat.StatBuffer st = StatFile(path);
|
||||
Index.IndexEntry indexEntry = new Index.IndexEntry(indexView, offset);
|
||||
indexEntry.MtimeSeconds = (uint)st.MTimespec.Sec;
|
||||
indexEntry.MtimeNanosecondFraction = (uint)st.MTimespec.Nsec;
|
||||
indexEntry.CtimeSeconds = (uint)st.CTimespec.Sec;
|
||||
indexEntry.CtimeNanosecondFraction = (uint)st.CTimespec.Nsec;
|
||||
indexEntry.Size = (uint)st.Size;
|
||||
indexEntry.Dev = (uint)st.Dev;
|
||||
indexEntry.Ino = (uint)st.Ino;
|
||||
indexEntry.Uid = st.UID;
|
||||
indexEntry.Gid = st.GID;
|
||||
return true;
|
||||
}
|
||||
catch (Win32Exception e)
|
||||
{
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("path", path);
|
||||
metadata.Add("exception", e.ToString());
|
||||
tracer.RelatedError(metadata, "Error stat-ing file.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("libc", EntryPoint = "open", SetLastError = true)]
|
||||
public static extern int Open(string path, int flag, ushort creationMode);
|
||||
|
||||
[DllImport("libc", EntryPoint = "close", SetLastError = true)]
|
||||
public static extern int Close(int fd);
|
||||
|
||||
[DllImport("libc", EntryPoint = "write", SetLastError = true)]
|
||||
private static unsafe extern IntPtr Write(int fileDescriptor, void* buf, IntPtr count);
|
||||
|
||||
[DllImport("libc", EntryPoint = "symlink", SetLastError = true)]
|
||||
private static extern int CreateSymLink(string linkTarget, string newLinkPath);
|
||||
|
||||
private static NativeStat.StatBuffer StatFile(string fileName)
|
||||
{
|
||||
NativeStat.StatBuffer statBuffer = new NativeStat.StatBuffer();
|
||||
if (NativeStat.Stat(fileName, out statBuffer) != 0)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error(), $"Failed to stat {fileName}");
|
||||
}
|
||||
|
||||
return statBuffer;
|
||||
}
|
||||
|
||||
private static class NativeStat
|
||||
{
|
||||
[DllImport("libc", EntryPoint = "stat$INODE64", SetLastError = true)]
|
||||
public static extern int Stat(string path, [Out] out StatBuffer statBuffer);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct TimeSpec
|
||||
{
|
||||
public long Sec;
|
||||
public long Nsec;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct StatBuffer
|
||||
{
|
||||
public int Dev; /* ID of device containing file */
|
||||
public ushort Mode; /* Mode of file (see below) */
|
||||
public ushort NLink; /* Number of hard links */
|
||||
public ulong Ino; /* File serial number */
|
||||
public uint UID; /* User ID of the file */
|
||||
public uint GID; /* Group ID of the file */
|
||||
public int RDev; /* Device ID */
|
||||
|
||||
public TimeSpec ATimespec; /* time of last access */
|
||||
public TimeSpec MTimespec; /* time of last data modification */
|
||||
public TimeSpec CTimespec; /* time of last status change */
|
||||
public TimeSpec BirthTimespec; /* time of file creation(birth) */
|
||||
|
||||
public long Size; /* file size, in bytes */
|
||||
public long Blocks; /* blocks allocated for file */
|
||||
public int BlkSize; /* optimal blocksize for I/O */
|
||||
public uint Glags; /* user defined flags for file */
|
||||
public uint Gen; /* file generation number */
|
||||
public int LSpare; /* RESERVED: DO NOT USE! */
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
|
||||
public long[] QSpare; /* RESERVED: DO NOT USE! */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
using GVFS.Common.Tracing;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.IO.MemoryMappedFiles;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace FastFetch
|
||||
{
|
||||
public class NativeWindowsMethods
|
||||
{
|
||||
private const int AccessDeniedWin32Error = 5;
|
||||
|
||||
public static unsafe void WriteFile(ITracer tracer, byte* originalData, long originalSize, string destination)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (SafeFileHandle fileHandle = OpenForWrite(tracer, destination))
|
||||
{
|
||||
if (fileHandle.IsInvalid)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
byte* data = originalData;
|
||||
long size = originalSize;
|
||||
uint written = 0;
|
||||
while (size > 0)
|
||||
{
|
||||
uint toWrite = size < uint.MaxValue ? (uint)size : uint.MaxValue;
|
||||
if (!WriteFile(fileHandle, data, toWrite, out written, IntPtr.Zero))
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
size -= written;
|
||||
data = data + written;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("destination", destination);
|
||||
metadata.Add("exception", e.ToString());
|
||||
tracer.RelatedError(metadata, "Error writing file.");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryStatFileAndUpdateIndex(ITracer tracer, string path, MemoryMappedViewAccessor indexView, long offset)
|
||||
{
|
||||
try
|
||||
{
|
||||
FileInfo file = new FileInfo(path);
|
||||
if (file.Exists)
|
||||
{
|
||||
Index.IndexEntry indexEntry = new Index.IndexEntry(indexView, offset);
|
||||
indexEntry.Mtime = file.LastWriteTimeUtc;
|
||||
indexEntry.Ctime = file.CreationTimeUtc;
|
||||
indexEntry.Size = (uint)file.Length;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (System.Security.SecurityException)
|
||||
{
|
||||
// Skip these.
|
||||
}
|
||||
catch (System.UnauthorizedAccessException)
|
||||
{
|
||||
// Skip these.
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static SafeFileHandle OpenForWrite(ITracer tracer, string fileName)
|
||||
{
|
||||
SafeFileHandle handle = CreateFile(fileName, FileAccess.Write, FileShare.None, IntPtr.Zero, FileMode.Create, FileAttributes.Normal, IntPtr.Zero);
|
||||
if (handle.IsInvalid)
|
||||
{
|
||||
// If we get a access denied, try reverting the acls to defaults inherited by parent
|
||||
if (Marshal.GetLastWin32Error() == AccessDeniedWin32Error)
|
||||
{
|
||||
tracer.RelatedEvent(
|
||||
EventLevel.Warning,
|
||||
"FailedOpenForWrite",
|
||||
new EventMetadata
|
||||
{
|
||||
{ TracingConstants.MessageKey.WarningMessage, "Received access denied. Attempting to delete." },
|
||||
{ "FileName", fileName }
|
||||
});
|
||||
|
||||
File.SetAttributes(fileName, FileAttributes.Normal);
|
||||
File.Delete(fileName);
|
||||
|
||||
handle = CreateFile(fileName, FileAccess.Write, FileShare.None, IntPtr.Zero, FileMode.Create, FileAttributes.Normal, IntPtr.Zero);
|
||||
}
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern SafeFileHandle CreateFile(
|
||||
[MarshalAs(UnmanagedType.LPTStr)] string filename,
|
||||
[MarshalAs(UnmanagedType.U4)] FileAccess access,
|
||||
[MarshalAs(UnmanagedType.U4)] FileShare share,
|
||||
IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero
|
||||
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
|
||||
[MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes,
|
||||
IntPtr templateFile);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static unsafe extern bool WriteFile(
|
||||
SafeFileHandle file,
|
||||
byte* buffer,
|
||||
uint numberOfBytesToWrite,
|
||||
out uint numberOfBytesWritten,
|
||||
IntPtr overlapped);
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
using CommandLine;
|
||||
using GVFS.PlatformLoader;
|
||||
|
||||
namespace FastFetch
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
GVFSPlatformLoader.Initialize();
|
||||
Parser.Default.ParseArguments<FastFetchVerb>(args)
|
||||
.WithParsed(fastFetch => fastFetch.Execute());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("FastFetch")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("FastFetch")]
|
||||
[assembly: AssemblyCopyright("Copyright © Microsoft 2019")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("07f2a520-2ab7-46dd-97c0-75d8e988d55b")]
|
|
@ -1,34 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using GVFS.Common;
|
||||
|
||||
namespace FastFetch
|
||||
{
|
||||
public static class WorkingTree
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates all files in the working tree in a asynchronous parallel manner.
|
||||
/// Files found are sent to callback in chunks.
|
||||
/// Make no assumptions about ordering or how big chunks will be
|
||||
/// </summary>
|
||||
/// <param name="repoRoot"></param>
|
||||
/// <param name="asyncParallelCallback"></param>
|
||||
public static void ForAllFiles(string repoRoot, Action<string, FileInfo[]> asyncParallelCallback)
|
||||
{
|
||||
ForAllDirectories(new DirectoryInfo(repoRoot), asyncParallelCallback);
|
||||
}
|
||||
|
||||
public static void ForAllDirectories(DirectoryInfo dir, Action<string, FileInfo[]> asyncParallelCallback)
|
||||
{
|
||||
asyncParallelCallback(dir.FullName, dir.GetFiles());
|
||||
|
||||
Parallel.ForEach(
|
||||
dir.EnumerateDirectories().Where(subdir =>
|
||||
(!subdir.Name.Equals(GVFSConstants.DotGit.Root, StringComparison.OrdinalIgnoreCase) &&
|
||||
!subdir.Attributes.HasFlag(FileAttributes.ReparsePoint))),
|
||||
subdir => { ForAllDirectories(subdir, asyncParallelCallback); });
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<package >
|
||||
<metadata>
|
||||
<id>FastFetch</id>
|
||||
<version>0.1.0</version>
|
||||
<authors>Microsoft</authors>
|
||||
<projectUrl>https://github.com/Microsoft/GVFS</projectUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>
|
||||
An internal tool for quickly pulling large git repos from a GVFS-enabled git server.
|
||||
This application will be deprecated once this same functionality is added to the gvfs prefetch verb.
|
||||
</description>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\..\..\BuildOutput\FastFetch\bin\x64\Release\FastFetch.exe" target="tools"/>
|
||||
<file src="..\..\..\BuildOutput\FastFetch\bin\x64\Release\*.dll" target="tools"/>
|
||||
<file src="..\..\..\BuildOutput\FastFetch\bin\x64\Release\*.pdb" target="tools"/>
|
||||
</files>
|
||||
</package>
|
|
@ -47,9 +47,6 @@
|
|||
<SubType>Designer</SubType>
|
||||
</BuildProps>
|
||||
<GeneratedPackageConfig Include="packages.config" />
|
||||
<BuildProps Include="ProjFS.props">
|
||||
<SubType>Designer</SubType>
|
||||
</BuildProps>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="GetTargetFrameworkProperties" />
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
|
||||
<Import Project="GVFS.props" />
|
||||
|
||||
|
||||
<PropertyGroup>
|
||||
<OutDir>$(BuildOutputDir)\$(MSBuildProjectName)\bin\$(Platform)\$(Configuration)\</OutDir>
|
||||
<IntDir>$(BuildOutputDir)\$(MSBuildProjectName)\intermediate\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
<PropertyGroup Label="Parameters">
|
||||
<GVFSVersion>0.2.173.2</GVFSVersion>
|
||||
<GitPackageVersion>2.20190805.1</GitPackageVersion>
|
||||
<GitPackageVersion>2.20190806.1</GitPackageVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Label="DefaultSettings">
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ProjFSNativePackage>GVFS.ProjFS.2019.411.1</ProjFSNativePackage>
|
||||
<ProjFSManagedPackage>Microsoft.Windows.ProjFS.1.1.19156.1</ProjFSManagedPackage>
|
||||
</PropertyGroup>
|
||||
</Project>
|
|
@ -1,187 +0,0 @@
|
|||
using GVFS.Common.FileSystem;
|
||||
using GVFS.Common.Tracing;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
|
||||
namespace GVFS.Common.Database
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles setting up the database for storing data used by GVFS and
|
||||
/// managing the connections to the database
|
||||
/// </summary>
|
||||
public class GVFSDatabase : IGVFSConnectionPool, IDisposable
|
||||
{
|
||||
private const int InitialPooledConnections = 5;
|
||||
private const int MillisecondsWaitingToGetConnection = 50;
|
||||
|
||||
private bool disposed = false;
|
||||
private string databasePath;
|
||||
private IDbConnectionFactory connectionFactory;
|
||||
private BlockingCollection<IDbConnection> connectionPool;
|
||||
|
||||
public GVFSDatabase(PhysicalFileSystem fileSystem, string enlistmentRoot, IDbConnectionFactory connectionFactory, int initialPooledConnections = InitialPooledConnections)
|
||||
{
|
||||
this.connectionPool = new BlockingCollection<IDbConnection>();
|
||||
this.databasePath = Path.Combine(enlistmentRoot, GVFSPlatform.Instance.Constants.DotGVFSRoot, GVFSConstants.DotGVFS.Databases.VFSForGit);
|
||||
this.connectionFactory = connectionFactory;
|
||||
|
||||
string folderPath = Path.GetDirectoryName(this.databasePath);
|
||||
fileSystem.CreateDirectory(folderPath);
|
||||
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < initialPooledConnections; i++)
|
||||
{
|
||||
this.connectionPool.Add(this.connectionFactory.OpenNewConnection(this.databasePath));
|
||||
}
|
||||
|
||||
this.Initialize();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new GVFSDatabaseException($"{nameof(GVFSDatabase)} constructor threw exception setting up connection pool and initializing", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (this.disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.disposed = true;
|
||||
this.connectionPool.CompleteAdding();
|
||||
while (this.connectionPool.TryTake(out IDbConnection connection))
|
||||
{
|
||||
connection.Dispose();
|
||||
}
|
||||
|
||||
this.connectionPool.Dispose();
|
||||
this.connectionPool = null;
|
||||
}
|
||||
|
||||
IDbConnection IGVFSConnectionPool.GetConnection()
|
||||
{
|
||||
if (this.disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(GVFSDatabase));
|
||||
}
|
||||
|
||||
IDbConnection connection;
|
||||
if (!this.connectionPool.TryTake(out connection, millisecondsTimeout: MillisecondsWaitingToGetConnection))
|
||||
{
|
||||
connection = this.connectionFactory.OpenNewConnection(this.databasePath);
|
||||
}
|
||||
|
||||
return new GVFSConnection(this, connection);
|
||||
}
|
||||
|
||||
private void ReturnToPool(IDbConnection connection)
|
||||
{
|
||||
if (this.disposed)
|
||||
{
|
||||
connection.Dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
this.connectionPool.TryAdd(connection);
|
||||
}
|
||||
catch (Exception ex) when (ex is InvalidOperationException || ex is ObjectDisposedException)
|
||||
{
|
||||
connection.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
IGVFSConnectionPool connectionPool = this;
|
||||
using (IDbConnection connection = connectionPool.GetConnection())
|
||||
using (IDbCommand command = connection.CreateCommand())
|
||||
{
|
||||
command.CommandText = "PRAGMA journal_mode=WAL;";
|
||||
command.ExecuteNonQuery();
|
||||
command.CommandText = "PRAGMA cache_size=-40000;";
|
||||
command.ExecuteNonQuery();
|
||||
command.CommandText = "PRAGMA synchronous=NORMAL;";
|
||||
command.ExecuteNonQuery();
|
||||
command.CommandText = "PRAGMA user_version;";
|
||||
object userVersion = command.ExecuteScalar();
|
||||
if (userVersion == null || Convert.ToInt64(userVersion) < 1)
|
||||
{
|
||||
command.CommandText = "PRAGMA user_version=1;";
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
PlaceholderTable.CreateTable(connection);
|
||||
SparseTable.CreateTable(connection);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This class is used to wrap a IDbConnection and return it to the connection pool when disposed
|
||||
/// </summary>
|
||||
private class GVFSConnection : IDbConnection
|
||||
{
|
||||
private IDbConnection connection;
|
||||
private GVFSDatabase database;
|
||||
|
||||
public GVFSConnection(GVFSDatabase database, IDbConnection connection)
|
||||
{
|
||||
this.database = database;
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
public string ConnectionString
|
||||
{
|
||||
get => this.connection.ConnectionString;
|
||||
set => this.connection.ConnectionString = value;
|
||||
}
|
||||
|
||||
public int ConnectionTimeout => this.connection.ConnectionTimeout;
|
||||
|
||||
public string Database => this.connection.Database;
|
||||
|
||||
public ConnectionState State => this.connection.State;
|
||||
|
||||
public IDbTransaction BeginTransaction()
|
||||
{
|
||||
return this.connection.BeginTransaction();
|
||||
}
|
||||
|
||||
public IDbTransaction BeginTransaction(IsolationLevel il)
|
||||
{
|
||||
return this.connection.BeginTransaction(il);
|
||||
}
|
||||
|
||||
public void ChangeDatabase(string databaseName)
|
||||
{
|
||||
this.connection.ChangeDatabase(databaseName);
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
this.connection.Close();
|
||||
}
|
||||
|
||||
public IDbCommand CreateCommand()
|
||||
{
|
||||
return this.connection.CreateCommand();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.database.ReturnToPool(this.connection);
|
||||
}
|
||||
|
||||
public void Open()
|
||||
{
|
||||
this.connection.Open();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace GVFS.Common.Database
|
||||
{
|
||||
public class GVFSDatabaseException : Exception
|
||||
{
|
||||
public GVFSDatabaseException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
using System.Data;
|
||||
|
||||
namespace GVFS.Common.Database
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for the IDbCommand interface
|
||||
/// </summary>
|
||||
public static class IDbCommandExtensions
|
||||
{
|
||||
public static IDbDataParameter AddParameter(this IDbCommand command, string name, DbType dbType, object value)
|
||||
{
|
||||
IDbDataParameter parameter = command.CreateParameter();
|
||||
parameter.ParameterName = name;
|
||||
parameter.DbType = dbType;
|
||||
parameter.Value = value;
|
||||
command.Parameters.Add(parameter);
|
||||
return parameter;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
using System.Data;
|
||||
|
||||
namespace GVFS.Common.Database
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface used to open a new connection to a database
|
||||
/// </summary>
|
||||
public interface IDbConnectionFactory
|
||||
{
|
||||
IDbConnection OpenNewConnection(string databasePath);
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
using System.Data;
|
||||
|
||||
namespace GVFS.Common.Database
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for getting a pooled database connection
|
||||
/// </summary>
|
||||
public interface IGVFSConnectionPool
|
||||
{
|
||||
IDbConnection GetConnection();
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace GVFS.Common.Database
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for interacting with placeholders
|
||||
/// </summary>
|
||||
public interface IPlaceholderCollection
|
||||
{
|
||||
int GetCount();
|
||||
void GetAllEntries(out List<IPlaceholderData> filePlaceholders, out List<IPlaceholderData> folderPlaceholders);
|
||||
int GetFilePlaceholdersCount();
|
||||
int GetFolderPlaceholdersCount();
|
||||
|
||||
HashSet<string> GetAllFilePaths();
|
||||
|
||||
void AddPartialFolder(string path);
|
||||
void AddExpandedFolder(string path);
|
||||
void AddPossibleTombstoneFolder(string path);
|
||||
|
||||
void AddFile(string path, string sha);
|
||||
|
||||
void Remove(string path);
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
namespace GVFS.Common.Database
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for holding placeholder information
|
||||
/// </summary>
|
||||
public interface IPlaceholderData
|
||||
{
|
||||
string Path { get; }
|
||||
string Sha { get; }
|
||||
bool IsFolder { get; }
|
||||
bool IsExpandedFolder { get; }
|
||||
bool IsPossibleTombstoneFolder { get; }
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace GVFS.Common.Database
|
||||
{
|
||||
public interface ISparseCollection
|
||||
{
|
||||
HashSet<string> GetAll();
|
||||
|
||||
void Add(string directory);
|
||||
void Remove(string directory);
|
||||
}
|
||||
}
|
|
@ -1,262 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
|
||||
namespace GVFS.Common.Database
|
||||
{
|
||||
/// <summary>
|
||||
/// This class is for interacting with the Placeholder table in the SQLite database
|
||||
/// </summary>
|
||||
public class PlaceholderTable : IPlaceholderCollection
|
||||
{
|
||||
private IGVFSConnectionPool connectionPool;
|
||||
|
||||
public PlaceholderTable(IGVFSConnectionPool connectionPool)
|
||||
{
|
||||
this.connectionPool = connectionPool;
|
||||
}
|
||||
|
||||
public static void CreateTable(IDbConnection connection)
|
||||
{
|
||||
using (IDbCommand command = connection.CreateCommand())
|
||||
{
|
||||
command.CommandText = "CREATE TABLE IF NOT EXISTS [Placeholder] (path TEXT PRIMARY KEY COLLATE NOCASE, pathType TINYINT NOT NULL, sha char(40) ) WITHOUT ROWID;";
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
public int GetCount()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (IDbConnection connection = this.connectionPool.GetConnection())
|
||||
using (IDbCommand command = connection.CreateCommand())
|
||||
{
|
||||
command.CommandText = "SELECT count(path) FROM Placeholder;";
|
||||
return Convert.ToInt32(command.ExecuteScalar());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new GVFSDatabaseException($"{nameof(PlaceholderTable)}.{nameof(this.GetCount)} Exception", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void GetAllEntries(out List<IPlaceholderData> filePlaceholders, out List<IPlaceholderData> folderPlaceholders)
|
||||
{
|
||||
try
|
||||
{
|
||||
filePlaceholders = new List<IPlaceholderData>();
|
||||
folderPlaceholders = new List<IPlaceholderData>();
|
||||
using (IDbConnection connection = this.connectionPool.GetConnection())
|
||||
using (IDbCommand command = connection.CreateCommand())
|
||||
{
|
||||
command.CommandText = "SELECT path, pathType, sha FROM Placeholder;";
|
||||
using (IDataReader reader = command.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
PlaceholderData data = new PlaceholderData();
|
||||
data.Path = reader.GetString(0);
|
||||
data.PathType = (PlaceholderData.PlaceholderType)reader.GetByte(1);
|
||||
|
||||
if (!reader.IsDBNull(2))
|
||||
{
|
||||
data.Sha = reader.GetString(2);
|
||||
}
|
||||
|
||||
if (data.PathType == PlaceholderData.PlaceholderType.File)
|
||||
{
|
||||
filePlaceholders.Add(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
folderPlaceholders.Add(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new GVFSDatabaseException($"{nameof(PlaceholderTable)}.{nameof(this.GetAllEntries)} Exception", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public HashSet<string> GetAllFilePaths()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (IDbConnection connection = this.connectionPool.GetConnection())
|
||||
using (IDbCommand command = connection.CreateCommand())
|
||||
{
|
||||
HashSet<string> fileEntries = new HashSet<string>();
|
||||
command.CommandText = $"SELECT path FROM Placeholder WHERE pathType = {(int)PlaceholderData.PlaceholderType.File};";
|
||||
using (IDataReader reader = command.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
fileEntries.Add(reader.GetString(0));
|
||||
}
|
||||
}
|
||||
|
||||
return fileEntries;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new GVFSDatabaseException($"{nameof(PlaceholderTable)}.{nameof(this.GetAllFilePaths)} Exception", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddPlaceholderData(IPlaceholderData data)
|
||||
{
|
||||
if (data.IsFolder)
|
||||
{
|
||||
if (data.IsExpandedFolder)
|
||||
{
|
||||
this.AddExpandedFolder(data.Path);
|
||||
}
|
||||
else if (data.IsPossibleTombstoneFolder)
|
||||
{
|
||||
this.AddPossibleTombstoneFolder(data.Path);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.AddPartialFolder(data.Path);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.AddFile(data.Path, data.Sha);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddFile(string path, string sha)
|
||||
{
|
||||
if (sha == null || sha.Length != 40)
|
||||
{
|
||||
throw new GVFSDatabaseException($"Invalid SHA '{sha ?? "null"}' for file {path}", innerException: null);
|
||||
}
|
||||
|
||||
this.Insert(new PlaceholderData() { Path = path, PathType = PlaceholderData.PlaceholderType.File, Sha = sha });
|
||||
}
|
||||
|
||||
public void AddPartialFolder(string path)
|
||||
{
|
||||
this.Insert(new PlaceholderData() { Path = path, PathType = PlaceholderData.PlaceholderType.PartialFolder });
|
||||
}
|
||||
|
||||
public void AddExpandedFolder(string path)
|
||||
{
|
||||
this.Insert(new PlaceholderData() { Path = path, PathType = PlaceholderData.PlaceholderType.ExpandedFolder });
|
||||
}
|
||||
|
||||
public void AddPossibleTombstoneFolder(string path)
|
||||
{
|
||||
this.Insert(new PlaceholderData() { Path = path, PathType = PlaceholderData.PlaceholderType.PossibleTombstoneFolder });
|
||||
}
|
||||
|
||||
public void Remove(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (IDbConnection connection = this.connectionPool.GetConnection())
|
||||
using (IDbCommand command = connection.CreateCommand())
|
||||
{
|
||||
command.CommandText = "DELETE FROM Placeholder WHERE path = @path;";
|
||||
command.AddParameter("@path", DbType.String, path);
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new GVFSDatabaseException($"{nameof(PlaceholderTable)}.{nameof(this.Remove)}({path}) Exception", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public int GetFilePlaceholdersCount()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (IDbConnection connection = this.connectionPool.GetConnection())
|
||||
using (IDbCommand command = connection.CreateCommand())
|
||||
{
|
||||
command.CommandText = $"SELECT count(path) FROM Placeholder WHERE pathType = {(int)PlaceholderData.PlaceholderType.File};";
|
||||
return Convert.ToInt32(command.ExecuteScalar());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new GVFSDatabaseException($"{nameof(PlaceholderTable)}.{nameof(this.GetCount)} Exception", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public int GetFolderPlaceholdersCount()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (IDbConnection connection = this.connectionPool.GetConnection())
|
||||
using (IDbCommand command = connection.CreateCommand())
|
||||
{
|
||||
command.CommandText = $"SELECT count(path) FROM Placeholder WHERE pathType = {(int)PlaceholderData.PlaceholderType.PartialFolder};";
|
||||
return Convert.ToInt32(command.ExecuteScalar());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new GVFSDatabaseException($"{nameof(PlaceholderTable)}.{nameof(this.GetCount)} Exception", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void Insert(PlaceholderData placeholder)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (IDbConnection connection = this.connectionPool.GetConnection())
|
||||
using (IDbCommand command = connection.CreateCommand())
|
||||
{
|
||||
command.CommandText = "INSERT OR REPLACE INTO Placeholder (path, pathType, sha) VALUES (@path, @pathType, @sha);";
|
||||
command.AddParameter("@path", DbType.String, placeholder.Path);
|
||||
command.AddParameter("@pathType", DbType.Int32, (int)placeholder.PathType);
|
||||
|
||||
if (placeholder.Sha == null)
|
||||
{
|
||||
command.AddParameter("@sha", DbType.String, DBNull.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
command.AddParameter("@sha", DbType.String, placeholder.Sha);
|
||||
}
|
||||
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new GVFSDatabaseException($"{nameof(PlaceholderTable)}.{nameof(this.Insert)}({placeholder.Path}, {placeholder.PathType}, {placeholder.Sha}) Exception", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public class PlaceholderData : IPlaceholderData
|
||||
{
|
||||
public enum PlaceholderType
|
||||
{
|
||||
File = 0,
|
||||
PartialFolder = 1,
|
||||
ExpandedFolder = 2,
|
||||
PossibleTombstoneFolder = 3,
|
||||
}
|
||||
|
||||
public string Path { get; set; }
|
||||
public PlaceholderType PathType { get; set; }
|
||||
public string Sha { get; set; }
|
||||
|
||||
public bool IsFolder => this.PathType != PlaceholderType.File;
|
||||
|
||||
public bool IsExpandedFolder => this.PathType == PlaceholderType.ExpandedFolder;
|
||||
|
||||
public bool IsPossibleTombstoneFolder => this.PathType == PlaceholderType.PossibleTombstoneFolder;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
|
||||
namespace GVFS.Common.Database
|
||||
{
|
||||
public class SparseTable : ISparseCollection
|
||||
{
|
||||
private IGVFSConnectionPool connectionPool;
|
||||
|
||||
public SparseTable(IGVFSConnectionPool connectionPool)
|
||||
{
|
||||
this.connectionPool = connectionPool;
|
||||
}
|
||||
|
||||
public static string NormalizePath(string path)
|
||||
{
|
||||
return path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar).Trim().Trim(Path.DirectorySeparatorChar);
|
||||
}
|
||||
|
||||
public static void CreateTable(IDbConnection connection)
|
||||
{
|
||||
using (IDbCommand command = connection.CreateCommand())
|
||||
{
|
||||
command.CommandText = "CREATE TABLE IF NOT EXISTS [Sparse] (path TEXT PRIMARY KEY COLLATE NOCASE) WITHOUT ROWID;";
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(string directoryPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (IDbConnection connection = this.connectionPool.GetConnection())
|
||||
using (IDbCommand command = connection.CreateCommand())
|
||||
{
|
||||
command.CommandText = "INSERT OR REPLACE INTO Sparse (path) VALUES (@path);";
|
||||
command.AddParameter("@path", DbType.String, NormalizePath(directoryPath));
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new GVFSDatabaseException($"{nameof(SparseTable)}.{nameof(this.Add)}({directoryPath}) Exception: {ex.ToString()}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public HashSet<string> GetAll()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (IDbConnection connection = this.connectionPool.GetConnection())
|
||||
using (IDbCommand command = connection.CreateCommand())
|
||||
{
|
||||
HashSet<string> directories = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
command.CommandText = $"SELECT path FROM Sparse;";
|
||||
using (IDataReader reader = command.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
directories.Add(reader.GetString(0));
|
||||
}
|
||||
}
|
||||
|
||||
return directories;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new GVFSDatabaseException($"{nameof(SparseTable)}.{nameof(this.GetAll)} Exception: {ex.ToString()}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove(string directoryPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (IDbConnection connection = this.connectionPool.GetConnection())
|
||||
using (IDbCommand command = connection.CreateCommand())
|
||||
{
|
||||
command.CommandText = "DELETE FROM Sparse WHERE path = @path;";
|
||||
command.AddParameter("@path", DbType.String, NormalizePath(directoryPath));
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new GVFSDatabaseException($"{nameof(SparseTable)}.{nameof(this.Remove)}({directoryPath}) Exception: {ex.ToString()}", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
using GVFS.Common.FileSystem;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
|
||||
namespace GVFS.Common.Database
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles creating connections to SQLite database and checking for issues with the database
|
||||
/// </summary>
|
||||
public class SqliteDatabase : IDbConnectionFactory
|
||||
{
|
||||
public static bool HasIssue(string databasePath, PhysicalFileSystem filesystem, out string issue)
|
||||
{
|
||||
issue = null;
|
||||
|
||||
if (filesystem.FileExists(databasePath))
|
||||
{
|
||||
List<string> integrityCheckResults = new List<string>();
|
||||
|
||||
try
|
||||
{
|
||||
string sqliteConnectionString = CreateConnectionString(databasePath);
|
||||
using (SqliteConnection integrityConnection = new SqliteConnection(sqliteConnectionString))
|
||||
{
|
||||
integrityConnection.Open();
|
||||
|
||||
using (SqliteCommand pragmaCommand = integrityConnection.CreateCommand())
|
||||
{
|
||||
pragmaCommand.CommandText = "PRAGMA integrity_check;";
|
||||
using (SqliteDataReader reader = pragmaCommand.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
integrityCheckResults.Add(reader.GetString(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
issue = $"Exception while trying to access {databasePath}: {e.Message}";
|
||||
return true;
|
||||
}
|
||||
|
||||
// If pragma integrity_check finds no errors, a single row with the value 'ok' is returned
|
||||
// http://www.sqlite.org/pragma.html#pragma_integrity_check
|
||||
if (integrityCheckResults.Count != 1 || integrityCheckResults[0] != "ok")
|
||||
{
|
||||
issue = string.Join(",", integrityCheckResults);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static string CreateConnectionString(string databasePath)
|
||||
{
|
||||
// Share-Cache mode allows multiple connections from the same process to share the same data cache
|
||||
// http://www.sqlite.org/sharedcache.html
|
||||
return $"data source={databasePath};Cache=Shared";
|
||||
}
|
||||
|
||||
public IDbConnection OpenNewConnection(string databasePath)
|
||||
{
|
||||
SqliteConnection connection = new SqliteConnection(CreateConnectionString(databasePath));
|
||||
connection.Open();
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
using GVFS.Common.Database;
|
||||
using GVFS.Common.FileSystem;
|
||||
using GVFS.Common.Tracing;
|
||||
using GVFS.DiskLayoutUpgrades;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace GVFS.Common.DiskLayoutUpgrades
|
||||
{
|
||||
public abstract class DiskLayoutUpgrade_SqlitePlaceholders : DiskLayoutUpgrade.MajorUpgrade
|
||||
{
|
||||
public override bool TryUpgrade(ITracer tracer, string enlistmentRoot)
|
||||
{
|
||||
string dotGVFSRoot = Path.Combine(enlistmentRoot, GVFSPlatform.Instance.Constants.DotGVFSRoot);
|
||||
try
|
||||
{
|
||||
PhysicalFileSystem fileSystem = new PhysicalFileSystem();
|
||||
string error;
|
||||
LegacyPlaceholderListDatabase placeholderList;
|
||||
if (!LegacyPlaceholderListDatabase.TryCreate(
|
||||
tracer,
|
||||
Path.Combine(dotGVFSRoot, GVFSConstants.DotGVFS.Databases.PlaceholderList),
|
||||
fileSystem,
|
||||
out placeholderList,
|
||||
out error))
|
||||
{
|
||||
tracer.RelatedError("Failed to open placeholder list database: " + error);
|
||||
return false;
|
||||
}
|
||||
|
||||
using (placeholderList)
|
||||
using (GVFSDatabase database = new GVFSDatabase(fileSystem, enlistmentRoot, new SqliteDatabase()))
|
||||
{
|
||||
PlaceholderTable placeholders = new PlaceholderTable(database);
|
||||
List<IPlaceholderData> oldPlaceholderEntries = placeholderList.GetAllEntries();
|
||||
foreach (IPlaceholderData entry in oldPlaceholderEntries)
|
||||
{
|
||||
placeholders.AddPlaceholderData(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
tracer.RelatedError("Error updating placeholder list database to SQLite: " + ex.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.TryIncrementMajorVersion(tracer, enlistmentRoot))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
using GVFS.Common.Tracing;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace GVFS.Common.FileSystem
|
||||
{
|
||||
public interface IKernelDriver
|
||||
{
|
||||
bool EnumerationExpandsDirectories { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether file sizes required to write/update placeholders
|
||||
/// </summary>
|
||||
bool EmptyPlaceholdersRequireFileSize { get; }
|
||||
|
||||
string LogsFolderPath { get; }
|
||||
bool IsSupported(string normalizedEnlistmentRootPath, out string warning, out string error);
|
||||
bool TryFlushLogs(out string errors);
|
||||
bool TryPrepareFolderForCallbacks(string folderPath, out string error, out Exception exception);
|
||||
bool IsReady(JsonTracer tracer, string enlistmentRoot, TextWriter output, out string error);
|
||||
bool IsGVFSUpgradeSupported();
|
||||
bool RegisterForOfflineIO();
|
||||
}
|
||||
}
|
|
@ -22,7 +22,6 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="LibGit2Sharp.NativeBinaries" Version="2.0.278" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="2.2.4" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
<PackageReference Include="NuGet.Commands" Version="4.9.2" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.0.2">
|
||||
|
|
|
@ -18,7 +18,6 @@ namespace GVFS.Common
|
|||
|
||||
public static GVFSPlatform Instance { get; private set; }
|
||||
|
||||
public abstract IKernelDriver KernelDriver { get; }
|
||||
public abstract IGitInstallation GitInstallation { get; }
|
||||
public abstract IDiskLayoutUpgradeData DiskLayoutUpgrade { get; }
|
||||
public abstract IPlatformFileSystem FileSystem { get; }
|
||||
|
|
|
@ -114,7 +114,7 @@ namespace GVFS.Upgrader
|
|||
|
||||
protected virtual bool IsGVFSUpgradeSupported()
|
||||
{
|
||||
return GVFSPlatform.Instance.KernelDriver.IsGVFSUpgradeSupported();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual bool IsServiceInstalledAndNotRunning()
|
||||
|
|
|
@ -1,438 +0,0 @@
|
|||
using GVFS.Common.Database;
|
||||
using GVFS.Common.FileSystem;
|
||||
using GVFS.Common.Tracing;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace GVFS.Common
|
||||
{
|
||||
public class LegacyPlaceholderListDatabase : FileBasedCollection, IPlaceholderCollection
|
||||
{
|
||||
// Special folder values must:
|
||||
// - Be 40 characters long
|
||||
// - Not be a valid SHA-1 value (to avoid collisions with files)
|
||||
public const string PartialFolderValue = " PARTIAL FOLDER";
|
||||
public const string ExpandedFolderValue = " EXPANDED FOLDER";
|
||||
public const string PossibleTombstoneFolderValue = " POSSIBLE TOMBSTONE FOLDER";
|
||||
|
||||
private const char PathTerminator = '\0';
|
||||
|
||||
// This list holds placeholder entries that are created between calls to
|
||||
// GetAllEntries and WriteAllEntriesAndFlush.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// 1) VFS4G parses the updated index (as part of a projection change)
|
||||
// 2) VFS4G starts the work to update placeholders
|
||||
// 3) VFS4G calls GetAllEntries
|
||||
// 4) VFS4G starts updating placeholders
|
||||
// 5) Some application reads a pure-virtual file (creating a new placeholder) while VFS4G is updating existing placeholders.
|
||||
// That new placeholder is added to placeholderChangesWhileRebuildingList.
|
||||
// 6) VFS4G completes updating the placeholders and calls WriteAllEntriesAndFlush.
|
||||
// Note: this list does *not* include the placeholders created in step 5, as the were not included in GetAllEntries.
|
||||
// 7) WriteAllEntriesAndFlush writes *both* the entires in placeholderDataEntries and those that were passed in as the parameter.
|
||||
//
|
||||
// This scenario is covered in the unit test PlaceholderDatabaseTests.HandlesRaceBetweenAddAndWriteAllEntries
|
||||
//
|
||||
// Because of this list, callers must always call WriteAllEntries after calling GetAllEntries.
|
||||
//
|
||||
// This list must always be accessed from inside one of FileBasedCollection's synchronizedAction callbacks because
|
||||
// there is race potential between creating the queue, adding to the queue, and writing to the data file.
|
||||
private List<PlaceholderDataEntry> placeholderChangesWhileRebuildingList;
|
||||
|
||||
private int count;
|
||||
|
||||
private LegacyPlaceholderListDatabase(ITracer tracer, PhysicalFileSystem fileSystem, string dataFilePath)
|
||||
: base(tracer, fileSystem, dataFilePath, collectionAppendsDirectlyToFile: true)
|
||||
{
|
||||
}
|
||||
|
||||
public static bool TryCreate(ITracer tracer, string dataFilePath, PhysicalFileSystem fileSystem, out LegacyPlaceholderListDatabase output, out string error)
|
||||
{
|
||||
LegacyPlaceholderListDatabase temp = new LegacyPlaceholderListDatabase(tracer, fileSystem, dataFilePath);
|
||||
|
||||
// We don't want to cache placeholders so this just serves to validate early and populate count.
|
||||
if (!temp.TryLoadFromDisk<string, string>(
|
||||
temp.TryParseAddLine,
|
||||
temp.TryParseRemoveLine,
|
||||
(key, value) => temp.count++,
|
||||
out error))
|
||||
{
|
||||
temp = null;
|
||||
output = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
error = null;
|
||||
output = temp;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Count is "estimated" because it's simply (# adds - # deletes). There is nothing to prevent
|
||||
/// multiple adds or deletes of the same path from being double counted
|
||||
/// </summary>
|
||||
public int GetCount()
|
||||
{
|
||||
return this.count;
|
||||
}
|
||||
|
||||
public void AddFile(string path, string sha)
|
||||
{
|
||||
this.AddAndFlush(path, sha);
|
||||
}
|
||||
|
||||
public void AddPartialFolder(string path)
|
||||
{
|
||||
this.AddAndFlush(path, PartialFolderValue);
|
||||
}
|
||||
|
||||
public void AddExpandedFolder(string path)
|
||||
{
|
||||
this.AddAndFlush(path, ExpandedFolderValue);
|
||||
}
|
||||
|
||||
public void AddPossibleTombstoneFolder(string path)
|
||||
{
|
||||
this.AddAndFlush(path, PossibleTombstoneFolderValue);
|
||||
}
|
||||
|
||||
public void Remove(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.WriteRemoveEntry(
|
||||
path,
|
||||
() =>
|
||||
{
|
||||
this.count--;
|
||||
if (this.placeholderChangesWhileRebuildingList != null)
|
||||
{
|
||||
this.placeholderChangesWhileRebuildingList.Add(new PlaceholderDataEntry(path));
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new FileBasedCollectionException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all entries and prepares the PlaceholderListDatabase for a call to WriteAllEntriesAndFlush.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// GetAllEntries was called (a second time) without first calling WriteAllEntriesAndFlush.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// Usage notes:
|
||||
/// - All calls to GetAllEntries must be paired with a subsequent call to WriteAllEntriesAndFlush
|
||||
/// - If WriteAllEntriesAndFlush is *not* called entries that were added to the PlaceholderListDatabase after
|
||||
/// calling GetAllEntries will be lost
|
||||
/// </remarks>
|
||||
public List<IPlaceholderData> GetAllEntries()
|
||||
{
|
||||
try
|
||||
{
|
||||
List<IPlaceholderData> placeholders = new List<IPlaceholderData>(Math.Max(1, this.count));
|
||||
|
||||
string error;
|
||||
if (!this.TryLoadFromDisk<string, string>(
|
||||
this.TryParseAddLine,
|
||||
this.TryParseRemoveLine,
|
||||
(key, value) => placeholders.Add(new PlaceholderData(path: key, fileShaOrFolderValue: value)),
|
||||
out error,
|
||||
() =>
|
||||
{
|
||||
if (this.placeholderChangesWhileRebuildingList != null)
|
||||
{
|
||||
throw new InvalidOperationException($"PlaceholderListDatabase should always flush queue placeholders using WriteAllEntriesAndFlush before calling {nameof(this.GetAllEntries)} again.");
|
||||
}
|
||||
|
||||
this.placeholderChangesWhileRebuildingList = new List<PlaceholderDataEntry>();
|
||||
}))
|
||||
{
|
||||
throw new InvalidDataException(error);
|
||||
}
|
||||
|
||||
return placeholders;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new FileBasedCollectionException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all entries and prepares the PlaceholderListDatabase for a call to WriteAllEntriesAndFlush.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// GetAllEntries was called (a second time) without first calling WriteAllEntriesAndFlush.
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// Usage notes:
|
||||
/// - All calls to GetAllEntries must be paired with a subsequent call to WriteAllEntriesAndFlush
|
||||
/// - If WriteAllEntriesAndFlush is *not* called entries that were added to the PlaceholderListDatabase after
|
||||
/// calling GetAllEntries will be lost
|
||||
/// </remarks>
|
||||
public void GetAllEntries(out List<IPlaceholderData> filePlaceholders, out List<IPlaceholderData> folderPlaceholders)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<IPlaceholderData> filePlaceholdersFromDisk = new List<IPlaceholderData>(Math.Max(1, this.count));
|
||||
List<IPlaceholderData> folderPlaceholdersFromDisk = new List<IPlaceholderData>(Math.Max(1, (int)(this.count * .3)));
|
||||
|
||||
string error;
|
||||
if (!this.TryLoadFromDisk<string, string>(
|
||||
this.TryParseAddLine,
|
||||
this.TryParseRemoveLine,
|
||||
(key, value) =>
|
||||
{
|
||||
if (PlaceholderData.IsShaAFolder(value))
|
||||
{
|
||||
folderPlaceholdersFromDisk.Add(new PlaceholderData(path: key, fileShaOrFolderValue: value));
|
||||
}
|
||||
else
|
||||
{
|
||||
filePlaceholdersFromDisk.Add(new PlaceholderData(path: key, fileShaOrFolderValue: value));
|
||||
}
|
||||
},
|
||||
out error,
|
||||
() =>
|
||||
{
|
||||
if (this.placeholderChangesWhileRebuildingList != null)
|
||||
{
|
||||
throw new InvalidOperationException($"PlaceholderListDatabase should always flush queue placeholders using WriteAllEntriesAndFlush before calling {(nameof(this.GetAllEntries))} again.");
|
||||
}
|
||||
|
||||
this.placeholderChangesWhileRebuildingList = new List<PlaceholderDataEntry>();
|
||||
}))
|
||||
{
|
||||
throw new InvalidDataException(error);
|
||||
}
|
||||
|
||||
filePlaceholders = filePlaceholdersFromDisk;
|
||||
folderPlaceholders = folderPlaceholdersFromDisk;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new FileBasedCollectionException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public HashSet<string> GetAllFilePaths()
|
||||
{
|
||||
try
|
||||
{
|
||||
HashSet<string> filePlaceholderPaths = new HashSet<string>(StringComparer.Ordinal);
|
||||
|
||||
string error;
|
||||
if (!this.TryLoadFromDisk<string, string>(
|
||||
this.TryParseAddLine,
|
||||
this.TryParseRemoveLine,
|
||||
(key, value) =>
|
||||
{
|
||||
if (!PlaceholderData.IsShaAFolder(value))
|
||||
{
|
||||
filePlaceholderPaths.Add(key);
|
||||
}
|
||||
},
|
||||
out error))
|
||||
{
|
||||
throw new InvalidDataException(error);
|
||||
}
|
||||
|
||||
return filePlaceholderPaths;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new FileBasedCollectionException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public int GetFilePlaceholdersCount()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public int GetFolderPlaceholdersCount()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void WriteAllEntriesAndFlush(IEnumerable<IPlaceholderData> updatedPlaceholders)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.WriteAndReplaceDataFile(() => this.GenerateDataLines(updatedPlaceholders));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new FileBasedCollectionException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<string> GenerateDataLines(IEnumerable<IPlaceholderData> updatedPlaceholders)
|
||||
{
|
||||
HashSet<string> keys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
this.count = 0;
|
||||
foreach (IPlaceholderData updated in updatedPlaceholders)
|
||||
{
|
||||
if (keys.Add(updated.Path))
|
||||
{
|
||||
this.count++;
|
||||
}
|
||||
|
||||
yield return this.FormatAddLine(updated.Path + PathTerminator + updated.Sha);
|
||||
}
|
||||
|
||||
if (this.placeholderChangesWhileRebuildingList != null)
|
||||
{
|
||||
foreach (PlaceholderDataEntry entry in this.placeholderChangesWhileRebuildingList)
|
||||
{
|
||||
if (entry.DeleteEntry)
|
||||
{
|
||||
if (keys.Remove(entry.Path))
|
||||
{
|
||||
this.count--;
|
||||
yield return this.FormatRemoveLine(entry.Path);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (keys.Add(entry.Path))
|
||||
{
|
||||
this.count++;
|
||||
}
|
||||
|
||||
yield return this.FormatAddLine(entry.Path + PathTerminator + entry.Sha);
|
||||
}
|
||||
}
|
||||
|
||||
this.placeholderChangesWhileRebuildingList = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddAndFlush(string path, string sha)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.WriteAddEntry(
|
||||
path + PathTerminator + sha,
|
||||
() =>
|
||||
{
|
||||
this.count++;
|
||||
if (this.placeholderChangesWhileRebuildingList != null)
|
||||
{
|
||||
this.placeholderChangesWhileRebuildingList.Add(new PlaceholderDataEntry(path, sha));
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new FileBasedCollectionException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryParseAddLine(string line, out string key, out string value, out string error)
|
||||
{
|
||||
// Expected: <Placeholder-Path>\0<40-Char-SHA1>
|
||||
int idx = line.IndexOf(PathTerminator);
|
||||
if (idx < 0)
|
||||
{
|
||||
key = null;
|
||||
value = null;
|
||||
error = "Add line missing path terminator: " + line;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (idx + 1 + GVFSConstants.ShaStringLength != line.Length)
|
||||
{
|
||||
key = null;
|
||||
value = null;
|
||||
error = $"Invalid SHA1 length {line.Length - idx - 1}: " + line;
|
||||
return false;
|
||||
}
|
||||
|
||||
key = line.Substring(0, idx);
|
||||
value = line.Substring(idx + 1, GVFSConstants.ShaStringLength);
|
||||
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryParseRemoveLine(string line, out string key, out string error)
|
||||
{
|
||||
// The key is a path taking the entire line.
|
||||
key = line;
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public class PlaceholderData : IPlaceholderData
|
||||
{
|
||||
public PlaceholderData(string path, string fileShaOrFolderValue)
|
||||
{
|
||||
this.Path = path;
|
||||
this.Sha = fileShaOrFolderValue;
|
||||
}
|
||||
|
||||
public string Path { get; }
|
||||
public string Sha { get; }
|
||||
|
||||
public bool IsFolder
|
||||
{
|
||||
get
|
||||
{
|
||||
return IsShaAFolder(this.Sha);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsExpandedFolder
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.Sha.Equals(ExpandedFolderValue, StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsPossibleTombstoneFolder
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.Sha.Equals(PossibleTombstoneFolderValue, StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsShaAFolder(string shaValue)
|
||||
{
|
||||
return shaValue.Equals(PartialFolderValue, StringComparison.Ordinal) ||
|
||||
shaValue.Equals(ExpandedFolderValue, StringComparison.Ordinal) ||
|
||||
shaValue.Equals(PossibleTombstoneFolderValue, StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
|
||||
private class PlaceholderDataEntry
|
||||
{
|
||||
public PlaceholderDataEntry(string path, string sha)
|
||||
{
|
||||
this.Path = path;
|
||||
this.Sha = sha;
|
||||
this.DeleteEntry = false;
|
||||
}
|
||||
|
||||
public PlaceholderDataEntry(string path)
|
||||
{
|
||||
this.Path = path;
|
||||
this.Sha = null;
|
||||
this.DeleteEntry = true;
|
||||
}
|
||||
|
||||
public string Path { get; }
|
||||
public string Sha { get; }
|
||||
public bool DeleteEntry { get; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,7 +27,8 @@ namespace GVFS.Common.Maintenance
|
|||
public class PackfileMaintenanceStep : GitMaintenanceStep
|
||||
{
|
||||
public const string PackfileLastRunFileName = "pack-maintenance.time";
|
||||
public const string DefaultBatchSize = "2g";
|
||||
public const string DefaultBatchSize = "2g";
|
||||
private const string MultiPackIndexLock = "multi-pack-index.lock";
|
||||
private readonly bool forceRun;
|
||||
private readonly string batchSize;
|
||||
|
||||
|
@ -105,15 +106,20 @@ namespace GVFS.Common.Maintenance
|
|||
activity.RelatedWarning($"Skipping {nameof(PackfileMaintenanceStep)} due to git pids {string.Join(",", processIds)}", Keywords.Telemetry);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.GetPackFilesInfo(out int beforeCount, out long beforeSize, out bool hasKeep);
|
||||
}
|
||||
|
||||
this.GetPackFilesInfo(out int beforeCount, out long beforeSize, out bool hasKeep);
|
||||
|
||||
if (!hasKeep)
|
||||
{
|
||||
activity.RelatedWarning(this.CreateEventMetadata(), "Skipping pack maintenance due to no .keep file.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
string multiPackIndexLockPath = Path.Combine(this.Context.Enlistment.GitPackRoot, MultiPackIndexLock);
|
||||
this.Context.FileSystem.TryDeleteFile(multiPackIndexLockPath);
|
||||
|
||||
this.RunGitCommand((process) => process.WriteMultiPackIndex(this.Context.Enlistment.GitObjectsRoot), nameof(GitProcess.WriteMultiPackIndex));
|
||||
|
||||
// If a LibGit2Repo is active, then it may hold handles to the .idx and .pack files we want
|
||||
// to delete during the 'git multi-pack-index expire' step. If one starts during the step,
|
||||
|
|
|
@ -10,7 +10,6 @@ namespace GVFS.Common.Maintenance
|
|||
public class PostFetchStep : GitMaintenanceStep
|
||||
{
|
||||
private const string CommitGraphChainLock = "commit-graph-chain.lock";
|
||||
private const string MultiPackIndexLock = "multi-pack-index.lock";
|
||||
private List<string> packIndexes;
|
||||
|
||||
public PostFetchStep(GVFSContext context, List<string> packIndexes, bool requireObjectCacheLock = true)
|
||||
|
@ -23,20 +22,6 @@ namespace GVFS.Common.Maintenance
|
|||
|
||||
protected override void PerformMaintenance()
|
||||
{
|
||||
using (ITracer activity = this.Context.Tracer.StartActivity("TryWriteMultiPackIndex", EventLevel.Informational))
|
||||
{
|
||||
string multiPackIndexLockPath = Path.Combine(this.Context.Enlistment.GitPackRoot, MultiPackIndexLock);
|
||||
this.Context.FileSystem.TryDeleteFile(multiPackIndexLockPath);
|
||||
|
||||
this.RunGitCommand((process) => process.WriteMultiPackIndex(this.Context.Enlistment.GitObjectsRoot), nameof(GitProcess.WriteMultiPackIndex));
|
||||
|
||||
GitProcess.Result verifyResult = this.RunGitCommand((process) => process.VerifyMultiPackIndex(this.Context.Enlistment.GitObjectsRoot), nameof(GitProcess.VerifyMultiPackIndex));
|
||||
if (!this.Stopping && verifyResult.ExitCodeIsFailure)
|
||||
{
|
||||
this.LogErrorAndRewriteMultiPackIndex(activity);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.packIndexes == null || this.packIndexes.Count == 0)
|
||||
{
|
||||
this.Context.Tracer.RelatedInfo(this.Area + ": Skipping commit-graph write due to no new packfiles");
|
||||
|
|
|
@ -312,31 +312,6 @@ namespace GVFS.Common.NamedPipes
|
|||
}
|
||||
}
|
||||
|
||||
public class EnableAndAttachProjFSRequest
|
||||
{
|
||||
public const string Header = nameof(EnableAndAttachProjFSRequest);
|
||||
|
||||
public string EnlistmentRoot { get; set; }
|
||||
|
||||
public static EnableAndAttachProjFSRequest FromMessage(Message message)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<EnableAndAttachProjFSRequest>(message.Body);
|
||||
}
|
||||
|
||||
public Message ToMessage()
|
||||
{
|
||||
return new Message(Header, JsonConvert.SerializeObject(this));
|
||||
}
|
||||
|
||||
public class Response : BaseResponse<EnableAndAttachProjFSRequest>
|
||||
{
|
||||
public static Response FromMessage(Message message)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<Response>(message.Body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class GetActiveRepoListRequest
|
||||
{
|
||||
public const string Header = nameof(GetActiveRepoListRequest);
|
||||
|
|
|
@ -1,130 +0,0 @@
|
|||
using CommandLine;
|
||||
using GVFS.Common;
|
||||
using GVFS.Common.NamedPipes;
|
||||
using GVFS.Platform.Mac;
|
||||
using GVFS.Platform.Windows;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace GVFS.FunctionalTests.LockHolder
|
||||
{
|
||||
public class AcquireGVFSLockVerb
|
||||
{
|
||||
private static string fullCommand = "GVFS.FunctionalTests.LockHolder";
|
||||
|
||||
[Option(
|
||||
"skip-release-lock",
|
||||
Default = false,
|
||||
Required = false,
|
||||
HelpText = "Skip releasing the GVFS lock when exiting the program.")]
|
||||
public bool NoReleaseLock { get; set; }
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
string errorMessage;
|
||||
string enlistmentRoot;
|
||||
if (!TryGetGVFSEnlistmentRootImplementation(Environment.CurrentDirectory, out enlistmentRoot, out errorMessage))
|
||||
{
|
||||
throw new Exception("Unable to get GVFS Enlistment root: " + errorMessage);
|
||||
}
|
||||
|
||||
string enlistmentPipename = GetNamedPipeNameImplementation(enlistmentRoot);
|
||||
|
||||
AcquireLock(enlistmentPipename);
|
||||
|
||||
Console.ReadLine();
|
||||
|
||||
if (!this.NoReleaseLock)
|
||||
{
|
||||
ReleaseLock(enlistmentPipename, enlistmentRoot);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryGetGVFSEnlistmentRootImplementation(string directory, out string enlistmentRoot, out string errorMessage)
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
return MacPlatform.TryGetGVFSEnlistmentRootImplementation(directory, out enlistmentRoot, out errorMessage);
|
||||
}
|
||||
|
||||
// Not able to use WindowsPlatform here - because of its dependency on WindowsIdentity (and also kernel32.dll).
|
||||
enlistmentRoot = null;
|
||||
|
||||
string finalDirectory;
|
||||
if (!WindowsFileSystem.TryGetNormalizedPathImplementation(directory, out finalDirectory, out errorMessage))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const string dotGVFSRoot = ".gvfs";
|
||||
enlistmentRoot = Paths.GetRoot(finalDirectory, dotGVFSRoot);
|
||||
if (enlistmentRoot == null)
|
||||
{
|
||||
errorMessage = $"Failed to find the root directory for {dotGVFSRoot} in {finalDirectory}";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string GetNamedPipeNameImplementation(string enlistmentRoot)
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
return MacPlatform.GetNamedPipeNameImplementation(enlistmentRoot);
|
||||
}
|
||||
|
||||
// Not able to use WindowsPlatform here - because of its dependency on WindowsIdentity (and also kernel32.dll).
|
||||
return "GVFS_" + enlistmentRoot.ToUpper().Replace(':', '_');
|
||||
}
|
||||
|
||||
private static void AcquireLock(string enlistmentPipename)
|
||||
{
|
||||
using (NamedPipeClient pipeClient = new NamedPipeClient(enlistmentPipename))
|
||||
{
|
||||
if (!pipeClient.Connect())
|
||||
{
|
||||
throw new Exception("The repo does not appear to be mounted. Use 'gvfs status' to check.");
|
||||
}
|
||||
|
||||
int pid = Process.GetCurrentProcess().Id;
|
||||
|
||||
string result;
|
||||
if (!GVFSLock.TryAcquireGVFSLockForProcess(
|
||||
unattended: false,
|
||||
pipeClient: pipeClient,
|
||||
fullCommand: AcquireGVFSLockVerb.fullCommand,
|
||||
pid: pid,
|
||||
isElevated: false,
|
||||
isConsoleOutputRedirectedToFile: false,
|
||||
checkAvailabilityOnly: false,
|
||||
gvfsEnlistmentRoot: null,
|
||||
gitCommandSessionId: string.Empty,
|
||||
result: out result))
|
||||
{
|
||||
throw new Exception(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ReleaseLock(string enlistmentPipename, string enlistmentRoot)
|
||||
{
|
||||
using (NamedPipeClient pipeClient = new NamedPipeClient(enlistmentPipename))
|
||||
{
|
||||
if (!pipeClient.Connect())
|
||||
{
|
||||
throw new Exception("The repo does not appear to be mounted. Use 'gvfs status' to check.");
|
||||
}
|
||||
|
||||
int pid = Process.GetCurrentProcess().Id;
|
||||
|
||||
NamedPipeMessages.LockRequest request = new NamedPipeMessages.LockRequest(pid: pid, isElevated: false, checkAvailabilityOnly: false, parsedCommand: AcquireGVFSLockVerb.fullCommand, gitCommandSessionId: string.Empty);
|
||||
NamedPipeMessages.Message requestMessage = request.CreateMessage(NamedPipeMessages.ReleaseLock.Request);
|
||||
|
||||
pipeClient.SendRequest(requestMessage);
|
||||
NamedPipeMessages.ReleaseLock.Response response = response = new NamedPipeMessages.ReleaseLock.Response(pipeClient.ReadResponse());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
|
||||
</startup>
|
||||
</configuration>
|
|
@ -1,43 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\GVFS.Build\GVFS.cs.props" />
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<Platforms>x64</Platforms>
|
||||
<!-- see https://github.com/NuGet/Home/issues/4837 for reference to CopyLocalLockFileAssemblies -->
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
|
||||
<RuntimeIdentifiers>win-x64;osx-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<RootNamespace>GVFS.FunctionalTests.LockHolder</RootNamespace>
|
||||
<AssemblyName>GVFS.FunctionalTests.LockHolder</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64' ">
|
||||
<Version>$(GVFSVersion)</Version>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64' ">
|
||||
<Version>$(GVFSVersion)</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\GVFS.Platform.POSIX\POSIXFileSystem.Shared.cs" Link="POSIXFileSystem.Shared.cs" />
|
||||
<Compile Include="..\GVFS.Platform.POSIX\POSIXPlatform.Shared.cs" Link="POSIXPlatform.Shared.cs" />
|
||||
<Compile Include="..\GVFS.Platform.Mac\MacPlatform.Shared.cs" Link="MacPlatform.Shared.cs" />
|
||||
<Compile Include="..\GVFS.Platform.Windows\WindowsFileSystem.Shared.cs" Link="WindowsFileSystem.Shared.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GVFS.Common\GVFS.Common.csproj" />
|
||||
<ProjectReference Include="..\GVFS.Virtualization\GVFS.Virtualization.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommandLineParser" Version="2.1.1-beta" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,13 +0,0 @@
|
|||
using CommandLine;
|
||||
|
||||
namespace GVFS.FunctionalTests.LockHolder
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
Parser.Default.ParseArguments<AcquireGVFSLockVerb>(args)
|
||||
.WithParsed(acquireGVFSLock => acquireGVFSLock.Execute());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("GVFS.FunctionalTests.LockHolder")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("GVFS.FunctionalTests.LockHolder")]
|
||||
[assembly: AssemblyCopyright("Copyright © Microsoft 2019")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("fa273f69-5762-43d8-aea1-b4f08090d624")]
|
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="CommandLineParser" version="2.2.1" targetFramework="net461" />
|
||||
<package id="StyleCop.Analyzers" version="1.0.2" targetFramework="net461" developmentDependency="true" />
|
||||
<package id="System.Collections" version="4.0.11" targetFramework="net461" />
|
||||
<package id="System.Console" version="4.0.0" targetFramework="net461" />
|
||||
<package id="System.Diagnostics.Debug" version="4.0.11" targetFramework="net461" />
|
||||
<package id="System.Globalization" version="4.0.11" targetFramework="net461" />
|
||||
<package id="System.IO" version="4.1.0" targetFramework="net461" />
|
||||
<package id="System.Linq" version="4.1.0" targetFramework="net461" />
|
||||
<package id="System.Linq.Expressions" version="4.1.0" targetFramework="net461" />
|
||||
<package id="System.Reflection" version="4.1.0" targetFramework="net461" />
|
||||
<package id="System.Reflection.Extensions" version="4.0.1" targetFramework="net461" />
|
||||
<package id="System.Reflection.TypeExtensions" version="4.1.0" targetFramework="net461" />
|
||||
<package id="System.Resources.ResourceManager" version="4.0.1" targetFramework="net461" />
|
||||
<package id="System.Runtime" version="4.1.0" targetFramework="net461" />
|
||||
<package id="System.Runtime.Extensions" version="4.1.0" targetFramework="net461" />
|
||||
</packages>
|
|
@ -1,208 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\packages\NUnit.3.10.1\build\NUnit.props" Condition="Exists('..\..\..\packages\NUnit.3.10.1\build\NUnit.props')" />
|
||||
<Import Project="$(SolutionDir)\GVFS\GVFS.Build\GVFS.cs.props" />
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{0F0A008E-AB12-40EC-A671-37A541B08C7F}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>GVFS.FunctionalTests.Windows</RootNamespace>
|
||||
<AssemblyName>GVFS.FunctionalTests.Windows</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
|
||||
<IsCodedUITest>False</IsCodedUITest>
|
||||
<TestProjectType>UnitTest</TestProjectType>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<StartupObject>GVFS.FunctionalTests.Program</StartupObject>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RunPostBuildEvent>Always</RunPostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Esent.Collections, Version=1.9.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\packages\Microsoft.Database.Collections.Generic.1.9.4\lib\net40\Esent.Collections.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Esent.Interop, Version=1.9.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\packages\ManagedEsent.1.9.4\lib\net40\Esent.Interop.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Esent.Isam, Version=1.9.4.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\packages\Microsoft.Database.Isam.1.9.4\lib\net40\Esent.Isam.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Data.Sqlite, Version=2.2.4.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\Microsoft.Data.Sqlite.Core.2.2.4\lib\netstandard2.0\Microsoft.Data.Sqlite.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework, Version=3.10.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\NUnit.3.10.1\lib\net45\nunit.framework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="nunitlite, Version=3.10.1.0, Culture=neutral, PublicKeyToken=2638cd05610744eb, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\NUnitLite.3.10.1\lib\net45\nunitlite.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.batteries_green, Version=1.1.12.351, Culture=neutral, PublicKeyToken=a84b7dcfb1391f7f, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\SQLitePCLRaw.bundle_green.1.1.12\lib\net45\SQLitePCLRaw.batteries_green.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.batteries_v2, Version=1.1.12.351, Culture=neutral, PublicKeyToken=8226ea5df37bcae9, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\SQLitePCLRaw.bundle_green.1.1.12\lib\net45\SQLitePCLRaw.batteries_v2.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.core, Version=1.1.12.351, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\SQLitePCLRaw.core.1.1.12\lib\net45\SQLitePCLRaw.core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="SQLitePCLRaw.provider.e_sqlite3, Version=1.1.12.351, Culture=neutral, PublicKeyToken=9c301db686d0bd12, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\packages\SQLitePCLRaw.provider.e_sqlite3.net45.1.1.12\lib\net45\SQLitePCLRaw.provider.e_sqlite3.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.ServiceProcess" />
|
||||
</ItemGroup>
|
||||
<Choose>
|
||||
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
|
||||
</ItemGroup>
|
||||
</When>
|
||||
<Otherwise />
|
||||
</Choose>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Windows\Tests\WindowsDiskLayoutUpgradeTests.cs" />
|
||||
<Compile Include="Windows\Tests\JunctionAndSubstTests.cs" />
|
||||
<Compile Include="Windows\Tests\WindowsTombstoneTests.cs" />
|
||||
<Compile Include="Windows\Tests\WindowsUpdatePlaceholderTests.cs" />
|
||||
<Compile Include="Windows\Tests\WindowsFileSystemTests.cs" />
|
||||
<Compile Include="Windows\Tests\ServiceTests.cs" />
|
||||
<Compile Include="Windows\Tests\SharedCacheUpgradeTests.cs" />
|
||||
<Compile Include="Windows\Tools\ESENTDatabase.cs" />
|
||||
<Compile Include="Windows\Tools\RegistryHelper.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
<None Include="Windows\TestData\BackgroundGitUpdates\epc.chk">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Windows\TestData\BackgroundGitUpdates\epc.log">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Windows\TestData\BackgroundGitUpdates\epcres00001.jrs">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Windows\TestData\BackgroundGitUpdates\epcres00002.jrs">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Windows\TestData\BackgroundGitUpdates\epctmp.log">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Windows\TestData\BackgroundGitUpdates\PersistentDictionary.edb">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Windows\TestData\BackgroundGitUpdates\PersistentDictionary.jfm">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\GVFS.FunctionalTests\**\*.cs">
|
||||
<Link>NetCore\%(RecursiveDir)\%(Filename)%(Extension)</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GVFS.Tests\GVFS.Tests.csproj">
|
||||
<Project>{72701BC3-5DA9-4C7A-BF10-9E98C9FC8EAC}</Project>
|
||||
<Name>GVFS.Tests</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Analyzer Include="..\..\..\packages\StyleCop.Analyzers.1.0.2\analyzers\dotnet\cs\StyleCop.Analyzers.CodeFixes.dll" />
|
||||
<Analyzer Include="..\..\..\packages\StyleCop.Analyzers.1.0.2\analyzers\dotnet\cs\StyleCop.Analyzers.dll" />
|
||||
</ItemGroup>
|
||||
<Choose>
|
||||
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.VisualStudio.QualityTools.CodedUITestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Extension, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.TestTools.UITesting, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</When>
|
||||
</Choose>
|
||||
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\packages\NUnit.3.10.1\build\NUnit.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\NUnit.3.10.1\build\NUnit.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.12\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.12\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.12\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.12\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.12\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.12\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets'))" />
|
||||
</Target>
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>
|
||||
xcopy /Y $(BuildOutputDir)\GVFS.Windows\bin\$(Platform)\$(Configuration)\* $(TargetDir)
|
||||
xcopy /Y $(BuildOutputDir)\GVFS.Windows\bin\$(Platform)\$(Configuration)\$(Platform)\* $(TargetDir)
|
||||
xcopy /Y $(BuildOutputDir)\GVFS.Service.Windows\bin\$(Platform)\$(Configuration)\* $(TargetDir)
|
||||
xcopy /Y $(BuildOutputDir)\GVFS.Mount.Windows\bin\$(Platform)\$(Configuration)\* $(TargetDir)
|
||||
xcopy /Y $(BuildOutputDir)\GVFS.NativeTests\bin\$(Platform)\$(Configuration)\* $(TargetDir)
|
||||
xcopy /Y $(BuildOutputDir)\GVFS.Hooks.Windows\bin\$(Platform)\$(Configuration)\* $(TargetDir)
|
||||
mkdir $(TargetDir)\netcoreapp2.1
|
||||
xcopy /Y $(BuildOutputDir)\FastFetch\bin\$(Platform)\$(Configuration)\netcoreapp2.1\* $(TargetDir)\netcoreapp2.1
|
||||
xcopy /Y $(BuildOutputDir)\GVFS.FunctionalTests.LockHolder\bin\$(Platform)\$(Configuration)\netcoreapp2.1\* $(TargetDir)\netcoreapp2.1
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<PreBuildEvent>
|
||||
</PreBuildEvent>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.12\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets" Condition="Exists('..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.linux.1.1.12\build\net35\SQLitePCLRaw.lib.e_sqlite3.linux.targets')" />
|
||||
<Import Project="..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.12\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets" Condition="Exists('..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.osx.1.1.12\build\net35\SQLitePCLRaw.lib.e_sqlite3.osx.targets')" />
|
||||
<Import Project="..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.12\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets" Condition="Exists('..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.v110_xp.1.1.12\build\net35\SQLitePCLRaw.lib.e_sqlite3.v110_xp.targets')" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -1,24 +0,0 @@
|
|||
using NUnit.Framework;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("GVFS.FunctionalTests")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("GVFS.FunctionalTests")]
|
||||
[assembly: AssemblyCopyright("Copyright © Microsoft 2015")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("0f0a008e-ab12-40ec-a671-37a541b08c7f")]
|
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
|
@ -1,238 +0,0 @@
|
|||
using GVFS.FunctionalTests.FileSystemRunners;
|
||||
using GVFS.FunctionalTests.Should;
|
||||
using GVFS.FunctionalTests.Tests.EnlistmentPerFixture;
|
||||
using GVFS.FunctionalTests.Tools;
|
||||
using GVFS.Tests.Should;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace GVFS.FunctionalTests.Windows.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Category(Categories.ExtraCoverage)]
|
||||
[Category(Categories.WindowsOnly)]
|
||||
public class JunctionAndSubstTests : TestsWithEnlistmentPerFixture
|
||||
{
|
||||
private const string SubstDrive = "Q:";
|
||||
private const string SubstDrivePath = @"Q:\";
|
||||
private const string JunctionAndSubstTestsName = nameof(JunctionAndSubstTests);
|
||||
private const string ExpectedStatusWaitingText = @"Waiting for 'GVFS.FunctionalTests.LockHolder'";
|
||||
|
||||
private string junctionsRoot;
|
||||
private FileSystemRunner fileSystem;
|
||||
|
||||
public JunctionAndSubstTests()
|
||||
{
|
||||
this.fileSystem = new SystemIORunner();
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void SetupJunctionRoot()
|
||||
{
|
||||
// Create junctionsRoot in Properties.Settings.Default.EnlistmentRoot (the parent folder of the GVFS enlistment root `this.Enlistment.EnlistmentRoot`)
|
||||
// junctionsRoot is created here (outside of the GVFS enlistment root) to ensure that git hooks and GVFS commands will not find a .gvfs folder
|
||||
// walking up the tree from their current (non-normalized) path.
|
||||
this.junctionsRoot = Path.Combine(Properties.Settings.Default.EnlistmentRoot, JunctionAndSubstTestsName, Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(this.junctionsRoot);
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDownJunctionRoot()
|
||||
{
|
||||
DirectoryInfo junctionsRootInfo = new DirectoryInfo(this.junctionsRoot);
|
||||
if (junctionsRootInfo.Exists)
|
||||
{
|
||||
foreach (DirectoryInfo junction in junctionsRootInfo.GetDirectories())
|
||||
{
|
||||
junction.Delete();
|
||||
}
|
||||
|
||||
junctionsRootInfo.Delete();
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void GVFSStatusWorksFromSubstDrive()
|
||||
{
|
||||
this.CreateSubstDrive(this.Enlistment.EnlistmentRoot);
|
||||
this.RepoStatusShouldBeMounted(workingDirectory: SubstDrivePath);
|
||||
|
||||
this.CreateSubstDrive(this.Enlistment.RepoRoot);
|
||||
this.RepoStatusShouldBeMounted(workingDirectory: SubstDrivePath);
|
||||
|
||||
string subFolderPath = this.Enlistment.GetVirtualPathTo("GVFS");
|
||||
this.CreateSubstDrive(subFolderPath);
|
||||
this.RepoStatusShouldBeMounted(workingDirectory: SubstDrivePath);
|
||||
|
||||
this.RepoStatusShouldBeMounted(workingDirectory: string.Empty, enlistmentPath: SubstDrive);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void GVFSStatusWorksFromJunction()
|
||||
{
|
||||
string enlistmentRootjunctionLink = Path.Combine(this.junctionsRoot, $"{nameof(this.GVFSStatusWorksFromJunction)}_ToEnlistmentRoot");
|
||||
this.CreateJunction(enlistmentRootjunctionLink, this.Enlistment.EnlistmentRoot);
|
||||
this.RepoStatusShouldBeMounted(workingDirectory: enlistmentRootjunctionLink);
|
||||
|
||||
string junctionLink = Path.Combine(this.junctionsRoot, $"{nameof(this.GVFSStatusWorksFromJunction)}_ToRepoRoot");
|
||||
this.CreateJunction(junctionLink, this.Enlistment.RepoRoot);
|
||||
this.RepoStatusShouldBeMounted(workingDirectory: junctionLink);
|
||||
|
||||
junctionLink = Path.Combine(this.junctionsRoot, $"{nameof(this.GVFSStatusWorksFromJunction)}_ToSubFolder");
|
||||
string subFolderPath = this.Enlistment.GetVirtualPathTo("GVFS");
|
||||
this.CreateJunction(junctionLink, subFolderPath);
|
||||
this.RepoStatusShouldBeMounted(workingDirectory: junctionLink);
|
||||
|
||||
this.RepoStatusShouldBeMounted(workingDirectory: string.Empty, enlistmentPath: enlistmentRootjunctionLink);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void GVFSMountWorksFromSubstDrive()
|
||||
{
|
||||
this.CreateSubstDrive(this.Enlistment.EnlistmentRoot);
|
||||
this.Enlistment.UnmountGVFS();
|
||||
this.MountGVFS(workingDirectory: SubstDrivePath);
|
||||
|
||||
this.CreateSubstDrive(this.Enlistment.RepoRoot);
|
||||
this.Enlistment.UnmountGVFS();
|
||||
this.MountGVFS(workingDirectory: SubstDrivePath);
|
||||
|
||||
string subFolderPath = this.Enlistment.GetVirtualPathTo("GVFS");
|
||||
subFolderPath.ShouldBeADirectory(this.fileSystem);
|
||||
this.CreateSubstDrive(subFolderPath);
|
||||
this.Enlistment.UnmountGVFS();
|
||||
this.MountGVFS(workingDirectory: SubstDrivePath);
|
||||
|
||||
this.Enlistment.UnmountGVFS();
|
||||
this.MountGVFS(workingDirectory: null, enlistmentPath: SubstDrive);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void GVFSMountWorksFromJunction()
|
||||
{
|
||||
string enlistmentRootjunctionLink = Path.Combine(this.junctionsRoot, $"{nameof(this.GVFSMountWorksFromJunction)}_ToEnlistmentRoot");
|
||||
this.CreateJunction(enlistmentRootjunctionLink, this.Enlistment.EnlistmentRoot);
|
||||
this.Enlistment.UnmountGVFS();
|
||||
this.MountGVFS(workingDirectory: enlistmentRootjunctionLink);
|
||||
|
||||
string junctionLink = Path.Combine(this.junctionsRoot, $"{nameof(this.GVFSMountWorksFromJunction)}_ToRepoRoot");
|
||||
this.CreateJunction(junctionLink, this.Enlistment.RepoRoot);
|
||||
this.Enlistment.UnmountGVFS();
|
||||
this.MountGVFS(workingDirectory: junctionLink);
|
||||
|
||||
string subFolderPath = this.Enlistment.GetVirtualPathTo("GVFS");
|
||||
subFolderPath.ShouldBeADirectory(this.fileSystem);
|
||||
junctionLink = Path.Combine(this.junctionsRoot, $"{nameof(this.GVFSMountWorksFromJunction)}_ToSubFolder");
|
||||
this.CreateJunction(junctionLink, subFolderPath);
|
||||
this.Enlistment.UnmountGVFS();
|
||||
this.MountGVFS(workingDirectory: junctionLink);
|
||||
|
||||
this.Enlistment.UnmountGVFS();
|
||||
this.MountGVFS(workingDirectory: null, enlistmentPath: enlistmentRootjunctionLink);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void GitCommandInSubstToSubfolderWaitsWhileAnotherIsRunning()
|
||||
{
|
||||
this.CreateSubstDrive(this.Enlistment.EnlistmentRoot);
|
||||
this.GitCommandWaitsForLock(Path.Combine(SubstDrivePath, "src"));
|
||||
|
||||
this.CreateSubstDrive(this.Enlistment.RepoRoot);
|
||||
this.GitCommandWaitsForLock(SubstDrivePath);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void GitCommandInJunctionToSubfolderWaitsWhileAnotherIsRunning()
|
||||
{
|
||||
string junctionLink = Path.Combine(this.junctionsRoot, $"{nameof(this.GitCommandInJunctionToSubfolderWaitsWhileAnotherIsRunning)}_ToEnlistmentRoot");
|
||||
this.CreateJunction(junctionLink, this.Enlistment.EnlistmentRoot);
|
||||
this.GitCommandWaitsForLock(Path.Combine(junctionLink, "src"));
|
||||
|
||||
junctionLink = Path.Combine(this.junctionsRoot, $"{nameof(this.GitCommandInJunctionToSubfolderWaitsWhileAnotherIsRunning)}_ToRepoRoot");
|
||||
this.CreateJunction(junctionLink, this.Enlistment.RepoRoot);
|
||||
this.GitCommandWaitsForLock(junctionLink);
|
||||
}
|
||||
|
||||
private void GitCommandWaitsForLock(string gitWorkingDirectory)
|
||||
{
|
||||
ManualResetEventSlim resetEvent = GitHelpers.AcquireGVFSLock(this.Enlistment, out _, resetTimeout: 3000);
|
||||
ProcessResult statusWait = GitHelpers.InvokeGitAgainstGVFSRepo(gitWorkingDirectory, "status", removeWaitingMessages: false);
|
||||
statusWait.Errors.ShouldContain(ExpectedStatusWaitingText);
|
||||
resetEvent.Set();
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
}
|
||||
|
||||
private void CreateSubstDrive(string path)
|
||||
{
|
||||
this.RemoveSubstDrive();
|
||||
this.fileSystem.DirectoryExists(path).ShouldBeTrue($"{path} needs to exist to be able to map it to a drive letter");
|
||||
string substResult = this.RunSubst($"{SubstDrive} {path}");
|
||||
this.fileSystem.DirectoryExists(SubstDrivePath).ShouldBeTrue($"{SubstDrivePath} should exist after creating mapping with subst. subst result: {substResult}");
|
||||
}
|
||||
|
||||
private void RemoveSubstDrive()
|
||||
{
|
||||
string substResult = this.RunSubst($"{SubstDrive} /D");
|
||||
this.fileSystem.DirectoryExists(SubstDrivePath).ShouldBeFalse($"{SubstDrivePath} should not exist after being removed with subst /D. subst result: {substResult}");
|
||||
}
|
||||
|
||||
private string RunSubst(string substArguments)
|
||||
{
|
||||
string cmdArguments = $"/C subst {substArguments}";
|
||||
ProcessResult result = ProcessHelper.Run("CMD.exe", cmdArguments);
|
||||
return !string.IsNullOrEmpty(result.Output) ? result.Output : result.Errors;
|
||||
}
|
||||
|
||||
private void CreateJunction(string junctionLink, string junctionTarget)
|
||||
{
|
||||
junctionLink.ShouldNotExistOnDisk(this.fileSystem);
|
||||
junctionTarget.ShouldBeADirectory(this.fileSystem);
|
||||
ProcessHelper.Run("CMD.exe", "/C mklink /J " + junctionLink + " " + junctionTarget);
|
||||
junctionLink.ShouldBeADirectory(this.fileSystem);
|
||||
}
|
||||
|
||||
private void RepoStatusShouldBeMounted(string workingDirectory, string enlistmentPath = null)
|
||||
{
|
||||
ProcessStartInfo startInfo = new ProcessStartInfo(GVFSTestConfig.PathToGVFS);
|
||||
startInfo.Arguments = "status" + (enlistmentPath != null ? $" {enlistmentPath}" : string.Empty);
|
||||
startInfo.UseShellExecute = false;
|
||||
startInfo.RedirectStandardOutput = true;
|
||||
startInfo.RedirectStandardError = true;
|
||||
startInfo.CreateNoWindow = true;
|
||||
startInfo.WorkingDirectory = workingDirectory;
|
||||
|
||||
ProcessResult result = ProcessHelper.Run(startInfo);
|
||||
result.ExitCode.ShouldEqual(0, result.Errors);
|
||||
result.Output.ShouldContain("Mount status: Ready");
|
||||
}
|
||||
|
||||
private void MountGVFS(string workingDirectory, string enlistmentPath = null)
|
||||
{
|
||||
string mountCommand;
|
||||
if (enlistmentPath != null)
|
||||
{
|
||||
mountCommand = $"mount \"{enlistmentPath}\" {TestConstants.InternalUseOnlyFlag} {GVFSHelpers.GetInternalParameter()}";
|
||||
}
|
||||
else
|
||||
{
|
||||
mountCommand = $"mount {TestConstants.InternalUseOnlyFlag} {GVFSHelpers.GetInternalParameter()}";
|
||||
}
|
||||
|
||||
ProcessStartInfo startInfo = new ProcessStartInfo(GVFSTestConfig.PathToGVFS);
|
||||
startInfo.Arguments = mountCommand;
|
||||
startInfo.UseShellExecute = false;
|
||||
startInfo.RedirectStandardOutput = true;
|
||||
startInfo.RedirectStandardError = true;
|
||||
startInfo.CreateNoWindow = true;
|
||||
startInfo.WorkingDirectory = workingDirectory;
|
||||
|
||||
ProcessResult result = ProcessHelper.Run(startInfo);
|
||||
result.ExitCode.ShouldEqual(0, result.Errors);
|
||||
|
||||
this.RepoStatusShouldBeMounted(workingDirectory, enlistmentPath);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
using GVFS.FunctionalTests.FileSystemRunners;
|
||||
using GVFS.FunctionalTests.Tests.EnlistmentPerFixture;
|
||||
using GVFS.FunctionalTests.Tools;
|
||||
using GVFS.FunctionalTests.Windows.Tools;
|
||||
using GVFS.Tests.Should;
|
||||
using Microsoft.Win32;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.ServiceProcess;
|
||||
|
||||
namespace GVFS.FunctionalTests.Windows.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[NonParallelizable]
|
||||
[Category(Categories.ExtraCoverage)]
|
||||
[Category(Categories.WindowsOnly)]
|
||||
public class ServiceTests : TestsWithEnlistmentPerFixture
|
||||
{
|
||||
private const string NativeLibPath = @"C:\Program Files\GVFS\ProjectedFSLib.dll";
|
||||
private const string PrjFltAutoLoggerKey = "SYSTEM\\CurrentControlSet\\Control\\WMI\\Autologger\\Microsoft-Windows-ProjFS-Filter-Log";
|
||||
private const string PrjFltAutoLoggerStartValue = "Start";
|
||||
|
||||
private FileSystemRunner fileSystem;
|
||||
|
||||
public ServiceTests()
|
||||
{
|
||||
this.fileSystem = new SystemIORunner();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void MountAsksServiceToEnsurePrjFltServiceIsHealthy()
|
||||
{
|
||||
if (!GVFSTestConfig.TestGVFSOnPath)
|
||||
{
|
||||
Assert.Ignore("Skipping test, test only enabled when --test-gvfs-on-path is set");
|
||||
}
|
||||
|
||||
this.Enlistment.UnmountGVFS();
|
||||
StopPrjFlt();
|
||||
|
||||
// Disable the ProjFS autologger
|
||||
RegistryHelper.GetValueFromRegistry(RegistryHive.LocalMachine, PrjFltAutoLoggerKey, PrjFltAutoLoggerStartValue).ShouldNotBeNull();
|
||||
RegistryHelper.TrySetDWordInRegistry(RegistryHive.LocalMachine, PrjFltAutoLoggerKey, PrjFltAutoLoggerStartValue, 0).ShouldBeTrue();
|
||||
|
||||
this.Enlistment.MountGVFS();
|
||||
IsPrjFltRunning().ShouldBeTrue();
|
||||
|
||||
// The service should have re-enabled the autologger
|
||||
Convert.ToInt32(RegistryHelper.GetValueFromRegistry(RegistryHive.LocalMachine, PrjFltAutoLoggerKey, PrjFltAutoLoggerStartValue)).ShouldEqual(1);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void ServiceStartsPrjFltService()
|
||||
{
|
||||
if (!GVFSTestConfig.TestGVFSOnPath)
|
||||
{
|
||||
Assert.Ignore("Skipping test, test only enabled when --test-gvfs-on-path is set");
|
||||
}
|
||||
|
||||
this.Enlistment.UnmountGVFS();
|
||||
StopPrjFlt();
|
||||
GVFSServiceProcess.StopService();
|
||||
GVFSServiceProcess.StartService();
|
||||
|
||||
ServiceController controller = new ServiceController("prjflt");
|
||||
controller.WaitForStatus(ServiceControllerStatus.Running, TimeSpan.FromSeconds(10));
|
||||
controller.Status.ShouldEqual(ServiceControllerStatus.Running);
|
||||
|
||||
this.Enlistment.MountGVFS();
|
||||
}
|
||||
|
||||
private static bool IsPrjFltRunning()
|
||||
{
|
||||
ServiceController controller = new ServiceController("prjflt");
|
||||
return controller.Status.Equals(ServiceControllerStatus.Running);
|
||||
}
|
||||
|
||||
private static void StopPrjFlt()
|
||||
{
|
||||
IsPrjFltRunning().ShouldBeTrue();
|
||||
|
||||
ServiceController controller = new ServiceController("prjflt");
|
||||
controller.Stop();
|
||||
controller.WaitForStatus(ServiceControllerStatus.Stopped);
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
||||
private static extern bool GetVersionEx([In, Out] ref OSVersionInfo versionInfo);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
private struct OSVersionInfo
|
||||
{
|
||||
public uint OSVersionInfoSize;
|
||||
public uint MajorVersion;
|
||||
public uint MinorVersion;
|
||||
public uint BuildNumber;
|
||||
public uint PlatformId;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
|
||||
public string CSDVersion;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,151 +0,0 @@
|
|||
using GVFS.FunctionalTests.FileSystemRunners;
|
||||
using GVFS.FunctionalTests.Should;
|
||||
using GVFS.FunctionalTests.Tests.MultiEnlistmentTests;
|
||||
using GVFS.FunctionalTests.Tools;
|
||||
using GVFS.FunctionalTests.Windows.Tests;
|
||||
using GVFS.FunctionalTests.Windows.Tools;
|
||||
using GVFS.Tests.Should;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace GVFS.FunctionalTests.Windows.Windows.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Category(Categories.ExtraCoverage)]
|
||||
[Category(Categories.WindowsOnly)]
|
||||
public class SharedCacheUpgradeTests : TestsWithMultiEnlistment
|
||||
{
|
||||
private string localCachePath;
|
||||
private string localCacheParentPath;
|
||||
|
||||
private FileSystemRunner fileSystem;
|
||||
|
||||
public SharedCacheUpgradeTests()
|
||||
{
|
||||
this.fileSystem = new SystemIORunner();
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void SetCacheLocation()
|
||||
{
|
||||
this.localCacheParentPath = Path.Combine(Properties.Settings.Default.EnlistmentRoot, "..", Guid.NewGuid().ToString("N"));
|
||||
this.localCachePath = Path.Combine(this.localCacheParentPath, ".customGVFSCache");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void MountUpgradesLocalSizesToSharedCache()
|
||||
{
|
||||
GVFSFunctionalTestEnlistment enlistment = this.CloneAndMountEnlistment();
|
||||
enlistment.UnmountGVFS();
|
||||
|
||||
string localCacheRoot = GVFSHelpers.GetPersistedLocalCacheRoot(enlistment.DotGVFSRoot);
|
||||
string gitObjectsRoot = GVFSHelpers.GetPersistedGitObjectsRoot(enlistment.DotGVFSRoot);
|
||||
|
||||
// Delete the existing repo metadata
|
||||
string versionJsonPath = Path.Combine(enlistment.DotGVFSRoot, GVFSHelpers.RepoMetadataName);
|
||||
versionJsonPath.ShouldBeAFile(this.fileSystem);
|
||||
this.fileSystem.DeleteFile(versionJsonPath);
|
||||
|
||||
// Since there isn't a sparse-checkout file that is used anymore one needs to be added
|
||||
// in order to test the old upgrades that might have needed it
|
||||
string sparseCheckoutPath = Path.Combine(enlistment.RepoRoot, TestConstants.DotGit.Info.SparseCheckoutPath);
|
||||
this.fileSystem.WriteAllText(sparseCheckoutPath, "/.gitattributes\r\n");
|
||||
|
||||
// "13.0" was the last version before blob sizes were moved out of Esent
|
||||
string metadataPath = Path.Combine(enlistment.DotGVFSRoot, GVFSHelpers.RepoMetadataName);
|
||||
this.fileSystem.CreateEmptyFile(metadataPath);
|
||||
GVFSHelpers.SaveDiskLayoutVersion(enlistment.DotGVFSRoot, "13", "0");
|
||||
GVFSHelpers.SaveLocalCacheRoot(enlistment.DotGVFSRoot, localCacheRoot);
|
||||
GVFSHelpers.SaveGitObjectsRoot(enlistment.DotGVFSRoot, gitObjectsRoot);
|
||||
|
||||
// Create a legacy PersistedDictionary sizes database
|
||||
List<KeyValuePair<string, long>> entries = new List<KeyValuePair<string, long>>()
|
||||
{
|
||||
new KeyValuePair<string, long>(new string('0', 40), 1),
|
||||
new KeyValuePair<string, long>(new string('1', 40), 2),
|
||||
new KeyValuePair<string, long>(new string('2', 40), 4),
|
||||
new KeyValuePair<string, long>(new string('3', 40), 8),
|
||||
};
|
||||
|
||||
ESENTDatabase.CreateEsentBlobSizesDatabase(enlistment.DotGVFSRoot, entries);
|
||||
|
||||
enlistment.MountGVFS();
|
||||
|
||||
string majorVersion;
|
||||
string minorVersion;
|
||||
GVFSHelpers.GetPersistedDiskLayoutVersion(enlistment.DotGVFSRoot, out majorVersion, out minorVersion);
|
||||
|
||||
majorVersion
|
||||
.ShouldBeAnInt("Disk layout version should always be an int")
|
||||
.ShouldEqual(WindowsDiskLayoutUpgradeTests.CurrentDiskLayoutMajorVersion, "Disk layout version should be upgraded to the latest");
|
||||
|
||||
minorVersion
|
||||
.ShouldBeAnInt("Disk layout version should always be an int")
|
||||
.ShouldEqual(WindowsDiskLayoutUpgradeTests.CurrentDiskLayoutMinorVersion, "Disk layout version should be upgraded to the latest");
|
||||
|
||||
string newBlobSizesRoot = Path.Combine(Path.GetDirectoryName(gitObjectsRoot), WindowsDiskLayoutUpgradeTests.BlobSizesCacheName);
|
||||
GVFSHelpers.GetPersistedBlobSizesRoot(enlistment.DotGVFSRoot)
|
||||
.ShouldEqual(newBlobSizesRoot);
|
||||
|
||||
string blobSizesDbPath = Path.Combine(newBlobSizesRoot, WindowsDiskLayoutUpgradeTests.BlobSizesDBFileName);
|
||||
newBlobSizesRoot.ShouldBeADirectory(this.fileSystem);
|
||||
blobSizesDbPath.ShouldBeAFile(this.fileSystem);
|
||||
|
||||
foreach (KeyValuePair<string, long> entry in entries)
|
||||
{
|
||||
GVFSHelpers.SQLiteBlobSizesDatabaseHasEntry(blobSizesDbPath, entry.Key, entry.Value);
|
||||
}
|
||||
|
||||
// Upgrade a second repo, and make sure all sizes from both upgrades are in the shared database
|
||||
|
||||
GVFSFunctionalTestEnlistment enlistment2 = this.CloneAndMountEnlistment();
|
||||
enlistment2.UnmountGVFS();
|
||||
|
||||
// Delete the existing repo metadata
|
||||
versionJsonPath = Path.Combine(enlistment2.DotGVFSRoot, GVFSHelpers.RepoMetadataName);
|
||||
versionJsonPath.ShouldBeAFile(this.fileSystem);
|
||||
this.fileSystem.DeleteFile(versionJsonPath);
|
||||
|
||||
// Since there isn't a sparse-checkout file that is used anymore one needs to be added
|
||||
// in order to test the old upgrades that might have needed it
|
||||
string sparseCheckoutPath2 = Path.Combine(enlistment2.RepoRoot, TestConstants.DotGit.Info.SparseCheckoutPath);
|
||||
this.fileSystem.WriteAllText(sparseCheckoutPath2, "/.gitattributes\r\n");
|
||||
|
||||
// "13.0" was the last version before blob sizes were moved out of Esent
|
||||
metadataPath = Path.Combine(enlistment2.DotGVFSRoot, GVFSHelpers.RepoMetadataName);
|
||||
this.fileSystem.CreateEmptyFile(metadataPath);
|
||||
GVFSHelpers.SaveDiskLayoutVersion(enlistment2.DotGVFSRoot, "13", "0");
|
||||
GVFSHelpers.SaveLocalCacheRoot(enlistment2.DotGVFSRoot, localCacheRoot);
|
||||
GVFSHelpers.SaveGitObjectsRoot(enlistment2.DotGVFSRoot, gitObjectsRoot);
|
||||
|
||||
// Create a legacy PersistedDictionary sizes database
|
||||
List<KeyValuePair<string, long>> additionalEntries = new List<KeyValuePair<string, long>>()
|
||||
{
|
||||
new KeyValuePair<string, long>(new string('4', 40), 16),
|
||||
new KeyValuePair<string, long>(new string('5', 40), 32),
|
||||
new KeyValuePair<string, long>(new string('6', 40), 64),
|
||||
};
|
||||
|
||||
ESENTDatabase.CreateEsentBlobSizesDatabase(enlistment2.DotGVFSRoot, additionalEntries);
|
||||
|
||||
enlistment2.MountGVFS();
|
||||
|
||||
foreach (KeyValuePair<string, long> entry in entries)
|
||||
{
|
||||
GVFSHelpers.SQLiteBlobSizesDatabaseHasEntry(blobSizesDbPath, entry.Key, entry.Value);
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string, long> entry in additionalEntries)
|
||||
{
|
||||
GVFSHelpers.SQLiteBlobSizesDatabaseHasEntry(blobSizesDbPath, entry.Key, entry.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private GVFSFunctionalTestEnlistment CloneAndMountEnlistment(string branch = null)
|
||||
{
|
||||
return this.CreateNewEnlistment(this.localCachePath, branch);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,413 +0,0 @@
|
|||
using GVFS.FunctionalTests.Should;
|
||||
using GVFS.FunctionalTests.Tests.EnlistmentPerTestCase;
|
||||
using GVFS.FunctionalTests.Tools;
|
||||
using GVFS.FunctionalTests.Windows.Tools;
|
||||
using GVFS.Tests.Should;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace GVFS.FunctionalTests.Windows.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Category(Categories.ExtraCoverage)]
|
||||
[Category(Categories.WindowsOnly)]
|
||||
public class WindowsDiskLayoutUpgradeTests : DiskLayoutUpgradeTests
|
||||
{
|
||||
public const int CurrentDiskLayoutMajorVersion = 19;
|
||||
public const int CurrentDiskLayoutMinorVersion = 0;
|
||||
|
||||
public const string BlobSizesCacheName = "blobSizes";
|
||||
public const string BlobSizesDBFileName = "BlobSizes.sql";
|
||||
|
||||
private const string DatabasesFolderName = "databases";
|
||||
|
||||
public override int GetCurrentDiskLayoutMajorVersion() => CurrentDiskLayoutMajorVersion;
|
||||
public override int GetCurrentDiskLayoutMinorVersion() => CurrentDiskLayoutMinorVersion;
|
||||
|
||||
[SetUp]
|
||||
public override void CreateEnlistment()
|
||||
{
|
||||
base.CreateEnlistment();
|
||||
|
||||
// Since there isn't a sparse-checkout file that is used anymore one needs to be added
|
||||
// in order to test the old upgrades that might have needed it
|
||||
string sparseCheckoutPath = Path.Combine(this.Enlistment.RepoRoot, TestConstants.DotGit.Info.SparseCheckoutPath);
|
||||
this.fileSystem.WriteAllText(sparseCheckoutPath, "/.gitattributes\r\n");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void MountUpgradesFromVersion7()
|
||||
{
|
||||
// Seven to eight is a just a version change (non-breaking), but preserves ESENT RepoMetadata
|
||||
this.RunEsentRepoMetadataUpgradeTest("7");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void MountUpgradesFromEsentToJsonRepoMetadata()
|
||||
{
|
||||
// Eight is the last version with ESENT RepoMetadata DB
|
||||
this.RunEsentRepoMetadataUpgradeTest("8");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void MountUpgradesFromEsentDatabasesToFlatDatabases()
|
||||
{
|
||||
this.Enlistment.UnmountGVFS();
|
||||
|
||||
// Delete the existing background ops data
|
||||
string flatBackgroundPath = Path.Combine(this.Enlistment.DotGVFSRoot, GVFSHelpers.BackgroundOpsFile);
|
||||
flatBackgroundPath.ShouldBeAFile(this.fileSystem);
|
||||
this.fileSystem.DeleteFile(flatBackgroundPath);
|
||||
|
||||
// Delete the existing placeholder data
|
||||
string placeholdersPath = Path.Combine(this.Enlistment.DotGVFSRoot, TestConstants.Databases.VFSForGit);
|
||||
placeholdersPath.ShouldBeAFile(this.fileSystem);
|
||||
this.fileSystem.DeleteFile(placeholdersPath);
|
||||
|
||||
ESENTDatabase.CreateEsentBackgroundOpsDatabase(this.Enlistment.DotGVFSRoot);
|
||||
ESENTDatabase.CreateEsentPlaceholderDatabase(this.Enlistment.DotGVFSRoot);
|
||||
|
||||
// Nine is the last version with ESENT BackgroundOps and Placeholders DBs
|
||||
GVFSHelpers.SaveDiskLayoutVersion(this.Enlistment.DotGVFSRoot, "9", "0");
|
||||
this.Enlistment.MountGVFS();
|
||||
|
||||
this.ValidatePersistedVersionMatchesCurrentVersion();
|
||||
|
||||
flatBackgroundPath.ShouldBeAFile(this.fileSystem);
|
||||
placeholdersPath.ShouldBeAFile(this.fileSystem);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void MountUpgradesFromPriorToPlaceholderCreationsBlockedForGit()
|
||||
{
|
||||
this.Enlistment.UnmountGVFS();
|
||||
|
||||
GVFSHelpers.SaveDiskLayoutVersion(this.Enlistment.DotGVFSRoot, "10", "0");
|
||||
|
||||
this.Enlistment.MountGVFS();
|
||||
|
||||
this.ValidatePersistedVersionMatchesCurrentVersion();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void MountFailsToUpgradeFromEsentVersion6ToJsonRepoMetadata()
|
||||
{
|
||||
this.Enlistment.UnmountGVFS();
|
||||
|
||||
// Delete the existing repo metadata
|
||||
string versionJsonPath = Path.Combine(this.Enlistment.DotGVFSRoot, GVFSHelpers.RepoMetadataName);
|
||||
versionJsonPath.ShouldBeAFile(this.fileSystem);
|
||||
this.fileSystem.DeleteFile(versionJsonPath);
|
||||
|
||||
ESENTDatabase.SaveDiskLayoutVersionAsEsentDatabase(this.Enlistment.DotGVFSRoot, "6");
|
||||
string esentDatabasePath = Path.Combine(this.Enlistment.DotGVFSRoot, ESENTDatabase.EsentRepoMetadataFolder);
|
||||
esentDatabasePath.ShouldBeADirectory(this.fileSystem);
|
||||
|
||||
this.Enlistment.TryMountGVFS().ShouldEqual(false, "Should not be able to upgrade from version 6");
|
||||
|
||||
esentDatabasePath.ShouldBeADirectory(this.fileSystem);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void MountSetsGitObjectsRootToLegacyDotGVFSCache()
|
||||
{
|
||||
this.Enlistment.UnmountGVFS();
|
||||
|
||||
// Delete the existing repo metadata
|
||||
string versionJsonPath = Path.Combine(this.Enlistment.DotGVFSRoot, GVFSHelpers.RepoMetadataName);
|
||||
versionJsonPath.ShouldBeAFile(this.fileSystem);
|
||||
this.fileSystem.DeleteFile(versionJsonPath);
|
||||
|
||||
// "11" was the last version before the introduction of a volume wide GVFS cache
|
||||
string metadataPath = Path.Combine(this.Enlistment.DotGVFSRoot, GVFSHelpers.RepoMetadataName);
|
||||
this.fileSystem.CreateEmptyFile(metadataPath);
|
||||
GVFSHelpers.SaveDiskLayoutVersion(this.Enlistment.DotGVFSRoot, "11", "0");
|
||||
|
||||
// Create the legacy cache location: <root>\.gvfs\gitObjectCache
|
||||
string legacyGitObjectsCachePath = Path.Combine(this.Enlistment.DotGVFSRoot, "gitObjectCache");
|
||||
this.fileSystem.CreateDirectory(legacyGitObjectsCachePath);
|
||||
|
||||
this.Enlistment.MountGVFS();
|
||||
|
||||
this.ValidatePersistedVersionMatchesCurrentVersion();
|
||||
|
||||
GVFSHelpers.GetPersistedLocalCacheRoot(this.Enlistment.DotGVFSRoot)
|
||||
.ShouldEqual(string.Empty, "LocalCacheRoot should be an empty string when upgrading from a version prior to 12");
|
||||
|
||||
GVFSHelpers.GetPersistedGitObjectsRoot(this.Enlistment.DotGVFSRoot)
|
||||
.ShouldEqual(legacyGitObjectsCachePath);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void MountWritesFolderPlaceholdersToPlaceholderDatabase()
|
||||
{
|
||||
this.PerformIOBeforePlaceholderDatabaseUpgradeTest();
|
||||
|
||||
this.Enlistment.UnmountGVFS();
|
||||
|
||||
this.fileSystem.DeleteFile(Path.Combine(this.Enlistment.DotGVFSRoot, TestConstants.Databases.VFSForGit));
|
||||
this.WriteOldPlaceholderListDatabase();
|
||||
|
||||
// Get the existing folder placeholder data
|
||||
string placeholderDatabasePath = Path.Combine(this.Enlistment.DotGVFSRoot, GVFSHelpers.PlaceholderListFile);
|
||||
string[] lines = this.GetPlaceholderDatabaseLinesBeforeUpgrade(placeholderDatabasePath);
|
||||
|
||||
// Placeholder database file should only have file placeholders
|
||||
this.fileSystem.WriteAllText(
|
||||
placeholderDatabasePath,
|
||||
string.Join(Environment.NewLine, lines.Where(x => !x.EndsWith(TestConstants.PartialFolderPlaceholderDatabaseValue))) + Environment.NewLine);
|
||||
|
||||
GVFSHelpers.SaveDiskLayoutVersion(this.Enlistment.DotGVFSRoot, "12", "1");
|
||||
|
||||
this.Enlistment.MountGVFS();
|
||||
this.Enlistment.UnmountGVFS();
|
||||
|
||||
// Validate the folder placeholders are in the placeholder database now
|
||||
this.GetPlaceholderDatabaseLinesAfterUpgradeFrom12_1(Path.Combine(this.Enlistment.DotGVFSRoot, TestConstants.Databases.VFSForGit));
|
||||
|
||||
this.ValidatePersistedVersionMatchesCurrentVersion();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void MountUpdatesAllZeroShaFolderPlaceholderEntriesToPartialFolderSpecialValue()
|
||||
{
|
||||
this.PerformIOBeforePlaceholderDatabaseUpgradeTest();
|
||||
|
||||
this.Enlistment.UnmountGVFS();
|
||||
this.WriteOldPlaceholderListDatabase();
|
||||
|
||||
// Get the existing folder placeholder data
|
||||
string placeholderDatabasePath = Path.Combine(this.Enlistment.DotGVFSRoot, GVFSHelpers.PlaceholderListFile);
|
||||
string[] lines = this.GetPlaceholderDatabaseLinesBeforeUpgrade(placeholderDatabasePath);
|
||||
|
||||
// Update the placeholder file so that folders have an all zero SHA
|
||||
this.fileSystem.WriteAllText(
|
||||
placeholderDatabasePath,
|
||||
string.Join(
|
||||
Environment.NewLine,
|
||||
lines.Select(x => x.Replace(TestConstants.PartialFolderPlaceholderDatabaseValue, TestConstants.AllZeroSha))) + Environment.NewLine);
|
||||
|
||||
GVFSHelpers.SaveDiskLayoutVersion(this.Enlistment.DotGVFSRoot, "16", "0");
|
||||
|
||||
this.Enlistment.MountGVFS();
|
||||
this.Enlistment.UnmountGVFS();
|
||||
|
||||
// Validate the folder placeholders in the database have PartialFolderPlaceholderDatabaseValue values
|
||||
this.GetPlaceholderDatabaseLinesAfterUpgradeFrom16(Path.Combine(this.Enlistment.DotGVFSRoot, TestConstants.Databases.VFSForGit));
|
||||
|
||||
this.ValidatePersistedVersionMatchesCurrentVersion();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void MountUpgradesPreSharedCacheLocalSizes()
|
||||
{
|
||||
this.Enlistment.UnmountGVFS();
|
||||
|
||||
// Delete the existing repo metadata
|
||||
string versionJsonPath = Path.Combine(this.Enlistment.DotGVFSRoot, GVFSHelpers.RepoMetadataName);
|
||||
versionJsonPath.ShouldBeAFile(this.fileSystem);
|
||||
this.fileSystem.DeleteFile(versionJsonPath);
|
||||
|
||||
// "11" was the last version before the introduction of a volume wide GVFS cache
|
||||
string metadataPath = Path.Combine(this.Enlistment.DotGVFSRoot, GVFSHelpers.RepoMetadataName);
|
||||
this.fileSystem.CreateEmptyFile(metadataPath);
|
||||
GVFSHelpers.SaveDiskLayoutVersion(this.Enlistment.DotGVFSRoot, "11", "0");
|
||||
|
||||
// Create the legacy cache location: <root>\.gvfs\gitObjectCache
|
||||
string legacyGitObjectsCachePath = Path.Combine(this.Enlistment.DotGVFSRoot, "gitObjectCache");
|
||||
this.fileSystem.CreateDirectory(legacyGitObjectsCachePath);
|
||||
|
||||
// Create a legacy PersistedDictionary sizes database
|
||||
List<KeyValuePair<string, long>> entries = new List<KeyValuePair<string, long>>()
|
||||
{
|
||||
new KeyValuePair<string, long>(new string('0', 40), 1),
|
||||
new KeyValuePair<string, long>(new string('1', 40), 2),
|
||||
new KeyValuePair<string, long>(new string('2', 40), 4),
|
||||
new KeyValuePair<string, long>(new string('3', 40), 8),
|
||||
};
|
||||
|
||||
ESENTDatabase.CreateEsentBlobSizesDatabase(this.Enlistment.DotGVFSRoot, entries);
|
||||
|
||||
this.Enlistment.MountGVFS();
|
||||
|
||||
this.ValidatePersistedVersionMatchesCurrentVersion();
|
||||
|
||||
GVFSHelpers.GetPersistedLocalCacheRoot(this.Enlistment.DotGVFSRoot)
|
||||
.ShouldEqual(string.Empty, "LocalCacheRoot should be an empty string when upgrading from a version prior to 12");
|
||||
|
||||
GVFSHelpers.GetPersistedGitObjectsRoot(this.Enlistment.DotGVFSRoot)
|
||||
.ShouldEqual(legacyGitObjectsCachePath);
|
||||
|
||||
string newBlobSizesRoot = Path.Combine(this.Enlistment.DotGVFSRoot, DatabasesFolderName, BlobSizesCacheName);
|
||||
GVFSHelpers.GetPersistedBlobSizesRoot(this.Enlistment.DotGVFSRoot)
|
||||
.ShouldEqual(newBlobSizesRoot);
|
||||
|
||||
string blobSizesDbPath = Path.Combine(newBlobSizesRoot, BlobSizesDBFileName);
|
||||
newBlobSizesRoot.ShouldBeADirectory(this.fileSystem);
|
||||
blobSizesDbPath.ShouldBeAFile(this.fileSystem);
|
||||
|
||||
foreach (KeyValuePair<string, long> entry in entries)
|
||||
{
|
||||
GVFSHelpers.SQLiteBlobSizesDatabaseHasEntry(blobSizesDbPath, entry.Key, entry.Value);
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void MountCreatesModifiedPathsDatabase()
|
||||
{
|
||||
this.Enlistment.UnmountGVFS();
|
||||
GVFSHelpers.SaveDiskLayoutVersion(this.Enlistment.DotGVFSRoot, "14", "0");
|
||||
|
||||
// Delete the existing modified paths database to make sure mount creates it.
|
||||
string modifiedPathsDatabasePath = Path.Combine(this.Enlistment.DotGVFSRoot, TestConstants.Databases.ModifiedPaths);
|
||||
this.fileSystem.DeleteFile(modifiedPathsDatabasePath);
|
||||
|
||||
// Overwrite the sparse-checkout with entries to test
|
||||
string sparseCheckoutPath = Path.Combine(this.Enlistment.RepoRoot, TestConstants.DotGit.Info.SparseCheckoutPath);
|
||||
string sparseCheckoutContent = @"/.gitattributes
|
||||
/developer/me/
|
||||
/developer/me/JLANGE9._prerazzle
|
||||
/developer/me/StateSwitch.Save
|
||||
/tools/x86/remote.exe
|
||||
/tools/x86/runelevated.exe
|
||||
/tools/amd64/remote.exe
|
||||
/tools/amd64/runelevated.exe
|
||||
/tools/perllib/MS/TraceLogging.dll
|
||||
/tools/managed/v2.0/midldd.CheckedInExe
|
||||
/tools/managed/v4.0/sdapi.dll
|
||||
/tools/managed/v2.0/midlpars.dll
|
||||
/tools/managed/v2.0/RPCDataSupport.dll
|
||||
";
|
||||
this.fileSystem.WriteAllText(sparseCheckoutPath, sparseCheckoutContent);
|
||||
|
||||
// Overwrite the always_exclude file with entries to test
|
||||
string alwaysExcludePath = Path.Combine(this.Enlistment.RepoRoot, TestConstants.DotGit.Info.AlwaysExcludePath);
|
||||
string alwaysExcludeContent = @"*
|
||||
!/developer
|
||||
!/developer/*
|
||||
!/developer/me
|
||||
!/developer/me/*
|
||||
!/tools
|
||||
!/tools/x86
|
||||
!/tools/x86/*
|
||||
!/tools/amd64
|
||||
!/tools/amd64/*
|
||||
!/tools/perllib/
|
||||
!/tools/perllib/MS/
|
||||
!/tools/perllib/MS/Somefile.txt
|
||||
!/tools/managed/
|
||||
!/tools/managed/v2.0/
|
||||
!/tools/managed/v2.0/MidlStaticAnalysis.dll
|
||||
!/tools/managed/v2.0/RPCDataSupport.dll
|
||||
";
|
||||
this.fileSystem.WriteAllText(alwaysExcludePath, alwaysExcludeContent);
|
||||
|
||||
this.Enlistment.MountGVFS();
|
||||
this.Enlistment.UnmountGVFS();
|
||||
|
||||
string[] expectedModifiedPaths =
|
||||
{
|
||||
"A .gitattributes",
|
||||
"A developer/me/",
|
||||
"A tools/x86/remote.exe",
|
||||
"A tools/x86/runelevated.exe",
|
||||
"A tools/amd64/remote.exe",
|
||||
"A tools/amd64/runelevated.exe",
|
||||
"A tools/perllib/MS/TraceLogging.dll",
|
||||
"A tools/managed/v2.0/midldd.CheckedInExe",
|
||||
"A tools/managed/v4.0/sdapi.dll",
|
||||
"A tools/managed/v2.0/midlpars.dll",
|
||||
"A tools/managed/v2.0/RPCDataSupport.dll",
|
||||
"A tools/managed/v2.0/MidlStaticAnalysis.dll",
|
||||
"A tools/perllib/MS/Somefile.txt",
|
||||
};
|
||||
|
||||
modifiedPathsDatabasePath.ShouldBeAFile(this.fileSystem);
|
||||
this.fileSystem.ReadAllText(modifiedPathsDatabasePath)
|
||||
.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.OrderBy(x => x)
|
||||
.ShouldMatchInOrder(expectedModifiedPaths.OrderBy(x => x));
|
||||
|
||||
this.ValidatePersistedVersionMatchesCurrentVersion();
|
||||
}
|
||||
|
||||
private void PlaceholderDatabaseShouldIncludeCommonLinesForUpgradeTestIO(string[] placeholderLines)
|
||||
{
|
||||
placeholderLines.ShouldContain(x => x.Contains("A Readme.md"));
|
||||
placeholderLines.ShouldContain(x => x.Contains("A Scripts\\RunUnitTests.bat"));
|
||||
placeholderLines.ShouldContain(x => x.Contains("A GVFS\\GVFS.Common\\Git\\GitRefs.cs"));
|
||||
placeholderLines.ShouldContain(x => x.Contains("A .gitignore"));
|
||||
placeholderLines.ShouldContain(x => x == "A Scripts\0" + TestConstants.PartialFolderPlaceholderDatabaseValue);
|
||||
placeholderLines.ShouldContain(x => x == "A GVFS\0" + TestConstants.PartialFolderPlaceholderDatabaseValue);
|
||||
placeholderLines.ShouldContain(x => x == "A GVFS\\GVFS.Common\0" + TestConstants.PartialFolderPlaceholderDatabaseValue);
|
||||
placeholderLines.ShouldContain(x => x == "A GVFS\\GVFS.Common\\Git\0" + TestConstants.PartialFolderPlaceholderDatabaseValue);
|
||||
placeholderLines.ShouldContain(x => x == "A GVFS\\GVFS.Tests\0" + TestConstants.PartialFolderPlaceholderDatabaseValue);
|
||||
}
|
||||
|
||||
private string[] GetPlaceholderDatabaseLinesBeforeUpgrade(string placeholderDatabasePath)
|
||||
{
|
||||
placeholderDatabasePath.ShouldBeAFile(this.fileSystem);
|
||||
string[] lines = this.fileSystem.ReadAllText(placeholderDatabasePath).Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
lines.Length.ShouldEqual(12);
|
||||
this.PlaceholderDatabaseShouldIncludeCommonLinesForUpgradeTestIO(lines);
|
||||
lines.ShouldContain(x => x.Contains("A GVFS\\GVFS.Tests\\Properties\\AssemblyInfo.cs"));
|
||||
lines.ShouldContain(x => x == "D GVFS\\GVFS.Tests\\Properties\\AssemblyInfo.cs");
|
||||
lines.ShouldContain(x => x == "A GVFS\\GVFS.Tests\\Properties\0" + TestConstants.PartialFolderPlaceholderDatabaseValue);
|
||||
return lines;
|
||||
}
|
||||
|
||||
private string[] GetPlaceholderDatabaseLinesAfterUpgradeFrom12_1(string placeholderDatabasePath)
|
||||
{
|
||||
placeholderDatabasePath.ShouldBeAFile(this.fileSystem);
|
||||
string[] lines = GVFSHelpers.GetAllSQLitePlaceholdersAsString(placeholderDatabasePath).Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
lines.Length.ShouldEqual(9);
|
||||
this.PlaceholderDatabaseShouldIncludeCommonLines(lines);
|
||||
return lines;
|
||||
}
|
||||
|
||||
private string[] GetPlaceholderDatabaseLinesAfterUpgradeFrom16(string placeholderDatabasePath)
|
||||
{
|
||||
placeholderDatabasePath.ShouldBeAFile(this.fileSystem);
|
||||
string[] lines = GVFSHelpers.GetAllSQLitePlaceholdersAsString(placeholderDatabasePath).Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
lines.Length.ShouldEqual(10);
|
||||
this.PlaceholderDatabaseShouldIncludeCommonLines(lines);
|
||||
lines.ShouldContain(x => x == this.PartialFolderPlaceholderString("GVFS", "GVFS.Tests", "Properties"));
|
||||
return lines;
|
||||
}
|
||||
|
||||
private void RunEsentRepoMetadataUpgradeTest(string sourceVersion)
|
||||
{
|
||||
this.Enlistment.UnmountGVFS();
|
||||
|
||||
// Delete the existing repo metadata
|
||||
string versionJsonPath = Path.Combine(this.Enlistment.DotGVFSRoot, GVFSHelpers.RepoMetadataName);
|
||||
versionJsonPath.ShouldBeAFile(this.fileSystem);
|
||||
this.fileSystem.DeleteFile(versionJsonPath);
|
||||
|
||||
ESENTDatabase.SaveDiskLayoutVersionAsEsentDatabase(this.Enlistment.DotGVFSRoot, sourceVersion);
|
||||
string esentDatabasePath = Path.Combine(this.Enlistment.DotGVFSRoot, ESENTDatabase.EsentRepoMetadataFolder);
|
||||
esentDatabasePath.ShouldBeADirectory(this.fileSystem);
|
||||
|
||||
// We should be able to mount, and there should no longer be any Esent Repo Metadata
|
||||
this.Enlistment.MountGVFS();
|
||||
esentDatabasePath.ShouldNotExistOnDisk(this.fileSystem);
|
||||
versionJsonPath.ShouldBeAFile(this.fileSystem);
|
||||
|
||||
this.ValidatePersistedVersionMatchesCurrentVersion();
|
||||
|
||||
GVFSHelpers.GetPersistedLocalCacheRoot(this.Enlistment.DotGVFSRoot)
|
||||
.ShouldEqual(string.Empty, "LocalCacheRoot should be an empty string when upgrading from a version prior to 12");
|
||||
|
||||
// We're starting with fresh enlisments, and so the legacy cache location: <root>\.gvfs\gitObjectCache should not be on disk
|
||||
Path.Combine(this.Enlistment.DotGVFSRoot, ".gvfs", "gitObjectCache").ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
// The upgrader should set GitObjectsRoot to src\.git\objects (because the legacy cache location is not on disk)
|
||||
GVFSHelpers.GetPersistedGitObjectsRoot(this.Enlistment.DotGVFSRoot)
|
||||
.ShouldNotBeNull("GitObjectsRoot should not be null")
|
||||
.ShouldEqual(Path.Combine(this.Enlistment.RepoRoot, ".git", "objects"));
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,60 +0,0 @@
|
|||
using GVFS.FunctionalTests.FileSystemRunners;
|
||||
using GVFS.FunctionalTests.Should;
|
||||
using GVFS.FunctionalTests.Tools;
|
||||
using GVFS.Tests.Should;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
||||
{
|
||||
[TestFixture]
|
||||
[Category(Categories.WindowsOnly)]
|
||||
[Category(Categories.GitCommands)]
|
||||
public class WindowsTombstoneTests : TestsWithEnlistmentPerFixture
|
||||
{
|
||||
private const string Delimiter = "\r\n";
|
||||
private const int TombstoneFolderPlaceholderType = 3;
|
||||
private FileSystemRunner fileSystem;
|
||||
|
||||
public WindowsTombstoneTests()
|
||||
{
|
||||
this.fileSystem = new SystemIORunner();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CheckoutCleansUpTombstones()
|
||||
{
|
||||
const string folderToDelete = "Scripts";
|
||||
|
||||
// Delete directory to create the tombstone
|
||||
string directoryToDelete = this.Enlistment.GetVirtualPathTo(folderToDelete);
|
||||
this.fileSystem.DeleteDirectory(directoryToDelete);
|
||||
this.Enlistment.UnmountGVFS();
|
||||
|
||||
// Remove the directory entry from modified paths so git will not keep the folder up to date
|
||||
string modifiedPathsFile = Path.Combine(this.Enlistment.DotGVFSRoot, TestConstants.Databases.ModifiedPaths);
|
||||
string modifiedPathsContent = this.fileSystem.ReadAllText(modifiedPathsFile);
|
||||
modifiedPathsContent = string.Join(Delimiter, modifiedPathsContent.Split(new[] { Delimiter }, StringSplitOptions.RemoveEmptyEntries).Where(x => !x.StartsWith($"A {folderToDelete}/")));
|
||||
this.fileSystem.WriteAllText(modifiedPathsFile, modifiedPathsContent + Delimiter);
|
||||
|
||||
// Add tombstone folder entry to the placeholder database so the checkout will remove the tombstone
|
||||
// and start projecting the folder again
|
||||
string placeholderDatabasePath = Path.Combine(this.Enlistment.DotGVFSRoot, TestConstants.Databases.VFSForGit);
|
||||
GVFSHelpers.AddPlaceholderFolder(placeholderDatabasePath, folderToDelete, TombstoneFolderPlaceholderType);
|
||||
|
||||
this.Enlistment.MountGVFS();
|
||||
directoryToDelete.ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
// checkout branch to remove tombstones and project the folder again
|
||||
GitHelpers.InvokeGitAgainstGVFSRepo(this.Enlistment.RepoRoot, "checkout -f HEAD");
|
||||
directoryToDelete.ShouldBeADirectory(this.fileSystem);
|
||||
|
||||
this.Enlistment.UnmountGVFS();
|
||||
|
||||
string placholders = GVFSHelpers.GetAllSQLitePlaceholdersAsString(placeholderDatabasePath);
|
||||
placholders.ShouldNotContain(ignoreCase: false, unexpectedSubstrings: $"{folderToDelete}{GVFSHelpers.PlaceholderFieldDelimiter}{TombstoneFolderPlaceholderType}{GVFSHelpers.PlaceholderFieldDelimiter}");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,476 +0,0 @@
|
|||
using GVFS.FunctionalTests.FileSystemRunners;
|
||||
using GVFS.FunctionalTests.Should;
|
||||
using GVFS.FunctionalTests.Tools;
|
||||
using GVFS.Tests.Should;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
||||
{
|
||||
// WindowsOnly because tests in this class depend on Windows specific file sharing behavior
|
||||
[TestFixture]
|
||||
[Category(Categories.WindowsOnly)]
|
||||
[Category(Categories.GitCommands)]
|
||||
public class WindowsUpdatePlaceholderTests : TestsWithEnlistmentPerFixture
|
||||
{
|
||||
private const string TestParentFolderName = "Test_EPF_UpdatePlaceholderTests";
|
||||
private const string OldCommitId = "5d7a7d4db1734fb468a4094469ec58d26301b59d";
|
||||
private const string NewFilesAndChangesCommitId = "fec239ea12de1eda6ae5329d4f345784d5b61ff9";
|
||||
private FileSystemRunner fileSystem;
|
||||
|
||||
public WindowsUpdatePlaceholderTests()
|
||||
{
|
||||
this.fileSystem = new SystemIORunner();
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public virtual void SetupForTest()
|
||||
{
|
||||
// Start each test at NewFilesAndChangesCommitId
|
||||
this.GitCheckoutCommitId(NewFilesAndChangesCommitId);
|
||||
this.GitStatusShouldBeClean(NewFilesAndChangesCommitId);
|
||||
}
|
||||
|
||||
[TestCase, Order(1)]
|
||||
public void LockToPreventDelete_SingleFile()
|
||||
{
|
||||
string testFile1Contents = "TestContentsLockToPreventDelete \r\n";
|
||||
string testFile1Name = "test.txt";
|
||||
string testFile1Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "LockToPreventDelete", testFile1Name));
|
||||
|
||||
testFile1Path.ShouldBeAFile(this.fileSystem).WithContents(testFile1Contents);
|
||||
using (SafeFileHandle testFile1Handle = this.CreateFile(testFile1Path, FileShare.Read))
|
||||
{
|
||||
testFile1Handle.IsInvalid.ShouldEqual(false);
|
||||
|
||||
ProcessResult result = this.InvokeGitAgainstGVFSRepo("checkout " + OldCommitId);
|
||||
result.Errors.ShouldContain(
|
||||
"GVFS was unable to delete the following files. To recover, close all handles to the files and run these commands:",
|
||||
"git clean -f " + TestParentFolderName + "/LockToPreventDelete/" + testFile1Name);
|
||||
|
||||
GitHelpers.CheckGitCommandAgainstGVFSRepo(
|
||||
this.Enlistment.RepoRoot,
|
||||
"status -u",
|
||||
"HEAD detached at " + OldCommitId,
|
||||
"Untracked files:",
|
||||
TestParentFolderName + "/LockToPreventDelete/" + testFile1Name);
|
||||
}
|
||||
|
||||
this.GitCleanFile(TestParentFolderName + "/LockToPreventDelete/" + testFile1Name);
|
||||
this.GitStatusShouldBeClean(OldCommitId);
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, TestParentFolderName + "/LockToPreventDelete/" + testFile1Name);
|
||||
testFile1Path.ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
this.GitCheckoutCommitId(NewFilesAndChangesCommitId);
|
||||
|
||||
this.GitStatusShouldBeClean(NewFilesAndChangesCommitId);
|
||||
testFile1Path.ShouldBeAFile(this.fileSystem).WithContents(testFile1Contents);
|
||||
}
|
||||
|
||||
[TestCase, Order(2)]
|
||||
public void LockToPreventDelete_MultipleFiles()
|
||||
{
|
||||
string testFile2Contents = "TestContentsLockToPreventDelete2 \r\n";
|
||||
string testFile3Contents = "TestContentsLockToPreventDelete3 \r\n";
|
||||
string testFile4Contents = "TestContentsLockToPreventDelete4 \r\n";
|
||||
|
||||
string testFile2Name = "test2.txt";
|
||||
string testFile3Name = "test3.txt";
|
||||
string testFile4Name = "test4.txt";
|
||||
|
||||
string testFile2Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "LockToPreventDelete", testFile2Name));
|
||||
string testFile3Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "LockToPreventDelete", testFile3Name));
|
||||
string testFile4Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "LockToPreventDelete", testFile4Name));
|
||||
|
||||
testFile2Path.ShouldBeAFile(this.fileSystem).WithContents(testFile2Contents);
|
||||
testFile3Path.ShouldBeAFile(this.fileSystem).WithContents(testFile3Contents);
|
||||
testFile4Path.ShouldBeAFile(this.fileSystem).WithContents(testFile4Contents);
|
||||
|
||||
using (SafeFileHandle testFile2Handle = this.CreateFile(testFile2Path, FileShare.Read))
|
||||
using (SafeFileHandle testFile3Handle = this.CreateFile(testFile3Path, FileShare.Read))
|
||||
using (SafeFileHandle testFile4Handle = this.CreateFile(testFile4Path, FileShare.Read))
|
||||
{
|
||||
testFile2Handle.IsInvalid.ShouldEqual(false);
|
||||
testFile3Handle.IsInvalid.ShouldEqual(false);
|
||||
testFile4Handle.IsInvalid.ShouldEqual(false);
|
||||
|
||||
ProcessResult result = this.InvokeGitAgainstGVFSRepo("checkout " + OldCommitId);
|
||||
result.Errors.ShouldContain(
|
||||
"GVFS was unable to delete the following files. To recover, close all handles to the files and run these commands:",
|
||||
"git clean -f " + TestParentFolderName + "/LockToPreventDelete/" + testFile2Name,
|
||||
"git clean -f " + TestParentFolderName + "/LockToPreventDelete/" + testFile3Name,
|
||||
"git clean -f " + TestParentFolderName + "/LockToPreventDelete/" + testFile4Name);
|
||||
|
||||
GitHelpers.CheckGitCommandAgainstGVFSRepo(
|
||||
this.Enlistment.RepoRoot,
|
||||
"status -u",
|
||||
"HEAD detached at " + OldCommitId,
|
||||
"Untracked files:",
|
||||
TestParentFolderName + "/LockToPreventDelete/" + testFile2Name,
|
||||
TestParentFolderName + "/LockToPreventDelete/" + testFile3Name,
|
||||
TestParentFolderName + "/LockToPreventDelete/" + testFile4Name);
|
||||
}
|
||||
|
||||
this.GitCleanFile(TestParentFolderName + "/LockToPreventDelete/" + testFile2Name);
|
||||
this.GitCleanFile(TestParentFolderName + "/LockToPreventDelete/" + testFile3Name);
|
||||
this.GitCleanFile(TestParentFolderName + "/LockToPreventDelete/" + testFile4Name);
|
||||
|
||||
this.GitStatusShouldBeClean(OldCommitId);
|
||||
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, TestParentFolderName + "/LockToPreventDelete/" + testFile2Name);
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, TestParentFolderName + "/LockToPreventDelete/" + testFile3Name);
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, TestParentFolderName + "/LockToPreventDelete/" + testFile4Name);
|
||||
|
||||
testFile2Path.ShouldNotExistOnDisk(this.fileSystem);
|
||||
testFile3Path.ShouldNotExistOnDisk(this.fileSystem);
|
||||
testFile4Path.ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
this.GitCheckoutCommitId(NewFilesAndChangesCommitId);
|
||||
|
||||
this.GitStatusShouldBeClean(NewFilesAndChangesCommitId);
|
||||
testFile2Path.ShouldBeAFile(this.fileSystem).WithContents(testFile2Contents);
|
||||
testFile3Path.ShouldBeAFile(this.fileSystem).WithContents(testFile3Contents);
|
||||
testFile4Path.ShouldBeAFile(this.fileSystem).WithContents(testFile4Contents);
|
||||
}
|
||||
|
||||
[TestCase, Order(3)]
|
||||
public void LockToPreventUpdate_SingleFile()
|
||||
{
|
||||
string testFile1Contents = "Commit2LockToPreventUpdate \r\n";
|
||||
string testFile1OldContents = "TestFileLockToPreventUpdate \r\n";
|
||||
string testFile1Name = "test.txt";
|
||||
string testFile1Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "LockToPreventUpdate", testFile1Name));
|
||||
|
||||
testFile1Path.ShouldBeAFile(this.fileSystem).WithContents(testFile1Contents);
|
||||
using (SafeFileHandle testFile1Handle = this.CreateFile(testFile1Path, FileShare.Read))
|
||||
{
|
||||
testFile1Handle.IsInvalid.ShouldEqual(false);
|
||||
|
||||
ProcessResult result = this.InvokeGitAgainstGVFSRepo("checkout " + OldCommitId);
|
||||
result.Errors.ShouldContain(
|
||||
"GVFS was unable to update the following files. To recover, close all handles to the files and run these commands:",
|
||||
"git checkout -- " + TestParentFolderName + "/LockToPreventUpdate/" + testFile1Name);
|
||||
|
||||
GitHelpers.CheckGitCommandAgainstGVFSRepo(
|
||||
this.Enlistment.RepoRoot,
|
||||
"status",
|
||||
"HEAD detached at " + OldCommitId,
|
||||
"Changes not staged for commit:",
|
||||
TestParentFolderName + "/LockToPreventUpdate/" + testFile1Name);
|
||||
}
|
||||
|
||||
this.GitCheckoutToDiscardChanges(TestParentFolderName + "/LockToPreventUpdate/" + testFile1Name);
|
||||
this.GitStatusShouldBeClean(OldCommitId);
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, TestParentFolderName + "/LockToPreventUpdate/" + testFile1Name);
|
||||
testFile1Path.ShouldBeAFile(this.fileSystem).WithContents(testFile1OldContents);
|
||||
|
||||
this.GitCheckoutCommitId(NewFilesAndChangesCommitId);
|
||||
|
||||
this.GitStatusShouldBeClean(NewFilesAndChangesCommitId);
|
||||
testFile1Path.ShouldBeAFile(this.fileSystem).WithContents(testFile1Contents);
|
||||
}
|
||||
|
||||
[TestCase, Order(4)]
|
||||
public void LockToPreventUpdate_MultipleFiles()
|
||||
{
|
||||
string testFile2Contents = "Commit2LockToPreventUpdate2 \r\n";
|
||||
string testFile3Contents = "Commit2LockToPreventUpdate3 \r\n";
|
||||
string testFile4Contents = "Commit2LockToPreventUpdate4 \r\n";
|
||||
|
||||
string testFile2OldContents = "TestFileLockToPreventUpdate2 \r\n";
|
||||
string testFile3OldContents = "TestFileLockToPreventUpdate3 \r\n";
|
||||
string testFile4OldContents = "TestFileLockToPreventUpdate4 \r\n";
|
||||
|
||||
string testFile2Name = "test2.txt";
|
||||
string testFile3Name = "test3.txt";
|
||||
string testFile4Name = "test4.txt";
|
||||
|
||||
string testFile2Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "LockToPreventUpdate", testFile2Name));
|
||||
string testFile3Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "LockToPreventUpdate", testFile3Name));
|
||||
string testFile4Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "LockToPreventUpdate", testFile4Name));
|
||||
|
||||
testFile2Path.ShouldBeAFile(this.fileSystem).WithContents(testFile2Contents);
|
||||
testFile3Path.ShouldBeAFile(this.fileSystem).WithContents(testFile3Contents);
|
||||
testFile4Path.ShouldBeAFile(this.fileSystem).WithContents(testFile4Contents);
|
||||
|
||||
using (SafeFileHandle testFile2Handle = this.CreateFile(testFile2Path, FileShare.Read))
|
||||
using (SafeFileHandle testFile3Handle = this.CreateFile(testFile3Path, FileShare.Read))
|
||||
using (SafeFileHandle testFile4Handle = this.CreateFile(testFile4Path, FileShare.Read))
|
||||
{
|
||||
testFile2Handle.IsInvalid.ShouldEqual(false);
|
||||
testFile3Handle.IsInvalid.ShouldEqual(false);
|
||||
testFile4Handle.IsInvalid.ShouldEqual(false);
|
||||
|
||||
ProcessResult result = this.InvokeGitAgainstGVFSRepo("checkout " + OldCommitId);
|
||||
result.Errors.ShouldContain(
|
||||
"GVFS was unable to update the following files. To recover, close all handles to the files and run these commands:",
|
||||
"git checkout -- " + TestParentFolderName + "/LockToPreventUpdate/" + testFile2Name,
|
||||
"git checkout -- " + TestParentFolderName + "/LockToPreventUpdate/" + testFile3Name,
|
||||
"git checkout -- " + TestParentFolderName + "/LockToPreventUpdate/" + testFile4Name);
|
||||
|
||||
GitHelpers.CheckGitCommandAgainstGVFSRepo(
|
||||
this.Enlistment.RepoRoot,
|
||||
"status",
|
||||
"HEAD detached at " + OldCommitId,
|
||||
"Changes not staged for commit:",
|
||||
TestParentFolderName + "/LockToPreventUpdate/" + testFile2Name,
|
||||
TestParentFolderName + "/LockToPreventUpdate/" + testFile3Name,
|
||||
TestParentFolderName + "/LockToPreventUpdate/" + testFile4Name);
|
||||
}
|
||||
|
||||
this.GitCheckoutToDiscardChanges(TestParentFolderName + "/LockToPreventUpdate/" + testFile2Name);
|
||||
this.GitCheckoutToDiscardChanges(TestParentFolderName + "/LockToPreventUpdate/" + testFile3Name);
|
||||
this.GitCheckoutToDiscardChanges(TestParentFolderName + "/LockToPreventUpdate/" + testFile4Name);
|
||||
|
||||
this.GitStatusShouldBeClean(OldCommitId);
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, TestParentFolderName + "/LockToPreventUpdate/" + testFile2Name);
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, TestParentFolderName + "/LockToPreventUpdate/" + testFile3Name);
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, TestParentFolderName + "/LockToPreventUpdate/" + testFile4Name);
|
||||
testFile2Path.ShouldBeAFile(this.fileSystem).WithContents(testFile2OldContents);
|
||||
testFile3Path.ShouldBeAFile(this.fileSystem).WithContents(testFile3OldContents);
|
||||
testFile4Path.ShouldBeAFile(this.fileSystem).WithContents(testFile4OldContents);
|
||||
|
||||
this.GitCheckoutCommitId(NewFilesAndChangesCommitId);
|
||||
|
||||
this.GitStatusShouldBeClean(NewFilesAndChangesCommitId);
|
||||
testFile2Path.ShouldBeAFile(this.fileSystem).WithContents(testFile2Contents);
|
||||
testFile3Path.ShouldBeAFile(this.fileSystem).WithContents(testFile3Contents);
|
||||
testFile4Path.ShouldBeAFile(this.fileSystem).WithContents(testFile4Contents);
|
||||
}
|
||||
|
||||
[TestCase, Order(5)]
|
||||
public void LockToPreventUpdateAndDelete()
|
||||
{
|
||||
string testFileUpdate1Contents = "Commit2LockToPreventUpdateAndDelete \r\n";
|
||||
string testFileUpdate2Contents = "Commit2LockToPreventUpdateAndDelete2 \r\n";
|
||||
string testFileUpdate3Contents = "Commit2LockToPreventUpdateAndDelete3 \r\n";
|
||||
string testFileDelete1Contents = "PreventDelete \r\n";
|
||||
string testFileDelete2Contents = "PreventDelete2 \r\n";
|
||||
string testFileDelete3Contents = "PreventDelete3 \r\n";
|
||||
|
||||
string testFileUpdate1OldContents = "TestFileLockToPreventUpdateAndDelete \r\n";
|
||||
string testFileUpdate2OldContents = "TestFileLockToPreventUpdateAndDelete2 \r\n";
|
||||
string testFileUpdate3OldContents = "TestFileLockToPreventUpdateAndDelete3 \r\n";
|
||||
|
||||
string testFileUpdate1Name = "test.txt";
|
||||
string testFileUpdate2Name = "test2.txt";
|
||||
string testFileUpdate3Name = "test3.txt";
|
||||
string testFileDelete1Name = "test_delete.txt";
|
||||
string testFileDelete2Name = "test_delete2.txt";
|
||||
string testFileDelete3Name = "test_delete3.txt";
|
||||
|
||||
string testFileUpdate1Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "LockToPreventUpdateAndDelete", testFileUpdate1Name));
|
||||
string testFileUpdate2Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "LockToPreventUpdateAndDelete", testFileUpdate2Name));
|
||||
string testFileUpdate3Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "LockToPreventUpdateAndDelete", testFileUpdate3Name));
|
||||
string testFileDelete1Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "LockToPreventUpdateAndDelete", testFileDelete1Name));
|
||||
string testFileDelete2Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "LockToPreventUpdateAndDelete", testFileDelete2Name));
|
||||
string testFileDelete3Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "LockToPreventUpdateAndDelete", testFileDelete3Name));
|
||||
|
||||
testFileUpdate1Path.ShouldBeAFile(this.fileSystem).WithContents(testFileUpdate1Contents);
|
||||
testFileUpdate2Path.ShouldBeAFile(this.fileSystem).WithContents(testFileUpdate2Contents);
|
||||
testFileUpdate3Path.ShouldBeAFile(this.fileSystem).WithContents(testFileUpdate3Contents);
|
||||
testFileDelete1Path.ShouldBeAFile(this.fileSystem).WithContents(testFileDelete1Contents);
|
||||
testFileDelete2Path.ShouldBeAFile(this.fileSystem).WithContents(testFileDelete2Contents);
|
||||
testFileDelete3Path.ShouldBeAFile(this.fileSystem).WithContents(testFileDelete3Contents);
|
||||
|
||||
using (SafeFileHandle testFileUpdate1Handle = this.CreateFile(testFileUpdate1Path, FileShare.Read))
|
||||
using (SafeFileHandle testFileUpdate2Handle = this.CreateFile(testFileUpdate2Path, FileShare.Read))
|
||||
using (SafeFileHandle testFileUpdate3Handle = this.CreateFile(testFileUpdate3Path, FileShare.Read))
|
||||
using (SafeFileHandle testFileDelete1Handle = this.CreateFile(testFileDelete1Path, FileShare.Read))
|
||||
using (SafeFileHandle testFileDelete2Handle = this.CreateFile(testFileDelete2Path, FileShare.Read))
|
||||
using (SafeFileHandle testFileDelete3Handle = this.CreateFile(testFileDelete3Path, FileShare.Read))
|
||||
{
|
||||
testFileUpdate1Handle.IsInvalid.ShouldEqual(false);
|
||||
testFileUpdate2Handle.IsInvalid.ShouldEqual(false);
|
||||
testFileUpdate3Handle.IsInvalid.ShouldEqual(false);
|
||||
testFileDelete1Handle.IsInvalid.ShouldEqual(false);
|
||||
testFileDelete2Handle.IsInvalid.ShouldEqual(false);
|
||||
testFileDelete3Handle.IsInvalid.ShouldEqual(false);
|
||||
|
||||
ProcessResult checkoutResult = GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "checkout " + OldCommitId);
|
||||
checkoutResult.Errors.ShouldContain(
|
||||
"HEAD is now at " + OldCommitId,
|
||||
"GVFS was unable to delete the following files. To recover, close all handles to the files and run these commands:",
|
||||
"git clean -f " + TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileDelete1Name,
|
||||
"git clean -f " + TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileDelete2Name,
|
||||
"git clean -f " + TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileDelete3Name,
|
||||
"GVFS was unable to update the following files. To recover, close all handles to the files and run these commands:",
|
||||
"git checkout -- " + TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileUpdate1Name,
|
||||
"git checkout -- " + TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileUpdate2Name,
|
||||
"git checkout -- " + TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileUpdate3Name);
|
||||
|
||||
GitHelpers.CheckGitCommandAgainstGVFSRepo(
|
||||
this.Enlistment.RepoRoot,
|
||||
"status",
|
||||
"HEAD detached at " + OldCommitId,
|
||||
"modified: Test_EPF_UpdatePlaceholderTests/LockToPreventUpdateAndDelete/test.txt",
|
||||
"modified: Test_EPF_UpdatePlaceholderTests/LockToPreventUpdateAndDelete/test2.txt",
|
||||
"modified: Test_EPF_UpdatePlaceholderTests/LockToPreventUpdateAndDelete/test3.txt",
|
||||
"Untracked files:\n (use \"git add <file>...\" to include in what will be committed)\n\n\tTest_EPF_UpdatePlaceholderTests/LockToPreventUpdateAndDelete/test_delete.txt\n\tTest_EPF_UpdatePlaceholderTests/LockToPreventUpdateAndDelete/test_delete2.txt\n\tTest_EPF_UpdatePlaceholderTests/LockToPreventUpdateAndDelete/test_delete3.txt",
|
||||
"no changes added to commit (use \"git add\" and/or \"git commit -a\")\n");
|
||||
}
|
||||
|
||||
this.GitCheckoutToDiscardChanges(TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileUpdate1Name);
|
||||
this.GitCheckoutToDiscardChanges(TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileUpdate2Name);
|
||||
this.GitCheckoutToDiscardChanges(TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileUpdate3Name);
|
||||
this.GitCleanFile(TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileDelete1Name);
|
||||
this.GitCleanFile(TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileDelete2Name);
|
||||
this.GitCleanFile(TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileDelete3Name);
|
||||
|
||||
this.GitStatusShouldBeClean(OldCommitId);
|
||||
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileUpdate1Name);
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileUpdate2Name);
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileUpdate3Name);
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileDelete1Name);
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileDelete2Name);
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileDelete3Name);
|
||||
|
||||
testFileUpdate1Path.ShouldBeAFile(this.fileSystem).WithContents(testFileUpdate1OldContents);
|
||||
testFileUpdate2Path.ShouldBeAFile(this.fileSystem).WithContents(testFileUpdate2OldContents);
|
||||
testFileUpdate3Path.ShouldBeAFile(this.fileSystem).WithContents(testFileUpdate3OldContents);
|
||||
testFileDelete1Path.ShouldNotExistOnDisk(this.fileSystem);
|
||||
testFileDelete2Path.ShouldNotExistOnDisk(this.fileSystem);
|
||||
testFileDelete3Path.ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
this.GitCheckoutCommitId(NewFilesAndChangesCommitId);
|
||||
|
||||
this.GitStatusShouldBeClean(NewFilesAndChangesCommitId);
|
||||
testFileUpdate1Path.ShouldBeAFile(this.fileSystem).WithContents(testFileUpdate1Contents);
|
||||
testFileUpdate2Path.ShouldBeAFile(this.fileSystem).WithContents(testFileUpdate2Contents);
|
||||
testFileUpdate3Path.ShouldBeAFile(this.fileSystem).WithContents(testFileUpdate3Contents);
|
||||
testFileDelete1Path.ShouldBeAFile(this.fileSystem).WithContents(testFileDelete1Contents);
|
||||
testFileDelete2Path.ShouldBeAFile(this.fileSystem).WithContents(testFileDelete2Contents);
|
||||
testFileDelete3Path.ShouldBeAFile(this.fileSystem).WithContents(testFileDelete3Contents);
|
||||
}
|
||||
|
||||
[TestCase, Order(6)]
|
||||
public void LockMoreThanMaxReportedFileNames()
|
||||
{
|
||||
string updateFilesFolder = "FilesToUpdate";
|
||||
string deleteFilesFolder = "FilesToDelete";
|
||||
|
||||
for (int i = 1; i <= 51; ++i)
|
||||
{
|
||||
this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "MaxFileListCount", updateFilesFolder, i.ToString() + ".txt")).ShouldBeAFile(this.fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "MaxFileListCount", deleteFilesFolder, i.ToString() + ".txt")).ShouldBeAFile(this.fileSystem);
|
||||
}
|
||||
|
||||
List<SafeFileHandle> openHandles = new List<SafeFileHandle>();
|
||||
try
|
||||
{
|
||||
for (int i = 1; i <= 51; ++i)
|
||||
{
|
||||
SafeFileHandle handle = this.CreateFile(
|
||||
this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "MaxFileListCount", updateFilesFolder, i.ToString() + ".txt")),
|
||||
FileShare.Read);
|
||||
openHandles.Add(handle);
|
||||
handle.IsInvalid.ShouldEqual(false);
|
||||
|
||||
handle = this.CreateFile(
|
||||
this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "MaxFileListCount", deleteFilesFolder, i.ToString() + ".txt")),
|
||||
FileShare.Read);
|
||||
openHandles.Add(handle);
|
||||
handle.IsInvalid.ShouldEqual(false);
|
||||
}
|
||||
|
||||
ProcessResult result = this.InvokeGitAgainstGVFSRepo("checkout " + OldCommitId);
|
||||
result.Errors.ShouldContain(
|
||||
"GVFS failed to update 102 files, run 'git status' to check the status of files in the repo");
|
||||
|
||||
List<string> expectedOutputStrings = new List<string>()
|
||||
{
|
||||
"HEAD detached at " + OldCommitId,
|
||||
"no changes added to commit (use \"git add\" and/or \"git commit -a\")\n"
|
||||
};
|
||||
|
||||
for (int expectedFilePrefix = 1; expectedFilePrefix <= 51; ++expectedFilePrefix)
|
||||
{
|
||||
expectedOutputStrings.Add("modified: Test_EPF_UpdatePlaceholderTests/MaxFileListCount/" + updateFilesFolder + "/" + expectedFilePrefix.ToString() + ".txt");
|
||||
expectedOutputStrings.Add("Test_EPF_UpdatePlaceholderTests/MaxFileListCount/" + deleteFilesFolder + "/" + expectedFilePrefix.ToString() + ".txt");
|
||||
}
|
||||
|
||||
GitHelpers.CheckGitCommandAgainstGVFSRepo(this.Enlistment.RepoRoot, "status -u", expectedOutputStrings.ToArray());
|
||||
}
|
||||
finally
|
||||
{
|
||||
foreach (SafeFileHandle handle in openHandles)
|
||||
{
|
||||
handle.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 1; i <= 51; ++i)
|
||||
{
|
||||
this.GitCheckoutToDiscardChanges(TestParentFolderName + "/MaxFileListCount/" + updateFilesFolder + "/" + i.ToString() + ".txt");
|
||||
this.GitCleanFile(TestParentFolderName + "/MaxFileListCount/" + deleteFilesFolder + "/" + i.ToString() + ".txt");
|
||||
}
|
||||
|
||||
this.GitStatusShouldBeClean(OldCommitId);
|
||||
this.GitCheckoutCommitId(NewFilesAndChangesCommitId);
|
||||
this.GitStatusShouldBeClean(NewFilesAndChangesCommitId);
|
||||
}
|
||||
|
||||
private ProcessResult InvokeGitAgainstGVFSRepo(string command)
|
||||
{
|
||||
return GitHelpers.InvokeGitAgainstGVFSRepo(this.Enlistment.RepoRoot, command);
|
||||
}
|
||||
|
||||
private void GitStatusShouldBeClean(string commitId)
|
||||
{
|
||||
GitHelpers.CheckGitCommandAgainstGVFSRepo(
|
||||
this.Enlistment.RepoRoot,
|
||||
"status",
|
||||
"HEAD detached at " + commitId,
|
||||
"nothing to commit, working tree clean");
|
||||
}
|
||||
|
||||
private void GitCleanFile(string gitPath)
|
||||
{
|
||||
GitHelpers.CheckGitCommandAgainstGVFSRepo(
|
||||
this.Enlistment.RepoRoot,
|
||||
"clean -f " + gitPath,
|
||||
"Removing " + gitPath);
|
||||
}
|
||||
|
||||
private void GitCheckoutToDiscardChanges(string gitPath)
|
||||
{
|
||||
GitHelpers.CheckGitCommandAgainstGVFSRepo(this.Enlistment.RepoRoot, "checkout -- " + gitPath);
|
||||
}
|
||||
|
||||
private void GitCheckoutCommitId(string commitId)
|
||||
{
|
||||
this.InvokeGitAgainstGVFSRepo("checkout " + commitId).Errors.ShouldContain("HEAD is now at " + commitId);
|
||||
}
|
||||
|
||||
private SafeFileHandle CreateFile(string path, FileShare shareMode)
|
||||
{
|
||||
return NativeMethods.CreateFile(
|
||||
path,
|
||||
(uint)FileAccess.Read,
|
||||
shareMode,
|
||||
IntPtr.Zero,
|
||||
FileMode.Open,
|
||||
(uint)FileAttributes.Normal,
|
||||
IntPtr.Zero);
|
||||
}
|
||||
|
||||
private bool CanUpdateAndDeletePlaceholdersWithOpenHandles()
|
||||
{
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724429(v=vs.85).aspx
|
||||
FileVersionInfo kernel32Info = FileVersionInfo.GetVersionInfo(Path.Combine(Environment.SystemDirectory, "kernel32.dll"));
|
||||
|
||||
// 16248 is first build with support - see 12658248 for details
|
||||
if (kernel32Info.FileBuildPart >= 16248)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Microsoft.Isam.Esent.Collections.Generic;
|
||||
|
||||
namespace GVFS.FunctionalTests.Windows.Tools
|
||||
{
|
||||
public static class ESENTDatabase
|
||||
{
|
||||
public const string EsentRepoMetadataFolder = "RepoMetadata";
|
||||
public const string EsentBackgroundOpsFolder = "BackgroundGitUpdates";
|
||||
public const string EsentBlobSizesFolder = "BlobSizes";
|
||||
public const string EsentPlaceholderFolder = "PlaceholderList";
|
||||
|
||||
private const string DiskLayoutMajorVersionKey = "DiskLayoutVersion";
|
||||
|
||||
public static void SaveDiskLayoutVersionAsEsentDatabase(string dotGVFSRoot, string majorVersion)
|
||||
{
|
||||
string metadataPath = Path.Combine(dotGVFSRoot, EsentRepoMetadataFolder);
|
||||
using (PersistentDictionary<string, string> repoMetadata = new PersistentDictionary<string, string>(metadataPath))
|
||||
{
|
||||
repoMetadata[DiskLayoutMajorVersionKey] = majorVersion;
|
||||
repoMetadata.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
public static void CreateEsentPlaceholderDatabase(string dotGVFSRoot)
|
||||
{
|
||||
string metadataPath = Path.Combine(dotGVFSRoot, EsentPlaceholderFolder);
|
||||
using (PersistentDictionary<string, string> placeholders = new PersistentDictionary<string, string>(metadataPath))
|
||||
{
|
||||
placeholders["mock:\\path"] = new string('0', 40);
|
||||
placeholders.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
public static void CreateEsentBackgroundOpsDatabase(string dotGVFSRoot)
|
||||
{
|
||||
// Copies an ESENT DB with a single entry:
|
||||
// Operation=6 (OnFirstWrite) Path=.gitattributes VirtualPath=.gitattributes Id=1
|
||||
string testDataPath = GetTestDataPath(EsentBackgroundOpsFolder);
|
||||
string metadataPath = Path.Combine(dotGVFSRoot, EsentBackgroundOpsFolder);
|
||||
Directory.CreateDirectory(metadataPath);
|
||||
foreach (string filepath in Directory.EnumerateFiles(testDataPath))
|
||||
{
|
||||
string filename = Path.GetFileName(filepath);
|
||||
File.Copy(filepath, Path.Combine(metadataPath, filename));
|
||||
}
|
||||
}
|
||||
|
||||
public static void CreateEsentBlobSizesDatabase(string dotGVFSRoot, List<KeyValuePair<string, long>> entries)
|
||||
{
|
||||
string metadataPath = Path.Combine(dotGVFSRoot, EsentBlobSizesFolder);
|
||||
using (PersistentDictionary<string, long> blobSizes = new PersistentDictionary<string, long>(metadataPath))
|
||||
{
|
||||
foreach (KeyValuePair<string, long> entry in entries)
|
||||
{
|
||||
blobSizes[entry.Key] = entry.Value;
|
||||
}
|
||||
|
||||
blobSizes.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetTestDataPath(string fileName)
|
||||
{
|
||||
string workingDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
return Path.Combine(workingDirectory, "Windows", "TestData", fileName);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
using Microsoft.Win32;
|
||||
|
||||
namespace GVFS.FunctionalTests.Windows.Tools
|
||||
{
|
||||
public class RegistryHelper
|
||||
{
|
||||
public static object GetValueFromRegistry(RegistryHive registryHive, string key, string valueName, RegistryView view)
|
||||
{
|
||||
RegistryKey localKey = RegistryKey.OpenBaseKey(registryHive, view);
|
||||
RegistryKey localKeySub = localKey.OpenSubKey(key);
|
||||
|
||||
object value = localKeySub == null ? null : localKeySub.GetValue(valueName);
|
||||
return value;
|
||||
}
|
||||
|
||||
public static object GetValueFromRegistry(RegistryHive registryHive, string key, string valueName)
|
||||
{
|
||||
object value = GetValueFromRegistry(registryHive, key, valueName, RegistryView.Registry64);
|
||||
if (value == null)
|
||||
{
|
||||
value = GetValueFromRegistry(registryHive, key, valueName, RegistryView.Registry32);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static bool TrySetDWordInRegistry(RegistryHive registryHive, string key, string valueName, uint value)
|
||||
{
|
||||
RegistryKey localKey = RegistryKey.OpenBaseKey(registryHive, RegistryView.Registry64);
|
||||
RegistryKey localKeySub = localKey.OpenSubKey(key, writable: true);
|
||||
|
||||
if (localKeySub == null)
|
||||
{
|
||||
localKey = RegistryKey.OpenBaseKey(registryHive, RegistryView.Registry32);
|
||||
localKeySub = localKey.OpenSubKey(key, writable: true);
|
||||
}
|
||||
|
||||
if (localKeySub == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
localKeySub.SetValue(valueName, value, RegistryValueKind.DWord);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="ManagedEsent" version="1.9.4" targetFramework="net461" />
|
||||
<package id="Microsoft.Data.Sqlite" version="2.2.4" targetFramework="net461" />
|
||||
<package id="Microsoft.Data.Sqlite.Core" version="2.2.4" targetFramework="net461" />
|
||||
<package id="Newtonsoft.Json" version="11.0.2" targetFramework="net461" />
|
||||
<package id="NUnit" version="3.10.1" targetFramework="net461" />
|
||||
<package id="NUnitLite" version="3.10.1" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.bundle_green" version="1.1.12" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.core" version="1.1.12" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.linux" version="1.1.12" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.osx" version="1.1.12" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.lib.e_sqlite3.v110_xp" version="1.1.12" targetFramework="net461" />
|
||||
<package id="SQLitePCLRaw.provider.e_sqlite3.net45" version="1.1.12" targetFramework="net461" />
|
||||
<package id="StyleCop.Analyzers" version="1.0.2" targetFramework="net461" developmentDependency="true" />
|
||||
</packages>
|
|
@ -40,7 +40,6 @@
|
|||
<WindowsBuildOutputs Include="$(BuildOutputDir)\GVFS.Windows\bin\$(Platform)\$(Configuration)\$(Platform)\**\*.*" />
|
||||
<WindowsBuildOutputs Include="$(BuildOutputDir)\GVFS.Service.Windows\bin\$(Platform)\$(Configuration)\**\*.*" />
|
||||
<WindowsBuildOutputs Include="$(BuildOutputDir)\GVFS.Mount.Windows\bin\$(Platform)\$(Configuration)\**\*.*" />
|
||||
<WindowsBuildOutputs Include="$(BuildOutputDir)\FastFetch\bin\$(Platform)\$(Configuration)\**\*.*" />
|
||||
<WindowsBuildOutputs Include="$(BuildOutputDir)\GVFS.NativeTests\bin\$(Platform)\$(Configuration)\**\*.*" />
|
||||
<WindowsBuildOutputs Include="$(BuildOutputDir)\GVFS.Hooks.Windows\bin\$(Platform)\$(Configuration)\**\*.*" />
|
||||
<WindowsBuildOutputs Include="$(BuildOutputDir)\GVFS.FunctionalTests.LockHolder\bin\$(Platform)\$(Configuration)\**\*.*" />
|
||||
|
|
|
@ -16,8 +16,6 @@ namespace GVFS.FunctionalTests
|
|||
|
||||
public static bool TestGVFSOnPath { get; set; }
|
||||
|
||||
public static bool ReplaceInboxProjFS { get; set; }
|
||||
|
||||
public static string PathToGVFS
|
||||
{
|
||||
get
|
||||
|
|
|
@ -28,12 +28,6 @@ namespace GVFS.FunctionalTests
|
|||
GVFSTestConfig.TestGVFSOnPath = true;
|
||||
}
|
||||
|
||||
if (runner.HasCustomArg("--replace-inbox-projfs"))
|
||||
{
|
||||
Console.WriteLine("Tests will replace inbox ProjFS");
|
||||
GVFSTestConfig.ReplaceInboxProjFS = true;
|
||||
}
|
||||
|
||||
GVFSTestConfig.LocalCacheRoot = runner.GetCustomArgWithParam("--shared-gvfs-cache-root");
|
||||
|
||||
HashSet<string> includeCategories = new HashSet<string>();
|
||||
|
@ -135,11 +129,6 @@ namespace GVFS.FunctionalTests
|
|||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
if (GVFSTestConfig.ReplaceInboxProjFS)
|
||||
{
|
||||
ProjFSFilterInstaller.ReplaceInboxProjFS();
|
||||
}
|
||||
|
||||
GVFSServiceProcess.InstallService();
|
||||
|
||||
string statusCacheVersionTokenPath = Path.Combine(
|
||||
|
|
|
@ -1,963 +0,0 @@
|
|||
using GVFS.FunctionalTests.FileSystemRunners;
|
||||
using GVFS.FunctionalTests.Should;
|
||||
using GVFS.FunctionalTests.Tests.EnlistmentPerFixture;
|
||||
using GVFS.FunctionalTests.Tools;
|
||||
using GVFS.Tests.Should;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace GVFS.FunctionalTests.Tests.LongRunningEnlistment
|
||||
{
|
||||
[TestFixture]
|
||||
public class BasicFileSystemTests : TestsWithEnlistmentPerFixture
|
||||
{
|
||||
private const int FileAttributeSparseFile = 0x00000200;
|
||||
private const int FileAttributeReparsePoint = 0x00000400;
|
||||
private const int FileAttributeRecallOnDataAccess = 0x00400000;
|
||||
|
||||
[TestCaseSource(typeof(FileRunnersAndFolders), nameof(FileRunnersAndFolders.Runners))]
|
||||
public void ShrinkFileContents(FileSystemRunner fileSystem, string parentFolder)
|
||||
{
|
||||
string filename = Path.Combine(parentFolder, "ShrinkFileContents");
|
||||
string originalVirtualContents = "0123456789";
|
||||
fileSystem.WriteAllText(this.Enlistment.GetVirtualPathTo(filename), originalVirtualContents);
|
||||
this.Enlistment.GetVirtualPathTo(filename).ShouldBeAFile(fileSystem).WithContents(originalVirtualContents);
|
||||
|
||||
string newText = "112233";
|
||||
fileSystem.WriteAllText(this.Enlistment.GetVirtualPathTo(filename), newText);
|
||||
this.Enlistment.GetVirtualPathTo(filename).ShouldBeAFile(fileSystem).WithContents(newText);
|
||||
fileSystem.DeleteFile(this.Enlistment.GetVirtualPathTo(filename));
|
||||
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, filename, parentFolder);
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileRunnersAndFolders), nameof(FileRunnersAndFolders.Runners))]
|
||||
public void GrowFileContents(FileSystemRunner fileSystem, string parentFolder)
|
||||
{
|
||||
string filename = Path.Combine(parentFolder, "GrowFileContents");
|
||||
string originalVirtualContents = "112233";
|
||||
fileSystem.WriteAllText(this.Enlistment.GetVirtualPathTo(filename), originalVirtualContents);
|
||||
this.Enlistment.GetVirtualPathTo(filename).ShouldBeAFile(fileSystem).WithContents(originalVirtualContents);
|
||||
|
||||
string newText = "0123456789";
|
||||
fileSystem.WriteAllText(this.Enlistment.GetVirtualPathTo(filename), newText);
|
||||
this.Enlistment.GetVirtualPathTo(filename).ShouldBeAFile(fileSystem).WithContents(newText);
|
||||
fileSystem.DeleteFile(this.Enlistment.GetVirtualPathTo(filename));
|
||||
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, filename, parentFolder);
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileRunnersAndFolders), nameof(FileRunnersAndFolders.Runners))]
|
||||
public void FilesAreBufferedAndCanBeFlushed(FileSystemRunner fileSystem, string parentFolder)
|
||||
{
|
||||
string filename = Path.Combine(parentFolder, "FilesAreBufferedAndCanBeFlushed");
|
||||
string filePath = this.Enlistment.GetVirtualPathTo(filename);
|
||||
|
||||
byte[] buffer = System.Text.Encoding.ASCII.GetBytes("Some test data");
|
||||
|
||||
using (FileStream writeStream = File.Open(filePath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.ReadWrite))
|
||||
{
|
||||
writeStream.Write(buffer, 0, buffer.Length);
|
||||
|
||||
using (FileStream readStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
|
||||
{
|
||||
readStream.Length.ShouldEqual(0);
|
||||
writeStream.Flush();
|
||||
readStream.Length.ShouldEqual(buffer.Length);
|
||||
|
||||
byte[] readBuffer = new byte[buffer.Length];
|
||||
readStream.Read(readBuffer, 0, readBuffer.Length).ShouldEqual(readBuffer.Length);
|
||||
readBuffer.ShouldMatchInOrder(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
fileSystem.DeleteFile(filePath);
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileRunnersAndFolders), nameof(FileRunnersAndFolders.Folders))]
|
||||
[Category(Categories.WindowsOnly)]
|
||||
public void NewFileAttributesAreUpdated(string parentFolder)
|
||||
{
|
||||
string filename = Path.Combine(parentFolder, "FileAttributesAreUpdated");
|
||||
FileSystemRunner fileSystem = FileSystemRunner.DefaultRunner;
|
||||
|
||||
string virtualFile = this.Enlistment.GetVirtualPathTo(filename);
|
||||
virtualFile.ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
File.Create(virtualFile).Dispose();
|
||||
virtualFile.ShouldBeAFile(fileSystem);
|
||||
|
||||
// Update defaults. FileInfo is not batched, so each of these will create a separate Open-Update-Close set.
|
||||
FileInfo before = new FileInfo(virtualFile);
|
||||
DateTime testValue = DateTime.Now + TimeSpan.FromDays(1);
|
||||
before.CreationTime = testValue;
|
||||
before.LastAccessTime = testValue;
|
||||
before.LastWriteTime = testValue;
|
||||
before.Attributes = FileAttributes.Hidden;
|
||||
|
||||
// FileInfo caches information. We can refresh, but just to be absolutely sure...
|
||||
virtualFile.ShouldBeAFile(fileSystem).WithInfo(testValue, testValue, testValue, FileAttributes.Hidden);
|
||||
|
||||
File.Delete(virtualFile);
|
||||
virtualFile.ShouldNotExistOnDisk(fileSystem);
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileRunnersAndFolders), nameof(FileRunnersAndFolders.Folders))]
|
||||
[Category(Categories.WindowsOnly)]
|
||||
public void NewFolderAttributesAreUpdated(string parentFolder)
|
||||
{
|
||||
string folderName = Path.Combine(parentFolder, "FolderAttributesAreUpdated");
|
||||
string virtualFolder = this.Enlistment.GetVirtualPathTo(folderName);
|
||||
Directory.CreateDirectory(virtualFolder);
|
||||
|
||||
FileSystemRunner fileSystem = FileSystemRunner.DefaultRunner;
|
||||
|
||||
virtualFolder.ShouldBeADirectory(fileSystem);
|
||||
|
||||
// Update defaults. DirectoryInfo is not batched, so each of these will create a separate Open-Update-Close set.
|
||||
DirectoryInfo before = new DirectoryInfo(virtualFolder);
|
||||
DateTime testValue = DateTime.Now + TimeSpan.FromDays(1);
|
||||
before.CreationTime = testValue;
|
||||
before.LastAccessTime = testValue;
|
||||
before.LastWriteTime = testValue;
|
||||
before.Attributes = FileAttributes.Hidden;
|
||||
|
||||
// DirectoryInfo caches information. We can refresh, but just to be absolutely sure...
|
||||
virtualFolder.ShouldBeADirectory(fileSystem)
|
||||
.WithInfo(testValue, testValue, testValue, FileAttributes.Hidden | FileAttributes.Directory, ignoreRecallAttributes: false);
|
||||
|
||||
Directory.Delete(virtualFolder);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
[Category(Categories.WindowsOnly)]
|
||||
public void ExpandedFileAttributesAreUpdated()
|
||||
{
|
||||
FileSystemRunner fileSystem = FileSystemRunner.DefaultRunner;
|
||||
|
||||
string filename = Path.Combine("GVFS", "GVFS", "GVFS.csproj");
|
||||
string virtualFile = this.Enlistment.GetVirtualPathTo(filename);
|
||||
|
||||
// Update defaults. FileInfo is not batched, so each of these will create a separate Open-Update-Close set.
|
||||
FileInfo before = new FileInfo(virtualFile);
|
||||
DateTime testValue = DateTime.Now + TimeSpan.FromDays(1);
|
||||
|
||||
// Setting the CreationTime results in a write handle being open to the file and the file being expanded
|
||||
before.CreationTime = testValue;
|
||||
before.LastAccessTime = testValue;
|
||||
before.LastWriteTime = testValue;
|
||||
before.Attributes = FileAttributes.Hidden;
|
||||
|
||||
// FileInfo caches information. We can refresh, but just to be absolutely sure...
|
||||
FileInfo info = virtualFile.ShouldBeAFile(fileSystem).WithInfo(testValue, testValue, testValue);
|
||||
|
||||
// Ignore the archive bit as it can be re-added to the file as part of its expansion to full
|
||||
FileAttributes attributes = info.Attributes & ~FileAttributes.Archive;
|
||||
|
||||
int retryCount = 0;
|
||||
int maxRetries = 10;
|
||||
while (attributes != FileAttributes.Hidden && retryCount < maxRetries)
|
||||
{
|
||||
// ProjFS attributes are remoted asynchronously when files are converted to full
|
||||
FileAttributes attributesLessProjFS = attributes & (FileAttributes)~(FileAttributeSparseFile | FileAttributeReparsePoint | FileAttributeRecallOnDataAccess);
|
||||
|
||||
attributesLessProjFS.ShouldEqual(
|
||||
FileAttributes.Hidden,
|
||||
$"Attributes (ignoring ProjFS attributes) do not match, expected: {FileAttributes.Hidden} actual: {attributesLessProjFS}");
|
||||
|
||||
++retryCount;
|
||||
Thread.Sleep(500);
|
||||
|
||||
info.Refresh();
|
||||
attributes = info.Attributes & ~FileAttributes.Archive;
|
||||
}
|
||||
|
||||
attributes.ShouldEqual(FileAttributes.Hidden, $"Attributes do not match, expected: {FileAttributes.Hidden} actual: {attributes}");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
[Category(Categories.WindowsOnly)]
|
||||
public void UnhydratedFolderAttributesAreUpdated()
|
||||
{
|
||||
FileSystemRunner fileSystem = FileSystemRunner.DefaultRunner;
|
||||
|
||||
string folderName = Path.Combine("GVFS", "GVFS", "CommandLine");
|
||||
string virtualFolder = this.Enlistment.GetVirtualPathTo(folderName);
|
||||
|
||||
// Update defaults. DirectoryInfo is not batched, so each of these will create a separate Open-Update-Close set.
|
||||
DirectoryInfo before = new DirectoryInfo(virtualFolder);
|
||||
DateTime testValue = DateTime.Now + TimeSpan.FromDays(1);
|
||||
before.CreationTime = testValue;
|
||||
before.LastAccessTime = testValue;
|
||||
before.LastWriteTime = testValue;
|
||||
before.Attributes = FileAttributes.Hidden;
|
||||
|
||||
// DirectoryInfo caches information. We can refresh, but just to be absolutely sure...
|
||||
virtualFolder.ShouldBeADirectory(fileSystem)
|
||||
.WithInfo(testValue, testValue, testValue, FileAttributes.Hidden | FileAttributes.Directory, ignoreRecallAttributes: true);
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileRunnersAndFolders), nameof(FileRunnersAndFolders.Runners))]
|
||||
public void CannotWriteToReadOnlyFile(FileSystemRunner fileSystem, string parentFolder)
|
||||
{
|
||||
string filename = Path.Combine(parentFolder, "CannotWriteToReadOnlyFile");
|
||||
string virtualFilePath = this.Enlistment.GetVirtualPathTo(filename);
|
||||
virtualFilePath.ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
// Write initial contents
|
||||
string originalContents = "Contents of ReadOnly file";
|
||||
fileSystem.WriteAllText(virtualFilePath, originalContents);
|
||||
virtualFilePath.ShouldBeAFile(fileSystem).WithContents(originalContents);
|
||||
|
||||
// Make file read only
|
||||
FileInfo fileInfo = new FileInfo(virtualFilePath);
|
||||
fileInfo.Attributes = FileAttributes.ReadOnly;
|
||||
|
||||
// Verify that file cannot be written to
|
||||
string newContents = "New contents for file";
|
||||
fileSystem.WriteAllTextShouldFail<UnauthorizedAccessException>(virtualFilePath, newContents);
|
||||
virtualFilePath.ShouldBeAFile(fileSystem).WithContents(originalContents);
|
||||
|
||||
// Cleanup
|
||||
fileInfo.Attributes = FileAttributes.Normal;
|
||||
fileSystem.DeleteFile(virtualFilePath);
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, filename, parentFolder);
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileRunnersAndFolders), nameof(FileRunnersAndFolders.Runners))]
|
||||
public void ReadonlyCanBeSetAndUnset(FileSystemRunner fileSystem, string parentFolder)
|
||||
{
|
||||
string filename = Path.Combine(parentFolder, "ReadonlyCanBeSetAndUnset");
|
||||
string virtualFilePath = this.Enlistment.GetVirtualPathTo(filename);
|
||||
virtualFilePath.ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
string originalContents = "Contents of ReadOnly file";
|
||||
fileSystem.WriteAllText(virtualFilePath, originalContents);
|
||||
|
||||
// Make file read only
|
||||
FileInfo fileInfo = new FileInfo(virtualFilePath);
|
||||
fileInfo.Attributes = FileAttributes.ReadOnly;
|
||||
virtualFilePath.ShouldBeAFile(fileSystem).WithAttribute(FileAttributes.ReadOnly);
|
||||
|
||||
// Clear read only
|
||||
fileInfo.Attributes = FileAttributes.Normal;
|
||||
virtualFilePath.ShouldBeAFile(fileSystem).WithoutAttribute(FileAttributes.ReadOnly);
|
||||
|
||||
// Cleanup
|
||||
fileSystem.DeleteFile(virtualFilePath);
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, filename, parentFolder);
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileRunnersAndFolders), nameof(FileRunnersAndFolders.Runners))]
|
||||
public void ChangeVirtualNTFSFileNameCase(FileSystemRunner fileSystem, string parentFolder)
|
||||
{
|
||||
string oldFilename = Path.Combine(parentFolder, "ChangePhysicalFileNameCase.txt");
|
||||
string newFilename = Path.Combine(parentFolder, "changephysicalfilenamecase.txt");
|
||||
string fileContents = "Hello World";
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, oldFilename, parentFolder);
|
||||
|
||||
fileSystem.WriteAllText(this.Enlistment.GetVirtualPathTo(oldFilename), fileContents);
|
||||
this.Enlistment.GetVirtualPathTo(oldFilename).ShouldBeAFile(fileSystem).WithContents(fileContents);
|
||||
this.Enlistment.GetVirtualPathTo(oldFilename).ShouldBeAFile(fileSystem).WithCaseMatchingName(Path.GetFileName(oldFilename));
|
||||
|
||||
fileSystem.MoveFile(this.Enlistment.GetVirtualPathTo(oldFilename), this.Enlistment.GetVirtualPathTo(newFilename));
|
||||
this.Enlistment.GetVirtualPathTo(newFilename).ShouldBeAFile(fileSystem).WithContents(fileContents);
|
||||
this.Enlistment.GetVirtualPathTo(newFilename).ShouldBeAFile(fileSystem).WithCaseMatchingName(Path.GetFileName(newFilename));
|
||||
|
||||
fileSystem.DeleteFile(this.Enlistment.GetVirtualPathTo(newFilename));
|
||||
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, newFilename, parentFolder);
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileRunnersAndFolders), nameof(FileRunnersAndFolders.Runners))]
|
||||
public void ChangeVirtualNTFSFileName(FileSystemRunner fileSystem, string parentFolder)
|
||||
{
|
||||
string oldFilename = Path.Combine(parentFolder, "ChangePhysicalFileName.txt");
|
||||
string newFilename = Path.Combine(parentFolder, "NewFileName.txt");
|
||||
string fileContents = "Hello World";
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, oldFilename, parentFolder);
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, newFilename, parentFolder);
|
||||
|
||||
fileSystem.WriteAllText(this.Enlistment.GetVirtualPathTo(oldFilename), fileContents);
|
||||
this.Enlistment.GetVirtualPathTo(oldFilename).ShouldBeAFile(fileSystem).WithContents(fileContents);
|
||||
this.Enlistment.GetVirtualPathTo(newFilename).ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
fileSystem.MoveFile(this.Enlistment.GetVirtualPathTo(oldFilename), this.Enlistment.GetVirtualPathTo(newFilename));
|
||||
this.Enlistment.GetVirtualPathTo(newFilename).ShouldBeAFile(fileSystem).WithContents(fileContents);
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, oldFilename, parentFolder);
|
||||
|
||||
fileSystem.DeleteFile(this.Enlistment.GetVirtualPathTo(newFilename));
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, newFilename, parentFolder);
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileRunnersAndFolders), nameof(FileRunnersAndFolders.Runners))]
|
||||
public void MoveVirtualNTFSFileToVirtualNTFSFolder(FileSystemRunner fileSystem, string parentFolder)
|
||||
{
|
||||
string testFolderName = Path.Combine(parentFolder, "test_folder");
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, testFolderName, parentFolder);
|
||||
|
||||
fileSystem.CreateDirectory(this.Enlistment.GetVirtualPathTo(testFolderName));
|
||||
this.Enlistment.GetVirtualPathTo(testFolderName).ShouldBeADirectory(fileSystem);
|
||||
|
||||
string testFileName = Path.Combine(parentFolder, "test.txt");
|
||||
string testFileContents = "This is the contents of a test file";
|
||||
fileSystem.WriteAllText(this.Enlistment.GetVirtualPathTo(testFileName), testFileContents);
|
||||
this.Enlistment.GetVirtualPathTo(testFileName).ShouldBeAFile(fileSystem).WithContents(testFileContents);
|
||||
|
||||
string newTestFileVirtualPath = Path.Combine(
|
||||
this.Enlistment.GetVirtualPathTo(testFolderName),
|
||||
Path.GetFileName(testFileName));
|
||||
|
||||
fileSystem.MoveFile(this.Enlistment.GetVirtualPathTo(testFileName), newTestFileVirtualPath);
|
||||
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, testFileName, parentFolder);
|
||||
newTestFileVirtualPath.ShouldBeAFile(fileSystem).WithContents(testFileContents);
|
||||
|
||||
fileSystem.DeleteFile(newTestFileVirtualPath);
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, Path.Combine(testFolderName, Path.GetFileName(testFileName)), parentFolder);
|
||||
|
||||
fileSystem.DeleteDirectory(this.Enlistment.GetVirtualPathTo(testFolderName));
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, testFolderName, parentFolder);
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileSystemRunner), nameof(FileSystemRunner.Runners))]
|
||||
public void MoveWorkingDirectoryFileToDotGitFolder(FileSystemRunner fileSystem)
|
||||
{
|
||||
string testFolderName = ".git";
|
||||
this.Enlistment.GetVirtualPathTo(testFolderName).ShouldBeADirectory(fileSystem);
|
||||
|
||||
string testFileName = "test.txt";
|
||||
this.Enlistment.GetVirtualPathTo(testFileName).ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
string testFileContents = "This is the contents of a test file";
|
||||
fileSystem.WriteAllText(this.Enlistment.GetVirtualPathTo(testFileName), testFileContents);
|
||||
this.Enlistment.GetVirtualPathTo(testFileName).ShouldBeAFile(fileSystem).WithContents(testFileContents);
|
||||
|
||||
string newTestFileVirtualPath = Path.Combine(this.Enlistment.GetVirtualPathTo(testFolderName), testFileName);
|
||||
fileSystem.MoveFile(this.Enlistment.GetVirtualPathTo(testFileName), newTestFileVirtualPath);
|
||||
this.Enlistment.GetVirtualPathTo(testFileName).ShouldNotExistOnDisk(fileSystem);
|
||||
newTestFileVirtualPath.ShouldBeAFile(fileSystem).WithContents(testFileContents);
|
||||
|
||||
fileSystem.DeleteFile(newTestFileVirtualPath);
|
||||
newTestFileVirtualPath.ShouldNotExistOnDisk(fileSystem);
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileSystemRunner), nameof(FileSystemRunner.Runners))]
|
||||
public void MoveDotGitFileToWorkingDirectoryFolder(FileSystemRunner fileSystem)
|
||||
{
|
||||
string testFolderName = "test_folder";
|
||||
this.Enlistment.GetVirtualPathTo(testFolderName).ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
fileSystem.CreateDirectory(this.Enlistment.GetVirtualPathTo(testFolderName));
|
||||
this.Enlistment.GetVirtualPathTo(testFolderName).ShouldBeADirectory(fileSystem);
|
||||
|
||||
string sourceFileFolder = ".git";
|
||||
string testFileName = "config";
|
||||
string sourceFileVirtualPath = Path.Combine(this.Enlistment.GetVirtualPathTo(sourceFileFolder), testFileName);
|
||||
string testFileContents = sourceFileVirtualPath.ShouldBeAFile(fileSystem).WithContents();
|
||||
|
||||
string targetTestFileVirtualPath = Path.Combine(this.Enlistment.GetVirtualPathTo(testFolderName), testFileName);
|
||||
|
||||
fileSystem.MoveFile(sourceFileVirtualPath, targetTestFileVirtualPath);
|
||||
sourceFileVirtualPath.ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
targetTestFileVirtualPath.ShouldBeAFile(fileSystem).WithContents(testFileContents);
|
||||
|
||||
fileSystem.MoveFile(targetTestFileVirtualPath, sourceFileVirtualPath);
|
||||
sourceFileVirtualPath.ShouldBeAFile(fileSystem).WithContents(testFileContents);
|
||||
targetTestFileVirtualPath.ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
fileSystem.DeleteDirectory(this.Enlistment.GetVirtualPathTo(testFolderName));
|
||||
this.Enlistment.GetVirtualPathTo(testFolderName).ShouldNotExistOnDisk(fileSystem);
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileRunnersAndFolders), nameof(FileRunnersAndFolders.Runners))]
|
||||
public void MoveVirtualNTFSFileToOverwriteVirtualNTFSFile(FileSystemRunner fileSystem, string parentFolder)
|
||||
{
|
||||
string targetFilename = Path.Combine(parentFolder, "TargetFile.txt");
|
||||
string sourceFilename = Path.Combine(parentFolder, "SourceFile.txt");
|
||||
string targetFileContents = "The Target";
|
||||
string sourceFileContents = "The Source";
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, targetFilename, parentFolder);
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, sourceFilename, parentFolder);
|
||||
|
||||
fileSystem.WriteAllText(this.Enlistment.GetVirtualPathTo(targetFilename), targetFileContents);
|
||||
this.Enlistment.GetVirtualPathTo(targetFilename).ShouldBeAFile(fileSystem).WithContents(targetFileContents);
|
||||
|
||||
fileSystem.WriteAllText(this.Enlistment.GetVirtualPathTo(sourceFilename), sourceFileContents);
|
||||
this.Enlistment.GetVirtualPathTo(sourceFilename).ShouldBeAFile(fileSystem).WithContents(sourceFileContents);
|
||||
|
||||
fileSystem.ReplaceFile(this.Enlistment.GetVirtualPathTo(sourceFilename), this.Enlistment.GetVirtualPathTo(targetFilename));
|
||||
|
||||
this.Enlistment.GetVirtualPathTo(targetFilename).ShouldBeAFile(fileSystem).WithContents(sourceFileContents);
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, sourceFilename, parentFolder);
|
||||
|
||||
fileSystem.DeleteFile(this.Enlistment.GetVirtualPathTo(targetFilename));
|
||||
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, targetFilename, parentFolder);
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileRunnersAndFolders), nameof(FileRunnersAndFolders.Runners))]
|
||||
public void MoveVirtualNTFSFileToInvalidFolder(FileSystemRunner fileSystem, string parentFolder)
|
||||
{
|
||||
string testFolderName = Path.Combine(parentFolder, "test_folder");
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, testFolderName, parentFolder);
|
||||
|
||||
string testFileName = Path.Combine(parentFolder, "test.txt");
|
||||
string testFileContents = "This is the contents of a test file";
|
||||
fileSystem.WriteAllText(this.Enlistment.GetVirtualPathTo(testFileName), testFileContents);
|
||||
this.Enlistment.GetVirtualPathTo(testFileName).ShouldBeAFile(fileSystem).WithContents(testFileContents);
|
||||
|
||||
string newTestFileVirtualPath = Path.Combine(
|
||||
this.Enlistment.GetVirtualPathTo(testFolderName),
|
||||
Path.GetFileName(testFileName));
|
||||
|
||||
fileSystem.MoveFileShouldFail(this.Enlistment.GetVirtualPathTo(testFileName), newTestFileVirtualPath);
|
||||
newTestFileVirtualPath.ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
this.Enlistment.GetVirtualPathTo(testFileName).ShouldBeAFile(fileSystem).WithContents(testFileContents);
|
||||
|
||||
fileSystem.DeleteFile(this.Enlistment.GetVirtualPathTo(testFileName));
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, testFileName, parentFolder);
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileRunnersAndFolders), nameof(FileRunnersAndFolders.Runners))]
|
||||
public void DeletedFilesCanBeImmediatelyRecreated(FileSystemRunner fileSystem, string parentFolder)
|
||||
{
|
||||
string filename = Path.Combine(parentFolder, "DeletedFilesCanBeImmediatelyRecreated");
|
||||
string filePath = this.Enlistment.GetVirtualPathTo(filename);
|
||||
filePath.ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
string testData = "Some test data";
|
||||
|
||||
fileSystem.WriteAllText(filePath, testData);
|
||||
|
||||
fileSystem.DeleteFile(filePath);
|
||||
|
||||
// Do not check for delete. Doing so removes a race between deleting and writing.
|
||||
// This write will throw if the problem exists.
|
||||
fileSystem.WriteAllText(filePath, testData);
|
||||
|
||||
filePath.ShouldBeAFile(fileSystem).WithContents().ShouldEqual(testData);
|
||||
fileSystem.DeleteFile(filePath);
|
||||
}
|
||||
|
||||
// WindowsOnly due to differences between POSIX and Windows delete
|
||||
[TestCaseSource(typeof(FileRunnersAndFolders), nameof(FileRunnersAndFolders.CanDeleteFilesWhileTheyAreOpenRunners))]
|
||||
[Category(Categories.WindowsOnly)]
|
||||
public void CanDeleteFilesWhileTheyAreOpen(FileSystemRunner fileSystem, string parentFolder)
|
||||
{
|
||||
string filename = Path.Combine(parentFolder, "CanDeleteFilesWhileTheyAreOpen");
|
||||
string filePath = this.Enlistment.GetVirtualPathTo(filename);
|
||||
|
||||
byte[] buffer = System.Text.Encoding.ASCII.GetBytes("Some test data for writing");
|
||||
|
||||
using (FileStream deletableWriteStream = File.Open(filePath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete))
|
||||
{
|
||||
deletableWriteStream.Write(buffer, 0, buffer.Length);
|
||||
deletableWriteStream.Flush();
|
||||
|
||||
using (FileStream deletableReadStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete))
|
||||
{
|
||||
byte[] readBuffer = new byte[buffer.Length];
|
||||
|
||||
deletableReadStream.Read(readBuffer, 0, readBuffer.Length).ShouldEqual(readBuffer.Length);
|
||||
readBuffer.ShouldMatchInOrder(buffer);
|
||||
|
||||
fileSystem.DeleteFile(filePath);
|
||||
filePath.ShouldBeAFile(fileSystem);
|
||||
|
||||
deletableWriteStream.Write(buffer, 0, buffer.Length);
|
||||
deletableWriteStream.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
filePath.ShouldNotExistOnDisk(fileSystem);
|
||||
}
|
||||
|
||||
// WindowsOnly due to differences between POSIX and Windows delete
|
||||
[TestCase]
|
||||
[Category(Categories.WindowsOnly)]
|
||||
public void CanDeleteHydratedFilesWhileTheyAreOpenForWrite()
|
||||
{
|
||||
FileSystemRunner fileSystem = FileSystemRunner.DefaultRunner;
|
||||
string fileName = "GVFS.sln";
|
||||
string virtualPath = this.Enlistment.GetVirtualPathTo(fileName);
|
||||
|
||||
virtualPath.ShouldBeAFile(fileSystem);
|
||||
|
||||
using (Stream stream = new FileStream(virtualPath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete))
|
||||
using (StreamReader reader = new StreamReader(stream))
|
||||
{
|
||||
// First line is empty, so read two lines
|
||||
string line = reader.ReadLine() + reader.ReadLine();
|
||||
line.Length.ShouldNotEqual(0);
|
||||
|
||||
File.Delete(virtualPath);
|
||||
|
||||
// Open deleted files should still exist
|
||||
virtualPath.ShouldBeAFile(fileSystem);
|
||||
|
||||
using (StreamWriter writer = new StreamWriter(stream))
|
||||
{
|
||||
writer.WriteLine("newline!");
|
||||
writer.Flush();
|
||||
|
||||
virtualPath.ShouldBeAFile(fileSystem);
|
||||
}
|
||||
}
|
||||
|
||||
virtualPath.ShouldNotExistOnDisk(fileSystem);
|
||||
}
|
||||
|
||||
// WindowsOnly because file timestamps on Mac are set to the time at which
|
||||
// placeholders are written
|
||||
[TestCase]
|
||||
[Category(Categories.WindowsOnly)]
|
||||
public void ProjectedBlobFileTimesMatchHead()
|
||||
{
|
||||
// TODO: 467539 - Update all runners to support getting create/modify/access times
|
||||
FileSystemRunner fileSystem = FileSystemRunner.DefaultRunner;
|
||||
|
||||
string filename = "AuthoringTests.md";
|
||||
string headFileName = Path.Combine(".git", "logs", "HEAD");
|
||||
this.Enlistment.GetVirtualPathTo(headFileName).ShouldBeAFile(fileSystem);
|
||||
|
||||
FileInfo headFileInfo = new FileInfo(this.Enlistment.GetVirtualPathTo(headFileName));
|
||||
FileInfo fileInfo = new FileInfo(this.Enlistment.GetVirtualPathTo(filename));
|
||||
|
||||
fileInfo.CreationTime.ShouldEqual(headFileInfo.CreationTime);
|
||||
|
||||
// Last access and last write can get set outside the test, make sure that are at least
|
||||
// as recent as the creation time on the HEAD file, and no later than now
|
||||
fileInfo.LastAccessTime.ShouldBeAtLeast(headFileInfo.CreationTime);
|
||||
fileInfo.LastWriteTime.ShouldBeAtLeast(headFileInfo.CreationTime);
|
||||
fileInfo.LastAccessTime.ShouldBeAtMost(DateTime.Now);
|
||||
fileInfo.LastWriteTime.ShouldBeAtMost(DateTime.Now);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
[Category(Categories.WindowsOnly)]
|
||||
public void ProjectedBlobFolderTimesMatchHead()
|
||||
{
|
||||
// TODO: 467539 - Update all runners to support getting create/modify/access times
|
||||
FileSystemRunner fileSystem = FileSystemRunner.DefaultRunner;
|
||||
|
||||
string folderName = Path.Combine("GVFS", "GVFS.Tests");
|
||||
string headFileName = Path.Combine(".git", "logs", "HEAD");
|
||||
this.Enlistment.GetVirtualPathTo(headFileName).ShouldBeAFile(fileSystem);
|
||||
|
||||
FileInfo headFileInfo = new FileInfo(this.Enlistment.GetVirtualPathTo(headFileName));
|
||||
DirectoryInfo folderInfo = new DirectoryInfo(this.Enlistment.GetVirtualPathTo(folderName));
|
||||
|
||||
folderInfo.CreationTime.ShouldEqual(headFileInfo.CreationTime);
|
||||
|
||||
// Last access and last write can get set outside the test, make sure that are at least
|
||||
// as recent as the creation time on the HEAD file, and no later than now
|
||||
folderInfo.LastAccessTime.ShouldBeAtLeast(headFileInfo.CreationTime);
|
||||
folderInfo.LastWriteTime.ShouldBeAtLeast(headFileInfo.CreationTime);
|
||||
folderInfo.LastAccessTime.ShouldBeAtMost(DateTime.Now);
|
||||
folderInfo.LastWriteTime.ShouldBeAtMost(DateTime.Now);
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileRunnersAndFolders), nameof(FileRunnersAndFolders.Runners))]
|
||||
public void NonExistentItemBehaviorIsCorrect(FileSystemRunner fileSystem, string parentFolder)
|
||||
{
|
||||
string nonExistentItem = Path.Combine(parentFolder, "BadFolderName");
|
||||
string nonExistentItem2 = Path.Combine(parentFolder, "BadFolderName2");
|
||||
|
||||
string virtualPathToNonExistentItem = this.Enlistment.GetVirtualPathTo(nonExistentItem).ShouldNotExistOnDisk(fileSystem);
|
||||
string virtualPathToNonExistentItem2 = this.Enlistment.GetVirtualPathTo(nonExistentItem2).ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
fileSystem.MoveFile_FileShouldNotBeFound(virtualPathToNonExistentItem, virtualPathToNonExistentItem2);
|
||||
fileSystem.DeleteFile_FileShouldNotBeFound(virtualPathToNonExistentItem);
|
||||
fileSystem.ReplaceFile_FileShouldNotBeFound(virtualPathToNonExistentItem, virtualPathToNonExistentItem2);
|
||||
fileSystem.ReadAllText_FileShouldNotBeFound(virtualPathToNonExistentItem);
|
||||
|
||||
// TODO #457434
|
||||
// fileSystem.MoveDirectoryShouldNotBeFound(nonExistentItem, true)
|
||||
fileSystem.DeleteDirectory_DirectoryShouldNotBeFound(virtualPathToNonExistentItem);
|
||||
|
||||
// TODO #457434
|
||||
// fileSystem.ReplaceDirectoryShouldNotBeFound(nonExistentItem, true)
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileRunnersAndFolders), nameof(FileRunnersAndFolders.Runners))]
|
||||
public void RenameEmptyVirtualNTFSFolder(FileSystemRunner fileSystem, string parentFolder)
|
||||
{
|
||||
string testFolderName = Path.Combine(parentFolder, "test_folder");
|
||||
string testFolderVirtualPath = this.Enlistment.GetVirtualPathTo(testFolderName);
|
||||
testFolderVirtualPath.ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
fileSystem.CreateDirectory(testFolderVirtualPath);
|
||||
testFolderVirtualPath.ShouldBeADirectory(fileSystem);
|
||||
|
||||
string newFolderName = Path.Combine(parentFolder, "test_folder_renamed");
|
||||
string newFolderVirtualPath = this.Enlistment.GetVirtualPathTo(newFolderName);
|
||||
newFolderVirtualPath.ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
fileSystem.MoveDirectory(testFolderVirtualPath, newFolderVirtualPath);
|
||||
testFolderVirtualPath.ShouldNotExistOnDisk(fileSystem);
|
||||
newFolderVirtualPath.ShouldBeADirectory(fileSystem);
|
||||
|
||||
fileSystem.DeleteDirectory(newFolderVirtualPath);
|
||||
newFolderVirtualPath.ShouldNotExistOnDisk(fileSystem);
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileRunnersAndFolders), nameof(FileRunnersAndFolders.Runners))]
|
||||
public void MoveVirtualNTFSFolderIntoVirtualNTFSFolder(FileSystemRunner fileSystem, string parentFolder)
|
||||
{
|
||||
string testFolderName = Path.Combine(parentFolder, "test_folder");
|
||||
string testFolderVirtualPath = this.Enlistment.GetVirtualPathTo(testFolderName);
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, testFolderName, parentFolder);
|
||||
|
||||
fileSystem.CreateDirectory(testFolderVirtualPath);
|
||||
testFolderVirtualPath.ShouldBeADirectory(fileSystem);
|
||||
|
||||
string targetFolderName = Path.Combine(parentFolder, "target_folder");
|
||||
string targetFolderVirtualPath = this.Enlistment.GetVirtualPathTo(targetFolderName);
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, targetFolderName, parentFolder);
|
||||
|
||||
fileSystem.CreateDirectory(targetFolderVirtualPath);
|
||||
targetFolderVirtualPath.ShouldBeADirectory(fileSystem);
|
||||
|
||||
string testFileName = Path.Combine(testFolderName, "test.txt");
|
||||
string testFileContents = "This is the contents of a test file";
|
||||
fileSystem.WriteAllText(this.Enlistment.GetVirtualPathTo(testFileName), testFileContents);
|
||||
this.Enlistment.GetVirtualPathTo(testFileName).ShouldBeAFile(fileSystem).WithContents(testFileContents);
|
||||
|
||||
string newTestFolder = Path.Combine(targetFolderName, Path.GetFileName(testFolderName));
|
||||
string newFolderVirtualPath = this.Enlistment.GetVirtualPathTo(newTestFolder);
|
||||
|
||||
fileSystem.MoveDirectory(testFolderVirtualPath, newFolderVirtualPath);
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, testFolderName, parentFolder);
|
||||
newFolderVirtualPath.ShouldBeADirectory(fileSystem);
|
||||
|
||||
string newTestFileName = Path.Combine(newTestFolder, Path.GetFileName(testFileName));
|
||||
this.Enlistment.GetVirtualPathTo(newTestFileName).ShouldBeAFile(fileSystem).WithContents(testFileContents);
|
||||
|
||||
fileSystem.DeleteDirectory(targetFolderVirtualPath);
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, targetFolderName, parentFolder);
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileRunnersAndFolders), nameof(FileRunnersAndFolders.Runners))]
|
||||
public void RenameAndMoveVirtualNTFSFolderIntoVirtualNTFSFolder(FileSystemRunner fileSystem, string parentFolder)
|
||||
{
|
||||
string testFolderName = Path.Combine(parentFolder, "test_folder");
|
||||
string testFolderVirtualPath = this.Enlistment.GetVirtualPathTo(testFolderName);
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, testFolderName, parentFolder);
|
||||
|
||||
fileSystem.CreateDirectory(testFolderVirtualPath);
|
||||
testFolderVirtualPath.ShouldBeADirectory(fileSystem);
|
||||
|
||||
string targetFolderName = Path.Combine(parentFolder, "target_folder");
|
||||
string targetFolderVirtualPath = this.Enlistment.GetVirtualPathTo(targetFolderName);
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, targetFolderName, parentFolder);
|
||||
|
||||
fileSystem.CreateDirectory(targetFolderVirtualPath);
|
||||
targetFolderVirtualPath.ShouldBeADirectory(fileSystem);
|
||||
|
||||
string testFileName = "test.txt";
|
||||
string testFilePartialPath = Path.Combine(testFolderName, testFileName);
|
||||
string testFileContents = "This is the contents of a test file";
|
||||
fileSystem.WriteAllText(this.Enlistment.GetVirtualPathTo(testFilePartialPath), testFileContents);
|
||||
this.Enlistment.GetVirtualPathTo(testFilePartialPath).ShouldBeAFile(fileSystem).WithContents(testFileContents);
|
||||
|
||||
string newTestFolder = Path.Combine(targetFolderName, "test_folder_renamed");
|
||||
string newFolderVirtualPath = this.Enlistment.GetVirtualPathTo(newTestFolder);
|
||||
|
||||
fileSystem.MoveDirectory(testFolderVirtualPath, newFolderVirtualPath);
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, testFolderName, parentFolder);
|
||||
newFolderVirtualPath.ShouldBeADirectory(fileSystem);
|
||||
|
||||
string newTestFileName = Path.Combine(newTestFolder, testFileName);
|
||||
this.Enlistment.GetVirtualPathTo(newTestFileName).ShouldBeAFile(fileSystem).WithContents(testFileContents);
|
||||
|
||||
fileSystem.DeleteDirectory(targetFolderVirtualPath);
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, targetFolderName, parentFolder);
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileSystemRunner), nameof(FileSystemRunner.Runners))]
|
||||
public void MoveVirtualNTFSFolderTreeIntoVirtualNTFSFolder(FileSystemRunner fileSystem)
|
||||
{
|
||||
string testFolderParent = "test_folder_parent";
|
||||
string testFolderChild = "test_folder_child";
|
||||
string testFolderGrandChild = "test_folder_grandchild";
|
||||
string testFile = "test.txt";
|
||||
this.Enlistment.GetVirtualPathTo(testFolderParent).ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
// Create the folder tree (to move)
|
||||
fileSystem.CreateDirectory(this.Enlistment.GetVirtualPathTo(testFolderParent));
|
||||
this.Enlistment.GetVirtualPathTo(testFolderParent).ShouldBeADirectory(fileSystem);
|
||||
|
||||
string realtiveChildFolderPath = Path.Combine(testFolderParent, testFolderChild);
|
||||
fileSystem.CreateDirectory(this.Enlistment.GetVirtualPathTo(realtiveChildFolderPath));
|
||||
this.Enlistment.GetVirtualPathTo(realtiveChildFolderPath).ShouldBeADirectory(fileSystem);
|
||||
|
||||
string realtiveGrandChildFolderPath = Path.Combine(realtiveChildFolderPath, testFolderGrandChild);
|
||||
fileSystem.CreateDirectory(this.Enlistment.GetVirtualPathTo(realtiveGrandChildFolderPath));
|
||||
this.Enlistment.GetVirtualPathTo(realtiveGrandChildFolderPath).ShouldBeADirectory(fileSystem);
|
||||
|
||||
string relativeTestFilePath = Path.Combine(realtiveGrandChildFolderPath, testFile);
|
||||
string testFileContents = "This is the contents of a test file";
|
||||
fileSystem.WriteAllText(this.Enlistment.GetVirtualPathTo(relativeTestFilePath), testFileContents);
|
||||
this.Enlistment.GetVirtualPathTo(relativeTestFilePath).ShouldBeAFile(fileSystem).WithContents(testFileContents);
|
||||
|
||||
// Create the target
|
||||
string targetFolder = "target_folder";
|
||||
this.Enlistment.GetVirtualPathTo(targetFolder).ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
fileSystem.CreateDirectory(this.Enlistment.GetVirtualPathTo(targetFolder));
|
||||
this.Enlistment.GetVirtualPathTo(targetFolder).ShouldBeADirectory(fileSystem);
|
||||
|
||||
fileSystem.MoveDirectory(
|
||||
this.Enlistment.GetVirtualPathTo(testFolderParent),
|
||||
this.Enlistment.GetVirtualPathTo(Path.Combine(targetFolder, testFolderParent)));
|
||||
|
||||
// The old tree structure should be gone
|
||||
this.Enlistment.GetVirtualPathTo(testFolderParent).ShouldNotExistOnDisk(fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(realtiveChildFolderPath).ShouldNotExistOnDisk(fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(realtiveGrandChildFolderPath).ShouldNotExistOnDisk(fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(relativeTestFilePath).ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
// The tree should have been moved under the target folder
|
||||
testFolderParent = Path.Combine(targetFolder, testFolderParent);
|
||||
realtiveChildFolderPath = Path.Combine(testFolderParent, testFolderChild);
|
||||
realtiveGrandChildFolderPath = Path.Combine(realtiveChildFolderPath, testFolderGrandChild);
|
||||
relativeTestFilePath = Path.Combine(realtiveGrandChildFolderPath, testFile);
|
||||
|
||||
this.Enlistment.GetVirtualPathTo(testFolderParent).ShouldBeADirectory(fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(realtiveChildFolderPath).ShouldBeADirectory(fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(realtiveGrandChildFolderPath).ShouldBeADirectory(fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(relativeTestFilePath).ShouldBeAFile(fileSystem).WithContents(testFileContents);
|
||||
|
||||
// Cleanup
|
||||
fileSystem.DeleteDirectory(this.Enlistment.GetVirtualPathTo(targetFolder));
|
||||
|
||||
this.Enlistment.GetVirtualPathTo(targetFolder).ShouldNotExistOnDisk(fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(testFolderParent).ShouldNotExistOnDisk(fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(realtiveChildFolderPath).ShouldNotExistOnDisk(fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(realtiveGrandChildFolderPath).ShouldNotExistOnDisk(fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(relativeTestFilePath).ShouldNotExistOnDisk(fileSystem);
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileSystemRunner), nameof(FileSystemRunner.Runners))]
|
||||
public void MoveDotGitFullFolderTreeToDotGitFullFolder(FileSystemRunner fileSystem)
|
||||
{
|
||||
string testFolderRoot = ".git";
|
||||
string testFolderParent = "test_folder_parent";
|
||||
string testFolderChild = "test_folder_child";
|
||||
string testFolderGrandChild = "test_folder_grandchild";
|
||||
string testFile = "test.txt";
|
||||
this.Enlistment.GetVirtualPathTo(Path.Combine(testFolderRoot, testFolderParent)).ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
// Create the folder tree (to move)
|
||||
fileSystem.CreateDirectory(this.Enlistment.GetVirtualPathTo(Path.Combine(testFolderRoot, testFolderParent)));
|
||||
this.Enlistment.GetVirtualPathTo(Path.Combine(testFolderRoot, testFolderParent)).ShouldBeADirectory(fileSystem);
|
||||
|
||||
string realtiveChildFolderPath = Path.Combine(testFolderRoot, testFolderParent, testFolderChild);
|
||||
fileSystem.CreateDirectory(this.Enlistment.GetVirtualPathTo(realtiveChildFolderPath));
|
||||
this.Enlistment.GetVirtualPathTo(realtiveChildFolderPath).ShouldBeADirectory(fileSystem);
|
||||
|
||||
string realtiveGrandChildFolderPath = Path.Combine(realtiveChildFolderPath, testFolderGrandChild);
|
||||
fileSystem.CreateDirectory(this.Enlistment.GetVirtualPathTo(realtiveGrandChildFolderPath));
|
||||
this.Enlistment.GetVirtualPathTo(realtiveGrandChildFolderPath).ShouldBeADirectory(fileSystem);
|
||||
|
||||
string relativeTestFilePath = Path.Combine(realtiveGrandChildFolderPath, testFile);
|
||||
string testFileContents = "This is the contents of a test file";
|
||||
fileSystem.WriteAllText(this.Enlistment.GetVirtualPathTo(relativeTestFilePath), testFileContents);
|
||||
this.Enlistment.GetVirtualPathTo(relativeTestFilePath).ShouldBeAFile(fileSystem).WithContents(testFileContents);
|
||||
|
||||
// Create the target
|
||||
string targetFolder = Path.Combine(".git", "target_folder");
|
||||
this.Enlistment.GetVirtualPathTo(targetFolder).ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
fileSystem.CreateDirectory(this.Enlistment.GetVirtualPathTo(targetFolder));
|
||||
this.Enlistment.GetVirtualPathTo(targetFolder).ShouldBeADirectory(fileSystem);
|
||||
|
||||
fileSystem.MoveDirectory(
|
||||
this.Enlistment.GetVirtualPathTo(Path.Combine(testFolderRoot, testFolderParent)),
|
||||
this.Enlistment.GetVirtualPathTo(Path.Combine(targetFolder, testFolderParent)));
|
||||
|
||||
// The old tree structure should be gone
|
||||
this.Enlistment.GetVirtualPathTo(Path.Combine(testFolderRoot, testFolderParent)).ShouldNotExistOnDisk(fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(realtiveChildFolderPath).ShouldNotExistOnDisk(fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(realtiveGrandChildFolderPath).ShouldNotExistOnDisk(fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(relativeTestFilePath).ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
// The tree should have been moved under the target folder
|
||||
testFolderParent = Path.Combine(targetFolder, testFolderParent);
|
||||
realtiveChildFolderPath = Path.Combine(testFolderParent, testFolderChild);
|
||||
realtiveGrandChildFolderPath = Path.Combine(realtiveChildFolderPath, testFolderGrandChild);
|
||||
relativeTestFilePath = Path.Combine(realtiveGrandChildFolderPath, testFile);
|
||||
|
||||
this.Enlistment.GetVirtualPathTo(testFolderParent).ShouldBeADirectory(fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(realtiveChildFolderPath).ShouldBeADirectory(fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(realtiveGrandChildFolderPath).ShouldBeADirectory(fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(relativeTestFilePath).ShouldBeAFile(fileSystem).WithContents(testFileContents);
|
||||
|
||||
// Cleanup
|
||||
fileSystem.DeleteDirectory(this.Enlistment.GetVirtualPathTo(targetFolder));
|
||||
|
||||
this.Enlistment.GetVirtualPathTo(targetFolder).ShouldNotExistOnDisk(fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(testFolderParent).ShouldNotExistOnDisk(fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(realtiveChildFolderPath).ShouldNotExistOnDisk(fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(realtiveGrandChildFolderPath).ShouldNotExistOnDisk(fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(relativeTestFilePath).ShouldNotExistOnDisk(fileSystem);
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileSystemRunner), nameof(FileSystemRunner.Runners))]
|
||||
public void DeleteIndexFileFails(FileSystemRunner fileSystem)
|
||||
{
|
||||
string indexFilePath = this.Enlistment.GetVirtualPathTo(Path.Combine(".git", "index"));
|
||||
indexFilePath.ShouldBeAFile(fileSystem);
|
||||
fileSystem.DeleteFile_AccessShouldBeDenied(indexFilePath);
|
||||
indexFilePath.ShouldBeAFile(fileSystem);
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileRunnersAndFolders), nameof(FileRunnersAndFolders.Runners))]
|
||||
public void MoveVirtualNTFSFolderIntoInvalidFolder(FileSystemRunner fileSystem, string parentFolder)
|
||||
{
|
||||
string testFolderParent = Path.Combine(parentFolder, "test_folder_parent");
|
||||
string testFolderChild = "test_folder_child";
|
||||
string testFolderGrandChild = "test_folder_grandchild";
|
||||
string testFile = "test.txt";
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, testFolderParent, parentFolder);
|
||||
|
||||
// Create the folder tree (to move)
|
||||
fileSystem.CreateDirectory(this.Enlistment.GetVirtualPathTo(testFolderParent));
|
||||
this.Enlistment.GetVirtualPathTo(testFolderParent).ShouldBeADirectory(fileSystem);
|
||||
|
||||
string realtiveChildFolderPath = Path.Combine(testFolderParent, testFolderChild);
|
||||
fileSystem.CreateDirectory(this.Enlistment.GetVirtualPathTo(realtiveChildFolderPath));
|
||||
this.Enlistment.GetVirtualPathTo(realtiveChildFolderPath).ShouldBeADirectory(fileSystem);
|
||||
|
||||
string realtiveGrandChildFolderPath = Path.Combine(realtiveChildFolderPath, testFolderGrandChild);
|
||||
fileSystem.CreateDirectory(this.Enlistment.GetVirtualPathTo(realtiveGrandChildFolderPath));
|
||||
this.Enlistment.GetVirtualPathTo(realtiveGrandChildFolderPath).ShouldBeADirectory(fileSystem);
|
||||
|
||||
string relativeTestFilePath = Path.Combine(realtiveGrandChildFolderPath, testFile);
|
||||
string testFileContents = "This is the contents of a test file";
|
||||
fileSystem.WriteAllText(this.Enlistment.GetVirtualPathTo(relativeTestFilePath), testFileContents);
|
||||
this.Enlistment.GetVirtualPathTo(relativeTestFilePath).ShouldBeAFile(fileSystem).WithContents(testFileContents);
|
||||
|
||||
string targetFolder = Path.Combine(parentFolder, "target_folder_does_not_exists");
|
||||
this.Enlistment.GetVirtualPathTo(targetFolder).ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
// This move should fail
|
||||
fileSystem.MoveDirectory_TargetShouldBeInvalid(
|
||||
this.Enlistment.GetVirtualPathTo(testFolderParent),
|
||||
this.Enlistment.GetVirtualPathTo(Path.Combine(targetFolder, Path.GetFileName(testFolderParent))));
|
||||
|
||||
// The old tree structure should still be there
|
||||
this.Enlistment.GetVirtualPathTo(testFolderParent).ShouldBeADirectory(fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(realtiveChildFolderPath).ShouldBeADirectory(fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(realtiveGrandChildFolderPath).ShouldBeADirectory(fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(relativeTestFilePath).ShouldBeAFile(fileSystem).WithContents(testFileContents);
|
||||
|
||||
// Cleanup
|
||||
fileSystem.DeleteDirectory(this.Enlistment.GetVirtualPathTo(testFolderParent));
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, testFolderParent, parentFolder);
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, realtiveChildFolderPath, parentFolder);
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, realtiveGrandChildFolderPath, parentFolder);
|
||||
FileRunnersAndFolders.ShouldNotExistOnDisk(this.Enlistment, fileSystem, relativeTestFilePath, parentFolder);
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileRunnersAndFolders), nameof(FileRunnersAndFolders.Folders))]
|
||||
[Category(Categories.WindowsOnly)]
|
||||
public void CreateFileInheritsParentDirectoryAttributes(string parentFolder)
|
||||
{
|
||||
string parentDirectoryPath = this.Enlistment.GetVirtualPathTo(Path.Combine(parentFolder, "CreateFileInheritsParentDirectoryAttributes"));
|
||||
FileSystemRunner.DefaultRunner.CreateDirectory(parentDirectoryPath);
|
||||
DirectoryInfo parentDirInfo = new DirectoryInfo(parentDirectoryPath);
|
||||
parentDirInfo.Attributes |= FileAttributes.NoScrubData;
|
||||
parentDirInfo.Attributes.HasFlag(FileAttributes.NoScrubData).ShouldEqual(true);
|
||||
|
||||
string targetFilePath = Path.Combine(parentDirectoryPath, "TargetFile");
|
||||
FileSystemRunner.DefaultRunner.WriteAllText(targetFilePath, "Some contents that don't matter");
|
||||
targetFilePath.ShouldBeAFile(FileSystemRunner.DefaultRunner).WithAttribute(FileAttributes.NoScrubData);
|
||||
|
||||
FileSystemRunner.DefaultRunner.DeleteDirectory(parentDirectoryPath);
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileRunnersAndFolders), nameof(FileRunnersAndFolders.Folders))]
|
||||
[Category(Categories.WindowsOnly)]
|
||||
public void CreateDirectoryInheritsParentDirectoryAttributes(string parentFolder)
|
||||
{
|
||||
string parentDirectoryPath = this.Enlistment.GetVirtualPathTo(Path.Combine(parentFolder, "CreateDirectoryInheritsParentDirectoryAttributes"));
|
||||
FileSystemRunner.DefaultRunner.CreateDirectory(parentDirectoryPath);
|
||||
DirectoryInfo parentDirInfo = new DirectoryInfo(parentDirectoryPath);
|
||||
parentDirInfo.Attributes |= FileAttributes.NoScrubData;
|
||||
parentDirInfo.Attributes.HasFlag(FileAttributes.NoScrubData).ShouldEqual(true);
|
||||
|
||||
string targetDirPath = Path.Combine(parentDirectoryPath, "TargetDir");
|
||||
FileSystemRunner.DefaultRunner.CreateDirectory(targetDirPath);
|
||||
targetDirPath.ShouldBeADirectory(FileSystemRunner.DefaultRunner).WithAttribute(FileAttributes.NoScrubData);
|
||||
|
||||
FileSystemRunner.DefaultRunner.DeleteDirectory(parentDirectoryPath);
|
||||
}
|
||||
|
||||
private class FileRunnersAndFolders
|
||||
{
|
||||
private const string DotGitFolder = ".git";
|
||||
|
||||
private static object[] allFolders =
|
||||
{
|
||||
new object[] { string.Empty },
|
||||
new object[] { DotGitFolder },
|
||||
};
|
||||
|
||||
public static object[] Runners
|
||||
{
|
||||
get
|
||||
{
|
||||
List<object[]> runnersAndParentFolders = new List<object[]>();
|
||||
foreach (object[] runner in FileSystemRunner.Runners.ToList())
|
||||
{
|
||||
runnersAndParentFolders.Add(new object[] { runner.ToList().First(), string.Empty });
|
||||
runnersAndParentFolders.Add(new object[] { runner.ToList().First(), DotGitFolder });
|
||||
}
|
||||
|
||||
return runnersAndParentFolders.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static object[] CanDeleteFilesWhileTheyAreOpenRunners
|
||||
{
|
||||
get
|
||||
{
|
||||
// Don't use the BashRunner for the CanDeleteFilesWhileTheyAreOpen test as bash.exe (rm command) moves
|
||||
// the file to the recycle bin rather than deleting it if the file that is getting removed is currently open.
|
||||
List<object[]> runnersAndParentFolders = new List<object[]>();
|
||||
foreach (object[] runner in FileSystemRunner.Runners.ToList())
|
||||
{
|
||||
if (!(runner.ToList().First() is BashRunner))
|
||||
{
|
||||
runnersAndParentFolders.Add(new object[] { runner.ToList().First(), string.Empty });
|
||||
runnersAndParentFolders.Add(new object[] { runner.ToList().First(), DotGitFolder });
|
||||
}
|
||||
}
|
||||
|
||||
return runnersAndParentFolders.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static object[] Folders
|
||||
{
|
||||
get
|
||||
{
|
||||
return allFolders;
|
||||
}
|
||||
}
|
||||
|
||||
public static void ShouldNotExistOnDisk(GVFSFunctionalTestEnlistment enlistment, FileSystemRunner fileSystem, string filename, string parentFolder)
|
||||
{
|
||||
enlistment.GetVirtualPathTo(filename).ShouldNotExistOnDisk(fileSystem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,233 +0,0 @@
|
|||
using GVFS.FunctionalTests.FileSystemRunners;
|
||||
using GVFS.FunctionalTests.Should;
|
||||
using GVFS.FunctionalTests.Tools;
|
||||
using GVFS.Tests.Should;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
||||
{
|
||||
[TestFixture]
|
||||
[Category(Categories.ExtraCoverage)]
|
||||
public class DehydrateTests : TestsWithEnlistmentPerFixture
|
||||
{
|
||||
private const int GVFSGenericError = 3;
|
||||
private FileSystemRunner fileSystem;
|
||||
|
||||
// Set forcePerRepoObjectCache to true so that DehydrateShouldSucceedEvenIfObjectCacheIsDeleted does
|
||||
// not delete the shared local cache
|
||||
public DehydrateTests()
|
||||
: base(forcePerRepoObjectCache: true)
|
||||
{
|
||||
this.fileSystem = new SystemIORunner();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void DehydrateShouldExitWithoutConfirm()
|
||||
{
|
||||
this.DehydrateShouldSucceed("To actually execute the dehydrate, run 'gvfs dehydrate --confirm'", confirm: false, noStatus: false);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
[Category(Categories.MacTODO.NeedsDehydrate)] // Dehydrate is not successful
|
||||
public void DehydrateShouldSucceedInCommonCase()
|
||||
{
|
||||
this.DehydrateShouldSucceed("The repo was successfully dehydrated and remounted", confirm: true, noStatus: false);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void DehydrateShouldFailOnUnmountedRepoWithStatus()
|
||||
{
|
||||
this.Enlistment.UnmountGVFS();
|
||||
this.DehydrateShouldFail("Failed to run git status because the repo is not mounted", noStatus: false);
|
||||
this.Enlistment.MountGVFS();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
[Category(Categories.MacTODO.NeedsDehydrate)] // Call CmdRunner which is windows specific
|
||||
public void DehydrateShouldSucceedEvenIfObjectCacheIsDeleted()
|
||||
{
|
||||
this.Enlistment.UnmountGVFS();
|
||||
CmdRunner.DeleteDirectoryWithUnlimitedRetries(this.Enlistment.GetObjectRoot(this.fileSystem));
|
||||
this.DehydrateShouldSucceed("The repo was successfully dehydrated and remounted", confirm: true, noStatus: true);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
[Category(Categories.MacTODO.NeedsDehydrate)] // .git / .gvfs are not copied to backup folder on Mac
|
||||
public void DehydrateShouldBackupFiles()
|
||||
{
|
||||
this.DehydrateShouldSucceed("The repo was successfully dehydrated and remounted", confirm: true, noStatus: false);
|
||||
string backupFolder = Path.Combine(this.Enlistment.EnlistmentRoot, "dehydrate_backup");
|
||||
backupFolder.ShouldBeADirectory(this.fileSystem);
|
||||
string[] backupFolderItems = this.fileSystem.EnumerateDirectory(backupFolder).Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
backupFolderItems.Length.ShouldEqual(1);
|
||||
this.DirectoryShouldContain(backupFolderItems[0], ".git", GVFSTestConfig.DotGVFSRoot, "src");
|
||||
|
||||
// .git folder items
|
||||
string gitFolder = Path.Combine(backupFolderItems[0], ".git");
|
||||
this.DirectoryShouldContain(gitFolder, "index");
|
||||
|
||||
// .gvfs folder items
|
||||
string gvfsFolder = Path.Combine(backupFolderItems[0], GVFSTestConfig.DotGVFSRoot);
|
||||
this.DirectoryShouldContain(gvfsFolder, "databases", "GVFS_projection");
|
||||
|
||||
string gvfsDatabasesFolder = Path.Combine(gvfsFolder, "databases");
|
||||
this.DirectoryShouldContain(gvfsDatabasesFolder, "BackgroundGitOperations.dat", "ModifiedPaths.dat", "VFSForGit.sqlite");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void DehydrateShouldFailIfLocalCacheNotInMetadata()
|
||||
{
|
||||
this.Enlistment.UnmountGVFS();
|
||||
|
||||
string majorVersion;
|
||||
string minorVersion;
|
||||
GVFSHelpers.GetPersistedDiskLayoutVersion(this.Enlistment.DotGVFSRoot, out majorVersion, out minorVersion);
|
||||
string objectsRoot = GVFSHelpers.GetPersistedGitObjectsRoot(this.Enlistment.DotGVFSRoot).ShouldNotBeNull();
|
||||
|
||||
string metadataPath = Path.Combine(this.Enlistment.DotGVFSRoot, GVFSHelpers.RepoMetadataName);
|
||||
string metadataBackupPath = metadataPath + ".backup";
|
||||
this.fileSystem.MoveFile(metadataPath, metadataBackupPath);
|
||||
|
||||
this.fileSystem.CreateEmptyFile(metadataPath);
|
||||
GVFSHelpers.SaveDiskLayoutVersion(this.Enlistment.DotGVFSRoot, majorVersion, minorVersion);
|
||||
GVFSHelpers.SaveGitObjectsRoot(this.Enlistment.DotGVFSRoot, objectsRoot);
|
||||
|
||||
this.DehydrateShouldFail("Failed to determine local cache path from repo metadata", noStatus: true);
|
||||
|
||||
this.fileSystem.DeleteFile(metadataPath);
|
||||
this.fileSystem.MoveFile(metadataBackupPath, metadataPath);
|
||||
|
||||
this.Enlistment.MountGVFS();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void DehydrateShouldFailIfGitObjectsRootNotInMetadata()
|
||||
{
|
||||
this.Enlistment.UnmountGVFS();
|
||||
|
||||
string majorVersion;
|
||||
string minorVersion;
|
||||
GVFSHelpers.GetPersistedDiskLayoutVersion(this.Enlistment.DotGVFSRoot, out majorVersion, out minorVersion);
|
||||
string localCacheRoot = GVFSHelpers.GetPersistedLocalCacheRoot(this.Enlistment.DotGVFSRoot).ShouldNotBeNull();
|
||||
|
||||
string metadataPath = Path.Combine(this.Enlistment.DotGVFSRoot, GVFSHelpers.RepoMetadataName);
|
||||
string metadataBackupPath = metadataPath + ".backup";
|
||||
this.fileSystem.MoveFile(metadataPath, metadataBackupPath);
|
||||
|
||||
this.fileSystem.CreateEmptyFile(metadataPath);
|
||||
GVFSHelpers.SaveDiskLayoutVersion(this.Enlistment.DotGVFSRoot, majorVersion, minorVersion);
|
||||
GVFSHelpers.SaveLocalCacheRoot(this.Enlistment.DotGVFSRoot, localCacheRoot);
|
||||
|
||||
this.DehydrateShouldFail("Failed to determine git objects root from repo metadata", noStatus: true);
|
||||
|
||||
this.fileSystem.DeleteFile(metadataPath);
|
||||
this.fileSystem.MoveFile(metadataBackupPath, metadataPath);
|
||||
|
||||
this.Enlistment.MountGVFS();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
[Category(Categories.MacTODO.NeedsDehydrate)] // Error messages on mac are different, DehydrateShouldFail fails on message checking
|
||||
public void DehydrateShouldFailOnWrongDiskLayoutVersion()
|
||||
{
|
||||
this.Enlistment.UnmountGVFS();
|
||||
|
||||
string majorVersion;
|
||||
string minorVersion;
|
||||
GVFSHelpers.GetPersistedDiskLayoutVersion(this.Enlistment.DotGVFSRoot, out majorVersion, out minorVersion);
|
||||
|
||||
int majorVersionNum;
|
||||
int minorVersionNum;
|
||||
int.TryParse(majorVersion.ShouldNotBeNull(), out majorVersionNum).ShouldEqual(true);
|
||||
int.TryParse(minorVersion.ShouldNotBeNull(), out minorVersionNum).ShouldEqual(true);
|
||||
|
||||
GVFSHelpers.SaveDiskLayoutVersion(this.Enlistment.DotGVFSRoot, (majorVersionNum - 1).ToString(), "0");
|
||||
this.DehydrateShouldFail("disk layout version doesn't match current version", noStatus: true);
|
||||
|
||||
GVFSHelpers.SaveDiskLayoutVersion(this.Enlistment.DotGVFSRoot, (majorVersionNum + 1).ToString(), "0");
|
||||
this.DehydrateShouldFail("Changes to GVFS disk layout do not allow mounting after downgrade.", noStatus: true);
|
||||
|
||||
GVFSHelpers.SaveDiskLayoutVersion(this.Enlistment.DotGVFSRoot, majorVersionNum.ToString(), minorVersionNum.ToString());
|
||||
|
||||
this.Enlistment.MountGVFS();
|
||||
}
|
||||
|
||||
private void DirectoryShouldContain(string directory, params string[] fileOrFolders)
|
||||
{
|
||||
string[] folderItems = this.fileSystem.EnumerateDirectory(directory).Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
folderItems.Length.ShouldEqual(fileOrFolders.Length);
|
||||
for (int i = 0; i < fileOrFolders.Length; i++)
|
||||
{
|
||||
Path.GetFileName(folderItems[i]).ShouldEqual(fileOrFolders[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void DehydrateShouldSucceed(string expectedOutput, bool confirm, bool noStatus)
|
||||
{
|
||||
ProcessResult result = this.RunDehydrateProcess(confirm, noStatus);
|
||||
result.ExitCode.ShouldEqual(0, $"mount exit code was {result.ExitCode}. Output: {result.Output}");
|
||||
|
||||
if (result.Output.Contains("Failed to move the src folder: Access to the path"))
|
||||
{
|
||||
string output = this.RunHandleProcess(Path.Combine(this.Enlistment.EnlistmentRoot, "src"));
|
||||
TestContext.Out.WriteLine(output);
|
||||
}
|
||||
|
||||
result.Output.ShouldContain(expectedOutput);
|
||||
}
|
||||
|
||||
private void DehydrateShouldFail(string expectedErrorMessage, bool noStatus)
|
||||
{
|
||||
ProcessResult result = this.RunDehydrateProcess(confirm: true, noStatus: noStatus);
|
||||
result.ExitCode.ShouldEqual(GVFSGenericError, $"mount exit code was not {GVFSGenericError}");
|
||||
result.Output.ShouldContain(expectedErrorMessage);
|
||||
}
|
||||
|
||||
private ProcessResult RunDehydrateProcess(bool confirm, bool noStatus)
|
||||
{
|
||||
string dehydrateFlags = string.Empty;
|
||||
if (confirm)
|
||||
{
|
||||
dehydrateFlags += " --confirm ";
|
||||
}
|
||||
|
||||
if (noStatus)
|
||||
{
|
||||
dehydrateFlags += " --no-status ";
|
||||
}
|
||||
|
||||
string enlistmentRoot = this.Enlistment.EnlistmentRoot;
|
||||
|
||||
ProcessStartInfo processInfo = new ProcessStartInfo(GVFSTestConfig.PathToGVFS);
|
||||
processInfo.Arguments = "dehydrate " + dehydrateFlags + " " + TestConstants.InternalUseOnlyFlag + " " + GVFSHelpers.GetInternalParameter();
|
||||
processInfo.WindowStyle = ProcessWindowStyle.Hidden;
|
||||
processInfo.WorkingDirectory = enlistmentRoot;
|
||||
processInfo.UseShellExecute = false;
|
||||
processInfo.RedirectStandardOutput = true;
|
||||
|
||||
return ProcessHelper.Run(processInfo);
|
||||
}
|
||||
|
||||
private string RunHandleProcess(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
ProcessStartInfo processInfo = new ProcessStartInfo("handle.exe");
|
||||
processInfo.Arguments = "-p " + path;
|
||||
processInfo.WindowStyle = ProcessWindowStyle.Hidden;
|
||||
processInfo.WorkingDirectory = this.Enlistment.EnlistmentRoot;
|
||||
processInfo.UseShellExecute = false;
|
||||
processInfo.RedirectStandardOutput = true;
|
||||
|
||||
return "handle.exe output: " + ProcessHelper.Run(processInfo).Output;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return $"Exception running handle.exe - {ex.Message}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,128 +0,0 @@
|
|||
using GVFS.FunctionalTests.FileSystemRunners;
|
||||
using GVFS.FunctionalTests.Properties;
|
||||
using GVFS.FunctionalTests.Tools;
|
||||
using GVFS.Tests.Should;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
||||
{
|
||||
[TestFixture]
|
||||
public class GVFSLockTests : TestsWithEnlistmentPerFixture
|
||||
{
|
||||
private FileSystemRunner fileSystem;
|
||||
|
||||
public GVFSLockTests()
|
||||
{
|
||||
this.fileSystem = new SystemIORunner();
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum MoveFileFlags : uint
|
||||
{
|
||||
MoveFileReplaceExisting = 0x00000001, // MOVEFILE_REPLACE_EXISTING
|
||||
MoveFileCopyAllowed = 0x00000002, // MOVEFILE_COPY_ALLOWED
|
||||
MoveFileDelayUntilReboot = 0x00000004, // MOVEFILE_DELAY_UNTIL_REBOOT
|
||||
MoveFileWriteThrough = 0x00000008, // MOVEFILE_WRITE_THROUGH
|
||||
MoveFileCreateHardlink = 0x00000010, // MOVEFILE_CREATE_HARDLINK
|
||||
MoveFileFailIfNotTrackable = 0x00000020, // MOVEFILE_FAIL_IF_NOT_TRACKABLE
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void GitCheckoutFailsOutsideLock()
|
||||
{
|
||||
const string BackupPrefix = "BACKUP_";
|
||||
string preCommand = "pre-command" + Settings.Default.BinaryFileNameExtension;
|
||||
string postCommand = "post-command" + Settings.Default.BinaryFileNameExtension;
|
||||
|
||||
string hooksBase = Path.Combine(this.Enlistment.RepoRoot, ".git", "hooks");
|
||||
|
||||
try
|
||||
{
|
||||
// Get hooks out of the way to simulate lock not being acquired as expected
|
||||
this.fileSystem.MoveFile(Path.Combine(hooksBase, preCommand), Path.Combine(hooksBase, BackupPrefix + preCommand));
|
||||
this.fileSystem.MoveFile(Path.Combine(hooksBase, postCommand), Path.Combine(hooksBase, BackupPrefix + postCommand));
|
||||
|
||||
ProcessResult result = GitHelpers.InvokeGitAgainstGVFSRepo(this.Enlistment.RepoRoot, "checkout FunctionalTests/20170510_minor");
|
||||
result.Errors.ShouldContain("fatal: unable to write new index file");
|
||||
|
||||
// Ensure that branch didnt move, note however that work dir might not be clean
|
||||
GitHelpers.CheckGitCommandAgainstGVFSRepo(
|
||||
this.Enlistment.RepoRoot,
|
||||
"status",
|
||||
"On branch " + Properties.Settings.Default.Commitish);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Reset hooks for cleanup.
|
||||
this.fileSystem.MoveFile(Path.Combine(hooksBase, BackupPrefix + preCommand), Path.Combine(hooksBase, preCommand));
|
||||
this.fileSystem.MoveFile(Path.Combine(hooksBase, BackupPrefix + postCommand), Path.Combine(hooksBase, postCommand));
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void LockPreventsRenameFromOutsideRootOnTopOfIndex()
|
||||
{
|
||||
this.OverwritingIndexShouldFail(Path.Combine(this.Enlistment.EnlistmentRoot, "LockPreventsRenameFromOutsideRootOnTopOfIndex.txt"));
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void LockPreventsRenameFromInsideWorkingTreeOnTopOfIndex()
|
||||
{
|
||||
this.OverwritingIndexShouldFail(this.Enlistment.GetVirtualPathTo("LockPreventsRenameFromInsideWorkingTreeOnTopOfIndex.txt"));
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void LockPreventsRenameOfIndexLockOnTopOfIndex()
|
||||
{
|
||||
this.OverwritingIndexShouldFail(this.Enlistment.GetVirtualPathTo(".git", "index.lock"));
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", EntryPoint = "MoveFileEx", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
private static extern bool WindowsMoveFileEx(
|
||||
string existingFileName,
|
||||
string newFileName,
|
||||
uint flags);
|
||||
|
||||
[DllImport("libc", EntryPoint = "rename", SetLastError = true)]
|
||||
private static extern int MacRename(string oldPath, string newPath);
|
||||
|
||||
private void OverwritingIndexShouldFail(string testFilePath)
|
||||
{
|
||||
string indexPath = this.Enlistment.GetVirtualPathTo(".git", "index");
|
||||
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
byte[] indexContents = File.ReadAllBytes(indexPath);
|
||||
|
||||
string testFileContents = "OverwriteIndexTest";
|
||||
this.fileSystem.WriteAllText(testFilePath, testFileContents);
|
||||
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
|
||||
this.RenameAndOverwrite(testFilePath, indexPath).ShouldBeFalse("GVFS should prevent renaming on top of index when GVFSLock is not held");
|
||||
byte[] newIndexContents = File.ReadAllBytes(indexPath);
|
||||
|
||||
indexContents.SequenceEqual(newIndexContents).ShouldBeTrue("Index contenst should not have changed");
|
||||
|
||||
this.fileSystem.DeleteFile(testFilePath);
|
||||
}
|
||||
|
||||
private bool RenameAndOverwrite(string oldPath, string newPath)
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return WindowsMoveFileEx(
|
||||
oldPath,
|
||||
newPath,
|
||||
(uint)(MoveFileFlags.MoveFileReplaceExisting | MoveFileFlags.MoveFileCopyAllowed));
|
||||
}
|
||||
else
|
||||
{
|
||||
return MacRename(oldPath, newPath) == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
using GVFS.FunctionalTests.Tools;
|
||||
using GVFS.Tests.Should;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
||||
{
|
||||
[TestFixture]
|
||||
[Category(Categories.GitCommands)]
|
||||
public class GitBlockCommandsTests : TestsWithEnlistmentPerFixture
|
||||
{
|
||||
[TestCase]
|
||||
public void GitBlockCommands()
|
||||
{
|
||||
this.CommandBlocked("fsck");
|
||||
this.CommandBlocked("gc");
|
||||
this.CommandNotBlocked("gc --auto");
|
||||
this.CommandBlocked("prune");
|
||||
this.CommandBlocked("prune");
|
||||
this.CommandBlocked("repack");
|
||||
this.CommandBlocked("submodule");
|
||||
this.CommandBlocked("submodule status");
|
||||
this.CommandBlocked("update-index --index-version 2");
|
||||
this.CommandBlocked("update-index --skip-worktree");
|
||||
this.CommandBlocked("update-index --no-skip-worktree");
|
||||
this.CommandBlocked("update-index --split-index");
|
||||
this.CommandBlocked("worktree list");
|
||||
}
|
||||
|
||||
private void CommandBlocked(string command)
|
||||
{
|
||||
ProcessResult result = GitHelpers.InvokeGitAgainstGVFSRepo(
|
||||
this.Enlistment.RepoRoot,
|
||||
command);
|
||||
result.ExitCode.ShouldNotEqual(0, $"Command {command} not blocked when it should be. Errors: {result.Errors}");
|
||||
}
|
||||
|
||||
private void CommandNotBlocked(string command)
|
||||
{
|
||||
ProcessResult result = GitHelpers.InvokeGitAgainstGVFSRepo(
|
||||
this.Enlistment.RepoRoot,
|
||||
command);
|
||||
result.ExitCode.ShouldEqual(0, $"Command {command} blocked when it should not be. Errors: {result.Errors}");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,488 +0,0 @@
|
|||
using GVFS.FunctionalTests.FileSystemRunners;
|
||||
using GVFS.FunctionalTests.Should;
|
||||
using GVFS.FunctionalTests.Tools;
|
||||
using GVFS.Tests.Should;
|
||||
using NUnit.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
||||
{
|
||||
[TestFixtureSource(typeof(FileSystemRunner), nameof(FileSystemRunner.Runners))]
|
||||
public class GitFilesTests : TestsWithEnlistmentPerFixture
|
||||
{
|
||||
private FileSystemRunner fileSystem;
|
||||
|
||||
public GitFilesTests(FileSystemRunner fileSystem)
|
||||
{
|
||||
this.fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
[TestCase, Order(1)]
|
||||
public void CreateFileTest()
|
||||
{
|
||||
string fileName = "file1.txt";
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, fileName);
|
||||
this.fileSystem.WriteAllText(this.Enlistment.GetVirtualPathTo(fileName), "Some content here");
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, fileName);
|
||||
this.Enlistment.GetVirtualPathTo(fileName).ShouldBeAFile(this.fileSystem).WithContents("Some content here");
|
||||
|
||||
string emptyFileName = "file1empty.txt";
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, emptyFileName);
|
||||
this.fileSystem.CreateEmptyFile(this.Enlistment.GetVirtualPathTo(emptyFileName));
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, emptyFileName);
|
||||
this.Enlistment.GetVirtualPathTo(emptyFileName).ShouldBeAFile(this.fileSystem);
|
||||
}
|
||||
|
||||
[TestCase, Order(2)]
|
||||
public void CreateHardLinkTest()
|
||||
{
|
||||
string existingFileName = "fileToLinkTo.txt";
|
||||
string existingFilePath = this.Enlistment.GetVirtualPathTo(existingFileName);
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, existingFileName);
|
||||
this.fileSystem.WriteAllText(existingFilePath, "Some content here");
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, existingFileName);
|
||||
existingFilePath.ShouldBeAFile(this.fileSystem).WithContents("Some content here");
|
||||
|
||||
string newLinkFileName = "newHardLink.txt";
|
||||
string newLinkFilePath = this.Enlistment.GetVirtualPathTo(newLinkFileName);
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, newLinkFileName);
|
||||
this.fileSystem.CreateHardLink(newLinkFilePath, existingFilePath);
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, newLinkFileName);
|
||||
newLinkFilePath.ShouldBeAFile(this.fileSystem).WithContents("Some content here");
|
||||
}
|
||||
|
||||
[TestCase, Order(3)]
|
||||
public void CreateFileInFolderTest()
|
||||
{
|
||||
string folderName = "folder2";
|
||||
string fileName = "file2.txt";
|
||||
string filePath = Path.Combine(folderName, fileName);
|
||||
|
||||
this.Enlistment.GetVirtualPathTo(filePath).ShouldNotExistOnDisk(this.fileSystem);
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, filePath);
|
||||
|
||||
this.fileSystem.CreateDirectory(this.Enlistment.GetVirtualPathTo(folderName));
|
||||
this.fileSystem.CreateEmptyFile(this.Enlistment.GetVirtualPathTo(filePath));
|
||||
this.Enlistment.GetVirtualPathTo(filePath).ShouldBeAFile(this.fileSystem);
|
||||
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, folderName + "/");
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, folderName + "/" + fileName);
|
||||
}
|
||||
|
||||
[TestCase, Order(4)]
|
||||
public void RenameEmptyFolderTest()
|
||||
{
|
||||
string folderName = "folder3a";
|
||||
string renamedFolderName = "folder3b";
|
||||
string[] expectedModifiedEntries =
|
||||
{
|
||||
renamedFolderName + "/",
|
||||
};
|
||||
|
||||
this.Enlistment.GetVirtualPathTo(folderName).ShouldNotExistOnDisk(this.fileSystem);
|
||||
this.fileSystem.CreateDirectory(this.Enlistment.GetVirtualPathTo(folderName));
|
||||
this.fileSystem.MoveDirectory(this.Enlistment.GetVirtualPathTo(folderName), this.Enlistment.GetVirtualPathTo(renamedFolderName));
|
||||
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, expectedModifiedEntries);
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, folderName + "/");
|
||||
}
|
||||
|
||||
[TestCase, Order(5)]
|
||||
public void RenameFolderTest()
|
||||
{
|
||||
string folderName = "folder4a";
|
||||
string renamedFolderName = "folder4b";
|
||||
string[] fileNames = { "a", "b", "c" };
|
||||
string[] expectedModifiedEntries =
|
||||
{
|
||||
renamedFolderName + "/",
|
||||
};
|
||||
|
||||
string[] unexpectedModifiedEntries =
|
||||
{
|
||||
renamedFolderName + "/" + fileNames[0],
|
||||
renamedFolderName + "/" + fileNames[1],
|
||||
renamedFolderName + "/" + fileNames[2],
|
||||
folderName + "/",
|
||||
folderName + "/" + fileNames[0],
|
||||
folderName + "/" + fileNames[1],
|
||||
folderName + "/" + fileNames[2],
|
||||
};
|
||||
|
||||
this.Enlistment.GetVirtualPathTo(folderName).ShouldNotExistOnDisk(this.fileSystem);
|
||||
this.fileSystem.CreateDirectory(this.Enlistment.GetVirtualPathTo(folderName));
|
||||
foreach (string fileName in fileNames)
|
||||
{
|
||||
string filePath = Path.Combine(folderName, fileName);
|
||||
this.fileSystem.CreateEmptyFile(this.Enlistment.GetVirtualPathTo(filePath));
|
||||
this.Enlistment.GetVirtualPathTo(filePath).ShouldBeAFile(this.fileSystem);
|
||||
}
|
||||
|
||||
this.fileSystem.MoveDirectory(this.Enlistment.GetVirtualPathTo(folderName), this.Enlistment.GetVirtualPathTo(renamedFolderName));
|
||||
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, expectedModifiedEntries);
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, unexpectedModifiedEntries);
|
||||
}
|
||||
|
||||
[TestCase, Order(6)]
|
||||
public void CaseOnlyRenameOfNewFolderKeepsModifiedPathsEntries()
|
||||
{
|
||||
if (this.fileSystem is PowerShellRunner)
|
||||
{
|
||||
Assert.Ignore("Powershell does not support case only renames.");
|
||||
}
|
||||
|
||||
this.fileSystem.CreateDirectory(Path.Combine(this.Enlistment.RepoRoot, "Folder"));
|
||||
this.fileSystem.CreateEmptyFile(Path.Combine(this.Enlistment.RepoRoot, "Folder", "testfile"));
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, "Folder/");
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, "Folder/testfile");
|
||||
|
||||
this.fileSystem.RenameDirectory(this.Enlistment.RepoRoot, "Folder", "folder");
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, "folder/");
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, "folder/testfile");
|
||||
}
|
||||
|
||||
[TestCase, Order(7)]
|
||||
public void ReadingFileDoesNotUpdateIndexOrModifiedPaths()
|
||||
{
|
||||
string gitFileToCheck = "GVFS/GVFS.FunctionalTests/Category/CategoryConstants.cs";
|
||||
string virtualFile = this.Enlistment.GetVirtualPathTo(gitFileToCheck);
|
||||
ProcessResult initialResult = GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "ls-files --debug -svmodc " + gitFileToCheck);
|
||||
initialResult.ShouldNotBeNull();
|
||||
initialResult.Output.ShouldNotBeNull();
|
||||
initialResult.Output.StartsWith("S ").ShouldEqual(true);
|
||||
initialResult.Output.ShouldContain("ctime: 0:0", "mtime: 0:0", "size: 0\t");
|
||||
|
||||
using (FileStream fileStreamToRead = File.OpenRead(virtualFile))
|
||||
{
|
||||
fileStreamToRead.ReadByte();
|
||||
}
|
||||
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
|
||||
ProcessResult afterUpdateResult = GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "ls-files --debug -svmodc " + gitFileToCheck);
|
||||
afterUpdateResult.ShouldNotBeNull();
|
||||
afterUpdateResult.Output.ShouldNotBeNull();
|
||||
afterUpdateResult.Output.StartsWith("S ").ShouldEqual(true);
|
||||
afterUpdateResult.Output.ShouldContain("ctime: 0:0", "mtime: 0:0", "size: 0\t");
|
||||
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, gitFileToCheck);
|
||||
}
|
||||
|
||||
[TestCase, Order(8)]
|
||||
public void ModifiedFileWillGetAddedToModifiedPathsFile()
|
||||
{
|
||||
string gitFileToTest = "GVFS/GVFS.Common/RetryWrapper.cs";
|
||||
string fileToCreate = this.Enlistment.GetVirtualPathTo(gitFileToTest);
|
||||
this.VerifyWorktreeBit(gitFileToTest, LsFilesStatus.SkipWorktree);
|
||||
|
||||
ManualResetEventSlim resetEvent = GitHelpers.AcquireGVFSLock(this.Enlistment, out _);
|
||||
|
||||
this.fileSystem.WriteAllText(fileToCreate, "Anything can go here");
|
||||
this.fileSystem.FileExists(fileToCreate).ShouldEqual(true);
|
||||
resetEvent.Set();
|
||||
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, gitFileToTest);
|
||||
this.VerifyWorktreeBit(gitFileToTest, LsFilesStatus.Cached);
|
||||
}
|
||||
|
||||
[TestCase, Order(9)]
|
||||
public void RenamedFileAddedToModifiedPathsFile()
|
||||
{
|
||||
string fileToRenameEntry = "Test_EPF_MoveRenameFileTests/ChangeUnhydratedFileName/Program.cs";
|
||||
string fileToRenameTargetEntry = "Test_EPF_MoveRenameFileTests/ChangeUnhydratedFileName/Program2.cs";
|
||||
this.VerifyWorktreeBit(fileToRenameEntry, LsFilesStatus.SkipWorktree);
|
||||
|
||||
this.fileSystem.MoveFile(
|
||||
this.Enlistment.GetVirtualPathTo(fileToRenameEntry),
|
||||
this.Enlistment.GetVirtualPathTo(fileToRenameTargetEntry));
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, fileToRenameEntry);
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, fileToRenameTargetEntry);
|
||||
|
||||
// Verify skip-worktree cleared
|
||||
this.VerifyWorktreeBit(fileToRenameEntry, LsFilesStatus.Cached);
|
||||
}
|
||||
|
||||
[TestCase, Order(10)]
|
||||
public void RenamedFileAndOverwrittenTargetAddedToModifiedPathsFile()
|
||||
{
|
||||
string fileToRenameEntry = "Test_EPF_MoveRenameFileTests_2/MoveUnhydratedFileToOverwriteUnhydratedFileAndWrite/RunUnitTests.bat";
|
||||
string fileToRenameTargetEntry = "Test_EPF_MoveRenameFileTests_2/MoveUnhydratedFileToOverwriteUnhydratedFileAndWrite/RunFunctionalTests.bat";
|
||||
this.VerifyWorktreeBit(fileToRenameEntry, LsFilesStatus.SkipWorktree);
|
||||
this.VerifyWorktreeBit(fileToRenameTargetEntry, LsFilesStatus.SkipWorktree);
|
||||
|
||||
this.fileSystem.ReplaceFile(
|
||||
this.Enlistment.GetVirtualPathTo(fileToRenameEntry),
|
||||
this.Enlistment.GetVirtualPathTo(fileToRenameTargetEntry));
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, fileToRenameEntry);
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, fileToRenameTargetEntry);
|
||||
|
||||
// Verify skip-worktree cleared
|
||||
this.VerifyWorktreeBit(fileToRenameEntry, LsFilesStatus.Cached);
|
||||
this.VerifyWorktreeBit(fileToRenameTargetEntry, LsFilesStatus.Cached);
|
||||
}
|
||||
|
||||
[TestCase, Order(11)]
|
||||
public void DeletedFileAddedToModifiedPathsFile()
|
||||
{
|
||||
string fileToDeleteEntry = "GVFlt_DeleteFileTest/GVFlt_DeleteFullFileWithoutFileContext_DeleteOnClose/a.txt";
|
||||
this.VerifyWorktreeBit(fileToDeleteEntry, LsFilesStatus.SkipWorktree);
|
||||
|
||||
this.fileSystem.DeleteFile(this.Enlistment.GetVirtualPathTo(fileToDeleteEntry));
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, fileToDeleteEntry);
|
||||
|
||||
// Verify skip-worktree cleared
|
||||
this.VerifyWorktreeBit(fileToDeleteEntry, LsFilesStatus.Cached);
|
||||
}
|
||||
|
||||
[TestCase, Order(12)]
|
||||
public void DeletedFolderAndChildrenAddedToToModifiedPathsFile()
|
||||
{
|
||||
string folderToDelete = "Scripts";
|
||||
string[] filesToDelete = new string[]
|
||||
{
|
||||
"Scripts/CreateCommonAssemblyVersion.bat",
|
||||
"Scripts/CreateCommonCliAssemblyVersion.bat",
|
||||
"Scripts/CreateCommonVersionHeader.bat",
|
||||
"Scripts/RunFunctionalTests.bat",
|
||||
"Scripts/RunUnitTests.bat"
|
||||
};
|
||||
|
||||
// Verify skip-worktree initial set for all files
|
||||
foreach (string file in filesToDelete)
|
||||
{
|
||||
this.VerifyWorktreeBit(file, LsFilesStatus.SkipWorktree);
|
||||
}
|
||||
|
||||
this.fileSystem.DeleteDirectory(this.Enlistment.GetVirtualPathTo(folderToDelete));
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, folderToDelete + "/");
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, filesToDelete);
|
||||
|
||||
// Verify skip-worktree cleared
|
||||
foreach (string file in filesToDelete)
|
||||
{
|
||||
this.VerifyWorktreeBit(file, LsFilesStatus.Cached);
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase, Order(13)]
|
||||
public void FileRenamedOutOfRepoAddedToModifiedPathsAndSkipWorktreeBitCleared()
|
||||
{
|
||||
string fileToRenameEntry = "GVFlt_MoveFileTest/PartialToOutside/from/lessInFrom.txt";
|
||||
string fileToRenameVirtualPath = this.Enlistment.GetVirtualPathTo(fileToRenameEntry);
|
||||
this.VerifyWorktreeBit(fileToRenameEntry, LsFilesStatus.SkipWorktree);
|
||||
|
||||
string fileOutsideRepoPath = Path.Combine(this.Enlistment.EnlistmentRoot, $"{nameof(this.FileRenamedOutOfRepoAddedToModifiedPathsAndSkipWorktreeBitCleared)}.txt");
|
||||
this.fileSystem.MoveFile(fileToRenameVirtualPath, fileOutsideRepoPath);
|
||||
fileOutsideRepoPath.ShouldBeAFile(this.fileSystem).WithContents("lessData");
|
||||
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, fileToRenameEntry);
|
||||
|
||||
// Verify skip-worktree cleared
|
||||
this.VerifyWorktreeBit(fileToRenameEntry, LsFilesStatus.Cached);
|
||||
}
|
||||
|
||||
[TestCase, Order(14)]
|
||||
public void OverwrittenFileAddedToModifiedPathsAndSkipWorktreeBitCleared()
|
||||
{
|
||||
string fileToOverwriteEntry = "Test_EPF_WorkingDirectoryTests/1/2/3/4/ReadDeepProjectedFile.cpp";
|
||||
string fileToOverwriteVirtualPath = this.Enlistment.GetVirtualPathTo(fileToOverwriteEntry);
|
||||
this.VerifyWorktreeBit(fileToOverwriteEntry, LsFilesStatus.SkipWorktree);
|
||||
|
||||
string testContents = $"Test contents for {nameof(this.OverwrittenFileAddedToModifiedPathsAndSkipWorktreeBitCleared)}";
|
||||
|
||||
this.fileSystem.WriteAllText(fileToOverwriteVirtualPath, testContents);
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
|
||||
fileToOverwriteVirtualPath.ShouldBeAFile(this.fileSystem).WithContents(testContents);
|
||||
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, fileToOverwriteEntry);
|
||||
|
||||
// Verify skip-worktree cleared
|
||||
this.VerifyWorktreeBit(fileToOverwriteEntry, LsFilesStatus.Cached);
|
||||
}
|
||||
|
||||
// WindowsOnly because Mac does not support SupersedeFile
|
||||
[TestCase, Order(15)]
|
||||
[Category(Categories.WindowsOnly)]
|
||||
public void SupersededFileAddedToModifiedPathsAndSkipWorktreeBitCleared()
|
||||
{
|
||||
string fileToSupersedeEntry = "GVFlt_FileOperationTest/WriteAndVerify.txt";
|
||||
string fileToSupersedePath = this.Enlistment.GetVirtualPathTo("GVFlt_FileOperationTest\\WriteAndVerify.txt");
|
||||
this.VerifyWorktreeBit(fileToSupersedeEntry, LsFilesStatus.SkipWorktree);
|
||||
|
||||
string newContent = $"{nameof(this.SupersededFileAddedToModifiedPathsAndSkipWorktreeBitCleared)} test new contents";
|
||||
|
||||
SupersedeFile(fileToSupersedePath, newContent).ShouldEqual(true);
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, fileToSupersedeEntry);
|
||||
|
||||
// Verify skip-worktree cleared
|
||||
this.VerifyWorktreeBit(fileToSupersedeEntry, LsFilesStatus.Cached);
|
||||
|
||||
// Verify new content written
|
||||
fileToSupersedePath.ShouldBeAFile(this.fileSystem).WithContents(newContent);
|
||||
}
|
||||
|
||||
[TestCase, Order(16)]
|
||||
public void FileMovedFromOutsideRepoToInside()
|
||||
{
|
||||
string fileName = "OutsideRepoToInside.txt";
|
||||
string fileOutsideRepo = Path.Combine(this.Enlistment.EnlistmentRoot, fileName);
|
||||
this.fileSystem.WriteAllText(fileOutsideRepo, "Contents for the new file");
|
||||
fileOutsideRepo.ShouldBeAFile(this.fileSystem);
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, fileName);
|
||||
|
||||
string fileMovedInsideRepo = this.Enlistment.GetVirtualPathTo(fileName);
|
||||
this.fileSystem.MoveFile(fileOutsideRepo, fileMovedInsideRepo);
|
||||
fileMovedInsideRepo.ShouldBeAFile(this.fileSystem);
|
||||
fileOutsideRepo.ShouldNotExistOnDisk(this.fileSystem);
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, fileName);
|
||||
}
|
||||
|
||||
[TestCase, Order(17)]
|
||||
public void FileMovedFromInsideRepoToOutside()
|
||||
{
|
||||
string fileInsideRepoEntry = "GitCommandsTests/RenameFileTests/1/#test";
|
||||
string fileNameFullPath = Path.Combine("GitCommandsTests", "RenameFileTests", "1", "#test");
|
||||
string fileInsideRepo = this.Enlistment.GetVirtualPathTo(fileNameFullPath);
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, fileInsideRepoEntry);
|
||||
|
||||
string fileNameOutsideRepo = "FileNameOutSideRepo";
|
||||
string fileMovedOutsideRepo = Path.Combine(this.Enlistment.EnlistmentRoot, fileNameOutsideRepo);
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, fileNameOutsideRepo);
|
||||
|
||||
this.fileSystem.MoveFile(fileInsideRepo, fileMovedOutsideRepo);
|
||||
|
||||
fileInsideRepo.ShouldNotExistOnDisk(this.fileSystem);
|
||||
fileMovedOutsideRepo.ShouldBeAFile(this.fileSystem);
|
||||
this.fileSystem.ReadAllText(fileMovedOutsideRepo).ShouldContain("test");
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, fileInsideRepoEntry);
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, fileNameOutsideRepo);
|
||||
}
|
||||
|
||||
[TestCase, Order(18)]
|
||||
public void HardlinkFromOutsideRepoToInside()
|
||||
{
|
||||
string fileName = "OutsideRepoToInside_FileForHardlink.txt";
|
||||
string fileOutsideRepo = Path.Combine(this.Enlistment.EnlistmentRoot, fileName);
|
||||
this.fileSystem.WriteAllText(fileOutsideRepo, "Contents for the new file");
|
||||
fileOutsideRepo.ShouldBeAFile(this.fileSystem);
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, fileName);
|
||||
|
||||
string fileNameLink = "OutsideRepoToInside_RepoLink.txt";
|
||||
string fileLinkInsideRepo = this.Enlistment.GetVirtualPathTo(fileNameLink);
|
||||
this.fileSystem.CreateHardLink(fileLinkInsideRepo, fileOutsideRepo);
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, fileNameLink);
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, fileName);
|
||||
fileLinkInsideRepo.ShouldBeAFile(this.fileSystem);
|
||||
}
|
||||
|
||||
[TestCase, Order(19)]
|
||||
public void HardlinkFromInsideRepoToOutside()
|
||||
{
|
||||
string fileName = "Readme.md";
|
||||
string fileInsideRepo = this.Enlistment.GetVirtualPathTo(fileName);
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, fileName);
|
||||
|
||||
string fileNameLink = "InsideRepoToOutside_RepoLink.txt";
|
||||
string fileLinkOutsideRepo = Path.Combine(this.Enlistment.EnlistmentRoot, fileNameLink);
|
||||
this.fileSystem.CreateHardLink(fileLinkOutsideRepo, fileInsideRepo);
|
||||
fileLinkOutsideRepo.ShouldBeAFile(this.fileSystem);
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, fileName);
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, fileNameLink);
|
||||
}
|
||||
|
||||
[TestCase, Order(20)]
|
||||
public void HardlinkInsideRepo()
|
||||
{
|
||||
string fileName = "InsideRepo_FileForHardlink.txt";
|
||||
string fileInsideRepo = this.Enlistment.GetVirtualPathTo(fileName);
|
||||
this.fileSystem.WriteAllText(fileInsideRepo, "Contents for the new file");
|
||||
fileInsideRepo.ShouldBeAFile(this.fileSystem);
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, fileName);
|
||||
|
||||
string fileNameLink = "InsideRepo_RepoLink.txt";
|
||||
string fileLinkInsideRepo = this.Enlistment.GetVirtualPathTo(fileNameLink);
|
||||
this.fileSystem.CreateHardLink(fileLinkInsideRepo, fileInsideRepo);
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, fileName);
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, fileNameLink);
|
||||
fileLinkInsideRepo.ShouldBeAFile(this.fileSystem);
|
||||
}
|
||||
|
||||
[TestCase, Order(21)]
|
||||
public void HardlinkExistingFileInRepo()
|
||||
{
|
||||
string fileName = "GVFS/GVFS.Mount/Program.cs";
|
||||
string fileNameLink = "HardLinkToReadme";
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, fileName);
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, fileNameLink);
|
||||
|
||||
string fileInsideRepo = this.Enlistment.GetVirtualPathTo(fileName);
|
||||
string fileLinkInsideRepo = this.Enlistment.GetVirtualPathTo(fileNameLink);
|
||||
this.fileSystem.CreateHardLink(fileLinkInsideRepo, fileInsideRepo);
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, fileName);
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, fileNameLink);
|
||||
fileInsideRepo.ShouldBeAFile(this.fileSystem);
|
||||
fileLinkInsideRepo.ShouldBeAFile(this.fileSystem);
|
||||
}
|
||||
|
||||
[DllImport("GVFS.NativeTests.dll", CharSet = CharSet.Unicode)]
|
||||
private static extern bool SupersedeFile(string path, [MarshalAs(UnmanagedType.LPStr)]string newContent);
|
||||
|
||||
private void VerifyWorktreeBit(string path, char expectedStatus)
|
||||
{
|
||||
ProcessResult lsfilesResult = GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "ls-files -svomdc " + path);
|
||||
lsfilesResult.ShouldNotBeNull();
|
||||
lsfilesResult.Output.ShouldNotBeNull();
|
||||
lsfilesResult.Output.Length.ShouldBeAtLeast(2);
|
||||
lsfilesResult.Output[0].ShouldEqual(expectedStatus);
|
||||
}
|
||||
|
||||
private static class LsFilesStatus
|
||||
{
|
||||
public const char Cached = 'H';
|
||||
public const char SkipWorktree = 'S';
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,134 +0,0 @@
|
|||
using GVFS.FunctionalTests.FileSystemRunners;
|
||||
using GVFS.FunctionalTests.Should;
|
||||
using GVFS.FunctionalTests.Tools;
|
||||
using GVFS.Tests.Should;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
||||
{
|
||||
[TestFixture]
|
||||
public class GitReadAndGitLockTests : TestsWithEnlistmentPerFixture
|
||||
{
|
||||
private const string ExpectedStatusWaitingText = @"Waiting for 'GVFS.FunctionalTests.LockHolder'";
|
||||
private FileSystemRunner fileSystem;
|
||||
|
||||
public GitReadAndGitLockTests()
|
||||
{
|
||||
this.fileSystem = new SystemIORunner();
|
||||
}
|
||||
|
||||
[TestCase, Order(1)]
|
||||
public void GitStatus()
|
||||
{
|
||||
GitHelpers.CheckGitCommandAgainstGVFSRepo(
|
||||
this.Enlistment.RepoRoot,
|
||||
"status",
|
||||
"On branch " + Properties.Settings.Default.Commitish,
|
||||
"nothing to commit, working tree clean");
|
||||
}
|
||||
|
||||
[TestCase, Order(2)]
|
||||
public void GitLog()
|
||||
{
|
||||
GitHelpers.CheckGitCommandAgainstGVFSRepo(this.Enlistment.RepoRoot, "log -n1", "commit", "Author:", "Date:");
|
||||
}
|
||||
|
||||
[TestCase, Order(3)]
|
||||
public void GitBranch()
|
||||
{
|
||||
GitHelpers.CheckGitCommandAgainstGVFSRepo(
|
||||
this.Enlistment.RepoRoot,
|
||||
"branch -a",
|
||||
"* " + Properties.Settings.Default.Commitish,
|
||||
"remotes/origin/" + Properties.Settings.Default.Commitish);
|
||||
}
|
||||
|
||||
[TestCase, Order(4)]
|
||||
public void GitCommandWaitsWhileAnotherIsRunning()
|
||||
{
|
||||
int pid;
|
||||
GitHelpers.AcquireGVFSLock(this.Enlistment, out pid, resetTimeout: 3000);
|
||||
|
||||
ProcessResult statusWait = GitHelpers.InvokeGitAgainstGVFSRepo(this.Enlistment.RepoRoot, "status", removeWaitingMessages: false);
|
||||
statusWait.Errors.ShouldContain(ExpectedStatusWaitingText);
|
||||
}
|
||||
|
||||
[TestCase, Order(5)]
|
||||
public void GitAliasNamedAfterKnownCommandAcquiresLock()
|
||||
{
|
||||
string alias = nameof(this.GitAliasNamedAfterKnownCommandAcquiresLock);
|
||||
|
||||
int pid;
|
||||
GitHelpers.AcquireGVFSLock(this.Enlistment, out pid, resetTimeout: 3000);
|
||||
GitHelpers.CheckGitCommandAgainstGVFSRepo(this.Enlistment.RepoRoot, "config --local alias." + alias + " status");
|
||||
ProcessResult statusWait = GitHelpers.InvokeGitAgainstGVFSRepo(this.Enlistment.RepoRoot, alias, removeWaitingMessages: false);
|
||||
statusWait.Errors.ShouldContain(ExpectedStatusWaitingText);
|
||||
}
|
||||
|
||||
[TestCase, Order(6)]
|
||||
public void GitAliasInSubfolderNamedAfterKnownCommandAcquiresLock()
|
||||
{
|
||||
string alias = nameof(this.GitAliasInSubfolderNamedAfterKnownCommandAcquiresLock);
|
||||
|
||||
int pid;
|
||||
GitHelpers.AcquireGVFSLock(this.Enlistment, out pid, resetTimeout: 3000);
|
||||
GitHelpers.CheckGitCommandAgainstGVFSRepo(this.Enlistment.RepoRoot, "config --local alias." + alias + " rebase");
|
||||
ProcessResult statusWait = GitHelpers.InvokeGitAgainstGVFSRepo(
|
||||
Path.Combine(this.Enlistment.RepoRoot, "GVFS"),
|
||||
alias + " origin/FunctionalTests/RebaseTestsSource_20170208",
|
||||
removeWaitingMessages: false);
|
||||
statusWait.Errors.ShouldContain(ExpectedStatusWaitingText);
|
||||
GitHelpers.CheckGitCommandAgainstGVFSRepo(this.Enlistment.RepoRoot, "rebase --abort");
|
||||
}
|
||||
|
||||
[TestCase, Order(7)]
|
||||
public void ExternalLockHolderReportedWhenBackgroundTasksArePending()
|
||||
{
|
||||
int pid;
|
||||
GitHelpers.AcquireGVFSLock(this.Enlistment, out pid, resetTimeout: 3000);
|
||||
|
||||
// Creating a new file will queue a background task
|
||||
string newFilePath = this.Enlistment.GetVirtualPathTo("ExternalLockHolderReportedWhenBackgroundTasksArePending.txt");
|
||||
newFilePath.ShouldNotExistOnDisk(this.fileSystem);
|
||||
this.fileSystem.WriteAllText(newFilePath, "New file contents");
|
||||
|
||||
ProcessResult statusWait = GitHelpers.InvokeGitAgainstGVFSRepo(this.Enlistment.RepoRoot, "status", removeWaitingMessages: false);
|
||||
|
||||
// Validate that GVFS still reports that the git command is holding the lock
|
||||
statusWait.Errors.ShouldContain(ExpectedStatusWaitingText);
|
||||
}
|
||||
|
||||
[TestCase, Order(8)]
|
||||
public void OrphanedGVFSLockIsCleanedUp()
|
||||
{
|
||||
int pid;
|
||||
GitHelpers.AcquireGVFSLock(this.Enlistment, out pid, resetTimeout: 1000, skipReleaseLock: true);
|
||||
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (Process.GetProcessById(pid))
|
||||
{
|
||||
}
|
||||
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ProcessResult statusWait = GitHelpers.InvokeGitAgainstGVFSRepo(this.Enlistment.RepoRoot, "status", removeWaitingMessages: false);
|
||||
|
||||
// There should not be any errors - in particular, there should not be
|
||||
// an error about "Waiting for GVFS.FunctionalTests.LockHolder"
|
||||
statusWait.Errors.ShouldEqual(string.Empty);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -296,30 +296,6 @@ namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
|||
this.Enlistment.MountGVFS();
|
||||
}
|
||||
|
||||
// Ported from ProjFS's BugRegressionTest
|
||||
[TestCase]
|
||||
[Category(Categories.WindowsOnly)]
|
||||
public void ProjFS_CMDHangNoneActiveInstance()
|
||||
{
|
||||
this.Enlistment.UnmountGVFS();
|
||||
|
||||
using (SafeFileHandle handle = NativeMethods.CreateFile(
|
||||
Path.Combine(this.Enlistment.RepoRoot, "aaa", "aaaa"),
|
||||
GenericRead,
|
||||
FileShare.Read,
|
||||
IntPtr.Zero,
|
||||
FileMode.Open,
|
||||
FileFlagBackupSemantics,
|
||||
IntPtr.Zero))
|
||||
{
|
||||
int lastError = Marshal.GetLastWin32Error();
|
||||
handle.IsInvalid.ShouldEqual(true);
|
||||
lastError.ShouldNotEqual(0); // 0 == ERROR_SUCCESS
|
||||
}
|
||||
|
||||
this.Enlistment.MountGVFS();
|
||||
}
|
||||
|
||||
private void MountShouldFail(int expectedExitCode, string expectedErrorMessage, string mountWorkingDirectory = null)
|
||||
{
|
||||
string enlistmentRoot = this.Enlistment.EnlistmentRoot;
|
||||
|
|
|
@ -1,152 +0,0 @@
|
|||
using GVFS.FunctionalTests.FileSystemRunners;
|
||||
using GVFS.FunctionalTests.Should;
|
||||
using GVFS.FunctionalTests.Tools;
|
||||
using GVFS.Tests.Should;
|
||||
using NUnit.Framework;
|
||||
using System.IO;
|
||||
|
||||
namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
||||
{
|
||||
// TODO 452590 - Combine all of the MoveRenameTests into a single fixture, and have each use different
|
||||
// well known files
|
||||
[TestFixtureSource(typeof(FileSystemRunner), nameof(FileSystemRunner.Runners))]
|
||||
public class MoveRenameFileTests : TestsWithEnlistmentPerFixture
|
||||
{
|
||||
public const string TestFileContents =
|
||||
@"using NUnitLite;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace GVFS.StressTests
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
string[] test_args = args;
|
||||
|
||||
for (int i = 0; i < Properties.Settings.Default.TestRepeatCount; i++)
|
||||
{
|
||||
Console.WriteLine(""Starting pass {0}"", i + 1);
|
||||
DateTime now = DateTime.Now;
|
||||
new AutoRun().Execute(test_args);
|
||||
Console.WriteLine(""Completed pass {0} in {1}"", i + 1, DateTime.Now - now);
|
||||
Console.WriteLine();
|
||||
|
||||
Thread.Sleep(TimeSpan.FromSeconds(1));
|
||||
}
|
||||
|
||||
Console.WriteLine(""All tests completed. Press Enter to exit."");
|
||||
Console.ReadLine();
|
||||
}
|
||||
}
|
||||
}";
|
||||
|
||||
private FileSystemRunner fileSystem;
|
||||
|
||||
public MoveRenameFileTests(FileSystemRunner fileSystem)
|
||||
{
|
||||
this.fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void ChangeUnhydratedFileName()
|
||||
{
|
||||
string oldFilename = Path.Combine("Test_EPF_MoveRenameFileTests", "ChangeUnhydratedFileName", "Program.cs");
|
||||
string newFilename = Path.Combine("Test_EPF_MoveRenameFileTests", "ChangeUnhydratedFileName", "renamed_Program.cs");
|
||||
|
||||
// Don't read oldFilename or check for its existence before calling MoveFile, because doing so
|
||||
// can cause the file to hydrate
|
||||
this.Enlistment.GetVirtualPathTo(newFilename).ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
this.fileSystem.MoveFile(this.Enlistment.GetVirtualPathTo(oldFilename), this.Enlistment.GetVirtualPathTo(newFilename));
|
||||
this.Enlistment.GetVirtualPathTo(newFilename).ShouldBeAFile(this.fileSystem).WithContents(TestFileContents);
|
||||
this.Enlistment.GetVirtualPathTo(oldFilename).ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
this.fileSystem.MoveFile(this.Enlistment.GetVirtualPathTo(newFilename), this.Enlistment.GetVirtualPathTo(oldFilename));
|
||||
this.Enlistment.GetVirtualPathTo(oldFilename).ShouldBeAFile(this.fileSystem).WithContents(TestFileContents);
|
||||
this.Enlistment.GetVirtualPathTo(newFilename).ShouldNotExistOnDisk(this.fileSystem);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void ChangeUnhydratedFileNameCase()
|
||||
{
|
||||
string oldName = "Readme.md";
|
||||
string newName = "readme.md";
|
||||
|
||||
string oldVirtualPath = this.Enlistment.GetVirtualPathTo(oldName);
|
||||
string newVirtualPath = this.Enlistment.GetVirtualPathTo(newName);
|
||||
|
||||
this.ChangeUnhydratedFileCase(oldName, oldVirtualPath, newName, newVirtualPath, knownFileContents: null);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void ChangeNestedUnhydratedFileNameCase()
|
||||
{
|
||||
string oldName = "Program.cs";
|
||||
string newName = "program.cs";
|
||||
string folderName = Path.Combine("Test_EPF_MoveRenameFileTests", "ChangeNestedUnhydratedFileNameCase");
|
||||
|
||||
string oldVirtualPath = this.Enlistment.GetVirtualPathTo(Path.Combine(folderName, oldName));
|
||||
string newVirtualPath = this.Enlistment.GetVirtualPathTo(Path.Combine(folderName, newName));
|
||||
|
||||
this.ChangeUnhydratedFileCase(oldName, oldVirtualPath, newName, newVirtualPath, TestFileContents);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void MoveUnhydratedFileToDotGitFolder()
|
||||
{
|
||||
string targetFolderName = ".git";
|
||||
this.Enlistment.GetVirtualPathTo(targetFolderName).ShouldBeADirectory(this.fileSystem);
|
||||
|
||||
string testFileName = "Program.cs";
|
||||
string testFileFolder = Path.Combine("Test_EPF_MoveRenameFileTests", "MoveUnhydratedFileToDotGitFolder");
|
||||
string testFilePathSubPath = Path.Combine(testFileFolder, testFileName);
|
||||
|
||||
string newTestFileVirtualPath = Path.Combine(this.Enlistment.GetVirtualPathTo(targetFolderName), testFileName);
|
||||
|
||||
this.fileSystem.MoveFile(this.Enlistment.GetVirtualPathTo(testFilePathSubPath), newTestFileVirtualPath);
|
||||
this.Enlistment.GetVirtualPathTo(testFilePathSubPath).ShouldNotExistOnDisk(this.fileSystem);
|
||||
newTestFileVirtualPath.ShouldBeAFile(this.fileSystem).WithContents(TestFileContents);
|
||||
|
||||
this.fileSystem.DeleteFile(newTestFileVirtualPath);
|
||||
newTestFileVirtualPath.ShouldNotExistOnDisk(this.fileSystem);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void MoveVirtualNTFSFileToOverwriteUnhydratedFile()
|
||||
{
|
||||
string targetFilename = ".gitattributes";
|
||||
|
||||
string sourceFilename = "SourceFile.txt";
|
||||
string sourceFileContents = "The Source";
|
||||
|
||||
this.fileSystem.WriteAllText(this.Enlistment.GetVirtualPathTo(sourceFilename), sourceFileContents);
|
||||
this.Enlistment.GetVirtualPathTo(sourceFilename).ShouldBeAFile(this.fileSystem).WithContents(sourceFileContents);
|
||||
|
||||
this.fileSystem.ReplaceFile(this.Enlistment.GetVirtualPathTo(sourceFilename), this.Enlistment.GetVirtualPathTo(targetFilename));
|
||||
this.Enlistment.GetVirtualPathTo(targetFilename).ShouldBeAFile(this.fileSystem).WithContents(sourceFileContents);
|
||||
|
||||
this.Enlistment.GetVirtualPathTo(sourceFilename).ShouldNotExistOnDisk(this.fileSystem);
|
||||
}
|
||||
|
||||
private void ChangeUnhydratedFileCase(
|
||||
string oldName,
|
||||
string oldVirtualPath,
|
||||
string newName,
|
||||
string newVirtualPath,
|
||||
string knownFileContents)
|
||||
{
|
||||
this.fileSystem.MoveFile(oldVirtualPath, newVirtualPath);
|
||||
string fileContents = newVirtualPath.ShouldBeAFile(this.fileSystem).WithCaseMatchingName(newName).WithContents();
|
||||
fileContents.ShouldBeNonEmpty();
|
||||
if (knownFileContents != null)
|
||||
{
|
||||
fileContents.ShouldEqual(knownFileContents);
|
||||
}
|
||||
|
||||
this.fileSystem.MoveFile(newVirtualPath, oldVirtualPath);
|
||||
oldVirtualPath.ShouldBeAFile(this.fileSystem).WithCaseMatchingName(oldName).WithContents(fileContents);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,161 +0,0 @@
|
|||
using GVFS.FunctionalTests.FileSystemRunners;
|
||||
using GVFS.FunctionalTests.Should;
|
||||
using NUnit.Framework;
|
||||
using System.IO;
|
||||
|
||||
namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
||||
{
|
||||
// TODO 452590 - Combine all of the MoveRenameTests into a single fixture, and have each use different
|
||||
// well known files
|
||||
[TestFixtureSource(typeof(FileSystemRunner), nameof(FileSystemRunner.Runners))]
|
||||
public class MoveRenameFileTests_2 : TestsWithEnlistmentPerFixture
|
||||
{
|
||||
private const string TestFileFolder = "Test_EPF_MoveRenameFileTests_2";
|
||||
|
||||
// Test_EPF_MoveRenameFileTests_2\RunUnitTests.bat
|
||||
private const string RunUnitTestsContents =
|
||||
@"@ECHO OFF
|
||||
IF ""%1""=="""" (SET ""Configuration=Debug"") ELSE (SET ""Configuration=%1"")
|
||||
|
||||
%~dp0\..\..\BuildOutput\GVFS.UnitTests\bin\x64\%Configuration%\GVFS.UnitTests.exe";
|
||||
|
||||
// Test_EPF_MoveRenameFileTests_2\RunFunctionalTests.bat
|
||||
private const string RunFunctioanlTestsContents =
|
||||
@"@ECHO OFF
|
||||
IF ""%1""=="""" (SET ""Configuration=Debug"") ELSE (SET ""Configuration=%1"")
|
||||
|
||||
%~dp0\..\..\BuildOutput\GVFS.FunctionalTests\bin\x64\%Configuration%\GVFS.FunctionalTests.exe %2";
|
||||
|
||||
private FileSystemRunner fileSystem;
|
||||
|
||||
public MoveRenameFileTests_2(FileSystemRunner fileSystem)
|
||||
{
|
||||
this.fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
// This test needs the GVFS folder to not exist on physical disk yet, so run it first
|
||||
[TestCase, Order(1)]
|
||||
public void MoveUnhydratedFileToUnhydratedFolderAndWrite()
|
||||
{
|
||||
string testFileContents = RunUnitTestsContents;
|
||||
string testFileName = "RunUnitTests.bat";
|
||||
|
||||
// Assume there will always be a GVFS folder when running tests
|
||||
string testFolderName = "GVFS";
|
||||
|
||||
string oldTestFileVirtualPath = this.Enlistment.GetVirtualPathTo(TestFileFolder, testFileName);
|
||||
string newTestFileVirtualPath = this.Enlistment.GetVirtualPathTo(testFolderName, testFileName);
|
||||
|
||||
this.fileSystem.MoveFile(oldTestFileVirtualPath, newTestFileVirtualPath);
|
||||
oldTestFileVirtualPath.ShouldNotExistOnDisk(this.fileSystem);
|
||||
newTestFileVirtualPath.ShouldBeAFile(this.fileSystem).WithContents(testFileContents);
|
||||
this.Enlistment.GetVirtualPathTo(testFolderName).ShouldBeADirectory(this.fileSystem);
|
||||
|
||||
// Writing after the move should succeed
|
||||
string newText = "New file text for test file";
|
||||
this.fileSystem.WriteAllText(newTestFileVirtualPath, newText);
|
||||
newTestFileVirtualPath.ShouldBeAFile(this.fileSystem).WithContents(newText);
|
||||
}
|
||||
|
||||
[TestCase, Order(2)]
|
||||
public void MoveUnhydratedFileToNewFolderAndWrite()
|
||||
{
|
||||
string testFolderName = "test_folder";
|
||||
this.Enlistment.GetVirtualPathTo(testFolderName).ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
this.fileSystem.CreateDirectory(this.Enlistment.GetVirtualPathTo(testFolderName));
|
||||
this.Enlistment.GetVirtualPathTo(testFolderName).ShouldBeADirectory(this.fileSystem);
|
||||
|
||||
string testFileName = "RunFunctionalTests.bat";
|
||||
string testFileContents = RunFunctioanlTestsContents;
|
||||
|
||||
string newTestFileVirtualPath = Path.Combine(this.Enlistment.GetVirtualPathTo(testFolderName), testFolderName);
|
||||
|
||||
this.fileSystem.MoveFile(this.Enlistment.GetVirtualPathTo(TestFileFolder, testFileName), newTestFileVirtualPath);
|
||||
this.Enlistment.GetVirtualPathTo(TestFileFolder, testFileName).ShouldNotExistOnDisk(this.fileSystem);
|
||||
newTestFileVirtualPath.ShouldBeAFile(this.fileSystem).WithContents(testFileContents);
|
||||
|
||||
// Writing after the move should succeed
|
||||
string newText = "New file text for test file";
|
||||
this.fileSystem.WriteAllText(newTestFileVirtualPath, newText);
|
||||
newTestFileVirtualPath.ShouldBeAFile(this.fileSystem);
|
||||
newTestFileVirtualPath.ShouldBeAFile(this.fileSystem).WithContents(newText);
|
||||
|
||||
this.fileSystem.DeleteFile(newTestFileVirtualPath);
|
||||
newTestFileVirtualPath.ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
this.fileSystem.DeleteDirectory(this.Enlistment.GetVirtualPathTo(testFolderName));
|
||||
this.Enlistment.GetVirtualPathTo(testFolderName).ShouldNotExistOnDisk(this.fileSystem);
|
||||
}
|
||||
|
||||
[TestCase, Order(3)]
|
||||
public void MoveUnhydratedFileToOverwriteUnhydratedFileAndWrite()
|
||||
{
|
||||
string targetFilename = Path.Combine(TestFileFolder, "MoveUnhydratedFileToOverwriteUnhydratedFileAndWrite", "RunFunctionalTests.bat");
|
||||
string sourceFilename = Path.Combine(TestFileFolder, "MoveUnhydratedFileToOverwriteUnhydratedFileAndWrite", "RunUnitTests.bat");
|
||||
string sourceFileContents = RunUnitTestsContents;
|
||||
|
||||
// Overwriting one unhydrated file with another should create a file at the target
|
||||
this.fileSystem.ReplaceFile(this.Enlistment.GetVirtualPathTo(sourceFilename), this.Enlistment.GetVirtualPathTo(targetFilename));
|
||||
this.Enlistment.GetVirtualPathTo(targetFilename).ShouldBeAFile(this.fileSystem).WithContents(sourceFileContents);
|
||||
|
||||
// Source file should be gone
|
||||
this.Enlistment.GetVirtualPathTo(sourceFilename).ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
// Writing after move should succeed
|
||||
string newText = "New file text for target file";
|
||||
this.fileSystem.WriteAllText(this.Enlistment.GetVirtualPathTo(targetFilename), newText);
|
||||
this.Enlistment.GetVirtualPathTo(targetFilename).ShouldBeAFile(this.fileSystem).WithContents(newText);
|
||||
}
|
||||
|
||||
[TestCase, Order(4)]
|
||||
public void CaseOnlyRenameFileInSubfolder()
|
||||
{
|
||||
string oldFilename = "CaseOnlyRenameFileInSubfolder.txt";
|
||||
string oldVirtualPath = this.Enlistment.GetVirtualPathTo(Path.Combine(TestFileFolder, oldFilename));
|
||||
oldVirtualPath.ShouldBeAFile(this.fileSystem).WithCaseMatchingName(oldFilename);
|
||||
|
||||
string newFilename = "caseonlyrenamefileinsubfolder.txt";
|
||||
string newVirtualPath = this.Enlistment.GetVirtualPathTo(Path.Combine(TestFileFolder, newFilename));
|
||||
|
||||
// Rename file, and confirm file name case was updated
|
||||
this.fileSystem.MoveFile(oldVirtualPath, newVirtualPath);
|
||||
newVirtualPath.ShouldBeAFile(this.fileSystem).WithCaseMatchingName(newFilename);
|
||||
}
|
||||
|
||||
[TestCase, Order(5)]
|
||||
public void MoveUnhydratedFileToOverwriteFullFileAndWrite()
|
||||
{
|
||||
string targetFilename = "TargetFile.txt";
|
||||
string targetFileContents = "The Target";
|
||||
|
||||
string sourceFilename = Path.Combine(
|
||||
TestFileFolder,
|
||||
"MoveUnhydratedFileToOverwriteFullFileAndWrite",
|
||||
"MoveUnhydratedFileToOverwriteFullFileAndWrite.txt");
|
||||
|
||||
string sourceFileContents =
|
||||
@"<?xml version=""1.0"" encoding=""utf-8""?>
|
||||
<packages>
|
||||
<package id=""NUnit"" version=""3.5.0"" targetFramework=""net452"" />
|
||||
<package id=""NUnitLite"" version=""3.5.0"" targetFramework=""net452"" />
|
||||
</packages>";
|
||||
|
||||
this.fileSystem.WriteAllText(this.Enlistment.GetVirtualPathTo(targetFilename), targetFileContents);
|
||||
this.Enlistment.GetVirtualPathTo(targetFilename).ShouldBeAFile(this.fileSystem).WithContents(targetFileContents);
|
||||
|
||||
// Overwriting a virtual NTFS file with an unprojected file should leave a file on disk at the
|
||||
// target location
|
||||
this.fileSystem.ReplaceFile(this.Enlistment.GetVirtualPathTo(sourceFilename), this.Enlistment.GetVirtualPathTo(targetFilename));
|
||||
this.Enlistment.GetVirtualPathTo(targetFilename).ShouldBeAFile(this.fileSystem).WithContents(sourceFileContents);
|
||||
|
||||
// Source file should be gone
|
||||
this.Enlistment.GetVirtualPathTo(sourceFilename).ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
// Writes should succeed after move
|
||||
string newText = "New file text for Readme.md";
|
||||
this.fileSystem.WriteAllText(this.Enlistment.GetVirtualPathTo(targetFilename), newText);
|
||||
this.Enlistment.GetVirtualPathTo(targetFilename).ShouldBeAFile(this.fileSystem).WithContents(newText);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,248 +0,0 @@
|
|||
using GVFS.FunctionalTests.FileSystemRunners;
|
||||
using GVFS.FunctionalTests.Should;
|
||||
using GVFS.Tests.Should;
|
||||
using NUnit.Framework;
|
||||
using System.IO;
|
||||
|
||||
namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
||||
{
|
||||
[TestFixtureSource(typeof(FileSystemRunner), nameof(FileSystemRunner.Runners))]
|
||||
public class MoveRenameFolderTests : TestsWithEnlistmentPerFixture
|
||||
{
|
||||
private const string TestFileContents =
|
||||
@"// dllmain.cpp : Defines the entry point for the DLL application.
|
||||
#include ""stdafx.h""
|
||||
|
||||
BOOL APIENTRY DllMain( HMODULE hModule,
|
||||
DWORD ul_reason_for_call,
|
||||
LPVOID lpReserved
|
||||
)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(hModule);
|
||||
UNREFERENCED_PARAMETER(lpReserved);
|
||||
|
||||
switch (ul_reason_for_call)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
case DLL_PROCESS_DETACH:
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
";
|
||||
private FileSystemRunner fileSystem;
|
||||
|
||||
public MoveRenameFolderTests(FileSystemRunner fileSystem)
|
||||
{
|
||||
this.fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
// WindowsOnly because renames of partial folders are blocked only on Windows
|
||||
[TestCase]
|
||||
[Category(Categories.WindowsOnly)]
|
||||
public void RenameFolderShouldFail()
|
||||
{
|
||||
string testFileName = "RenameFolderShouldFail.cpp";
|
||||
string oldFolderName = Path.Combine("Test_EPF_MoveRenameFolderTests", "RenameFolderShouldFail", "source");
|
||||
string newFolderName = Path.Combine("Test_EPF_MoveRenameFolderTests", "RenameFolderShouldFail", "sourcerenamed");
|
||||
this.Enlistment.GetVirtualPathTo(newFolderName).ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
this.fileSystem.MoveDirectory_RequestShouldNotBeSupported(this.Enlistment.GetVirtualPathTo(oldFolderName), this.Enlistment.GetVirtualPathTo(newFolderName));
|
||||
|
||||
this.Enlistment.GetVirtualPathTo(oldFolderName).ShouldBeADirectory(this.fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(newFolderName).ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
this.Enlistment.GetVirtualPathTo(Path.Combine(newFolderName, testFileName)).ShouldNotExistOnDisk(this.fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(Path.Combine(oldFolderName, testFileName)).ShouldBeAFile(this.fileSystem).WithContents(TestFileContents);
|
||||
}
|
||||
|
||||
// MacOnly because renames of partial folders are blocked on Windows
|
||||
[TestCase]
|
||||
[Category(Categories.MacOnly)]
|
||||
public void ChangeUnhydratedFolderName()
|
||||
{
|
||||
string testFileName = "ChangeUnhydratedFolderName.cpp";
|
||||
string oldFolderName = Path.Combine("Test_EPF_MoveRenameFolderTests", "ChangeUnhydratedFolderName", "source");
|
||||
string newFolderName = Path.Combine("Test_EPF_MoveRenameFolderTests", "ChangeUnhydratedFolderName", "source_renamed");
|
||||
this.Enlistment.GetVirtualPathTo(newFolderName).ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
this.fileSystem.MoveDirectory(this.Enlistment.GetVirtualPathTo(oldFolderName), this.Enlistment.GetVirtualPathTo(newFolderName));
|
||||
|
||||
this.Enlistment.GetVirtualPathTo(newFolderName).ShouldBeADirectory(this.fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(oldFolderName).ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
this.Enlistment.GetVirtualPathTo(Path.Combine(oldFolderName, testFileName)).ShouldNotExistOnDisk(this.fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(Path.Combine(newFolderName, testFileName)).ShouldBeAFile(this.fileSystem).WithContents(TestFileContents);
|
||||
}
|
||||
|
||||
// MacOnly because renames of partial folders are blocked on Windows
|
||||
[TestCase]
|
||||
[Category(Categories.MacOnly)]
|
||||
public void MoveUnhydratedFolderToNewFolder()
|
||||
{
|
||||
string testFileName = "MoveUnhydratedFolderToVirtualNTFSFolder.cpp";
|
||||
string oldFolderName = Path.Combine("Test_EPF_MoveRenameFolderTests", "MoveUnhydratedFolderToVirtualNTFSFolder");
|
||||
|
||||
string newFolderName = "NewPerFixtureParent";
|
||||
this.Enlistment.GetVirtualPathTo(newFolderName).ShouldNotExistOnDisk(this.fileSystem);
|
||||
this.fileSystem.CreateDirectory(this.Enlistment.GetVirtualPathTo(newFolderName));
|
||||
this.Enlistment.GetVirtualPathTo(newFolderName).ShouldBeADirectory(this.fileSystem);
|
||||
|
||||
string movedFolderPath = Path.Combine(newFolderName, "EnlistmentPerFixture");
|
||||
this.fileSystem.MoveDirectory(this.Enlistment.GetVirtualPathTo(oldFolderName), this.Enlistment.GetVirtualPathTo(movedFolderPath));
|
||||
|
||||
this.Enlistment.GetVirtualPathTo(movedFolderPath).ShouldBeADirectory(this.fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(oldFolderName).ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
this.Enlistment.GetVirtualPathTo(Path.Combine(oldFolderName, testFileName)).ShouldNotExistOnDisk(this.fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(Path.Combine(movedFolderPath, testFileName)).ShouldBeAFile(this.fileSystem).WithContents(TestFileContents);
|
||||
}
|
||||
|
||||
// MacOnly because renames of partial folders are blocked on Windows
|
||||
[TestCase]
|
||||
[Category(Categories.MacOnly)]
|
||||
public void MoveUnhydratedFolderToFullFolderInDotGitFolder()
|
||||
{
|
||||
string testFileName = "MoveUnhydratedFolderToFullFolderInDotGitFolder.cpp";
|
||||
string oldFolderName = Path.Combine("Test_EPF_MoveRenameFolderTests", "MoveUnhydratedFolderToFullFolderInDotGitFolder");
|
||||
|
||||
string newFolderName = Path.Combine(".git", "NewPerFixtureParent");
|
||||
this.Enlistment.GetVirtualPathTo(newFolderName).ShouldNotExistOnDisk(this.fileSystem);
|
||||
this.fileSystem.CreateDirectory(this.Enlistment.GetVirtualPathTo(newFolderName));
|
||||
this.Enlistment.GetVirtualPathTo(newFolderName).ShouldBeADirectory(this.fileSystem);
|
||||
|
||||
string movedFolderPath = Path.Combine(newFolderName, "Should");
|
||||
this.fileSystem.MoveDirectory(this.Enlistment.GetVirtualPathTo(oldFolderName), this.Enlistment.GetVirtualPathTo(movedFolderPath));
|
||||
|
||||
this.Enlistment.GetVirtualPathTo(movedFolderPath).ShouldBeADirectory(this.fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(oldFolderName).ShouldNotExistOnDisk(this.fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(Path.Combine(oldFolderName, testFileName)).ShouldNotExistOnDisk(this.fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(Path.Combine(movedFolderPath, testFileName)).ShouldBeAFile(this.fileSystem).WithContents(TestFileContents);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void MoveFullFolderToFullFolderInDotGitFolder()
|
||||
{
|
||||
string fileContents = "Test contents for MoveFullFolderToFullFolderInDotGitFolder";
|
||||
string testFileName = "MoveFullFolderToFullFolderInDotGitFolder.txt";
|
||||
string oldFolderPath = this.Enlistment.GetVirtualPathTo("MoveFullFolderToFullFolderInDotGitFolder");
|
||||
oldFolderPath.ShouldNotExistOnDisk(this.fileSystem);
|
||||
this.fileSystem.CreateDirectory(oldFolderPath);
|
||||
oldFolderPath.ShouldBeADirectory(this.fileSystem);
|
||||
|
||||
string oldFilePath = Path.Combine(oldFolderPath, testFileName);
|
||||
this.fileSystem.WriteAllText(oldFilePath, fileContents);
|
||||
oldFilePath.ShouldBeAFile(this.fileSystem).WithContents(fileContents);
|
||||
|
||||
string newFolderName = "NewMoveFullFolderToFullFolderInDotGitFolder";
|
||||
string newFolderPath = this.Enlistment.GetVirtualPathTo(".git", newFolderName);
|
||||
newFolderPath.ShouldNotExistOnDisk(this.fileSystem);
|
||||
this.fileSystem.CreateDirectory(newFolderPath);
|
||||
newFolderPath.ShouldBeADirectory(this.fileSystem);
|
||||
|
||||
string movedFolderPath = Path.Combine(newFolderPath, "Should");
|
||||
this.fileSystem.MoveDirectory(oldFolderPath, movedFolderPath);
|
||||
|
||||
Path.Combine(movedFolderPath).ShouldBeADirectory(this.fileSystem);
|
||||
oldFolderPath.ShouldNotExistOnDisk(this.fileSystem);
|
||||
Path.Combine(movedFolderPath, testFileName).ShouldBeAFile(this.fileSystem).WithContents(fileContents);
|
||||
}
|
||||
|
||||
// MacOnly because renames of partial folders are blocked on Windows
|
||||
[TestCase]
|
||||
[Category(Categories.MacOnly)]
|
||||
public void MoveAndRenameUnhydratedFolderToNewFolder()
|
||||
{
|
||||
string testFileName = "MoveAndRenameUnhydratedFolderToNewFolder.cpp";
|
||||
string oldFolderName = Path.Combine("Test_EPF_MoveRenameFolderTests", "MoveAndRenameUnhydratedFolderToNewFolder");
|
||||
|
||||
string newFolderName = "NewPerTestCaseParent";
|
||||
this.Enlistment.GetVirtualPathTo(newFolderName).ShouldNotExistOnDisk(this.fileSystem);
|
||||
this.fileSystem.CreateDirectory(this.Enlistment.GetVirtualPathTo(newFolderName));
|
||||
this.Enlistment.GetVirtualPathTo(newFolderName).ShouldBeADirectory(this.fileSystem);
|
||||
|
||||
string movedFolderPath = Path.Combine(newFolderName, "MoveAndRenameUnhydratedFolderToNewFolder_renamed");
|
||||
this.fileSystem.MoveDirectory(this.Enlistment.GetVirtualPathTo(oldFolderName), this.Enlistment.GetVirtualPathTo(movedFolderPath));
|
||||
|
||||
this.Enlistment.GetVirtualPathTo(movedFolderPath).ShouldBeADirectory(this.fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(oldFolderName).ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
this.Enlistment.GetVirtualPathTo(Path.Combine(oldFolderName, testFileName)).ShouldNotExistOnDisk(this.fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(Path.Combine(movedFolderPath, testFileName)).ShouldBeAFile(this.fileSystem).WithContents(TestFileContents);
|
||||
}
|
||||
|
||||
// MacOnly because renames of partial folders are blocked on Windows
|
||||
[TestCase]
|
||||
[Category(Categories.MacOnly)]
|
||||
public void MoveFolderWithUnhydratedAndFullContents()
|
||||
{
|
||||
string testFileName = "MoveFolderWithUnhydratedAndFullContents.cpp";
|
||||
string oldFolderName = Path.Combine("Test_EPF_MoveRenameFolderTests", "MoveFolderWithUnhydratedAndFullContents");
|
||||
|
||||
string newFile = "TestFile.txt";
|
||||
string newFileContents = "Contents of TestFile.txt";
|
||||
this.fileSystem.WriteAllText(this.Enlistment.GetVirtualPathTo(Path.Combine(oldFolderName, newFile)), newFileContents);
|
||||
|
||||
string newFolderName = "New_MoveFolderWithUnhydratedAndFullContents";
|
||||
this.Enlistment.GetVirtualPathTo(newFolderName).ShouldNotExistOnDisk(this.fileSystem);
|
||||
this.fileSystem.CreateDirectory(this.Enlistment.GetVirtualPathTo(newFolderName));
|
||||
this.Enlistment.GetVirtualPathTo(newFolderName).ShouldBeADirectory(this.fileSystem);
|
||||
|
||||
string movedFolderPath = Path.Combine(newFolderName, "MoveFolderWithUnhydratedAndFullContents_renamed");
|
||||
this.fileSystem.MoveDirectory(this.Enlistment.GetVirtualPathTo(oldFolderName), this.Enlistment.GetVirtualPathTo(movedFolderPath));
|
||||
|
||||
this.Enlistment.GetVirtualPathTo(movedFolderPath).ShouldBeADirectory(this.fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(oldFolderName).ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
// Test file should have been moved
|
||||
this.Enlistment.GetVirtualPathTo(Path.Combine(oldFolderName, testFileName)).ShouldNotExistOnDisk(this.fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(Path.Combine(movedFolderPath, testFileName)).ShouldBeAFile(this.fileSystem).WithContents(TestFileContents);
|
||||
|
||||
// New file should have been moved
|
||||
this.Enlistment.GetVirtualPathTo(Path.Combine(oldFolderName, newFile)).ShouldNotExistOnDisk(this.fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(Path.Combine(movedFolderPath, newFile)).ShouldBeAFile(this.fileSystem).WithContents(newFileContents);
|
||||
}
|
||||
|
||||
// MacOnly because renames of partial folders are blocked on Windows
|
||||
[TestCase]
|
||||
[Category(Categories.MacOnly)]
|
||||
public void MoveFolderWithUnexpandedChildFolders()
|
||||
{
|
||||
string oldFolderPath = this.Enlistment.GetVirtualPathTo("Test_EPF_MoveRenameFileTests");
|
||||
string newFolderName = "Test_EPF_MoveRenameFileTests_Renamed";
|
||||
string newFolderPath = this.Enlistment.GetVirtualPathTo(newFolderName);
|
||||
this.fileSystem.MoveDirectory(oldFolderPath, newFolderPath);
|
||||
oldFolderPath.ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
newFolderPath.ShouldBeADirectory(this.fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(newFolderName, "ChangeNestedUnhydratedFileNameCase", "Program.cs")
|
||||
.ShouldBeAFile(this.fileSystem)
|
||||
.WithContents(MoveRenameFileTests.TestFileContents);
|
||||
|
||||
this.Enlistment.GetVirtualPathTo(newFolderName, "ChangeUnhydratedFileName", "Program.cs")
|
||||
.ShouldBeAFile(this.fileSystem)
|
||||
.WithContents(MoveRenameFileTests.TestFileContents);
|
||||
|
||||
this.Enlistment.GetVirtualPathTo(newFolderName, "MoveUnhydratedFileToDotGitFolder", "Program.cs")
|
||||
.ShouldBeAFile(this.fileSystem)
|
||||
.WithContents(MoveRenameFileTests.TestFileContents);
|
||||
|
||||
// Test moving a folder with a very deep unhydrated child tree
|
||||
oldFolderPath = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests");
|
||||
|
||||
// But expand the folder we will be renaming (so that only the children have not been expanded)
|
||||
oldFolderPath.ShouldBeADirectory(this.fileSystem).WithDirectories().ShouldContain(dir => dir.Name.Equals("1"));
|
||||
|
||||
newFolderName = "Test_EPF_WorkingDirectoryTests_Renamed";
|
||||
newFolderPath = this.Enlistment.GetVirtualPathTo(newFolderName);
|
||||
this.fileSystem.MoveDirectory(oldFolderPath, newFolderPath);
|
||||
oldFolderPath.ShouldNotExistOnDisk(this.fileSystem);
|
||||
this.Enlistment.GetVirtualPathTo(newFolderName, "1", "2", "3", "4", "ReadDeepProjectedFile.cpp")
|
||||
.ShouldBeAFile(this.fileSystem)
|
||||
.WithContents(WorkingDirectoryTests.TestFileContents);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,184 +0,0 @@
|
|||
using GVFS.FunctionalTests.FileSystemRunners;
|
||||
using GVFS.FunctionalTests.Should;
|
||||
using GVFS.Tests.Should;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
||||
{
|
||||
// TODO 469238: Elaborate on these tests?
|
||||
[TestFixture]
|
||||
public class MultithreadedReadWriteTests : TestsWithEnlistmentPerFixture
|
||||
{
|
||||
[TestCase, Order(1)]
|
||||
public void CanReadVirtualFileInParallel()
|
||||
{
|
||||
// Note: This test MUST go first, or else it needs to ensure that it is reading a unique path compared to the
|
||||
// other tests in this class. That applies to every directory in the path, as well as the leaf file name.
|
||||
// Otherwise, this test loses most of its value because there will be no races occurring on creating the
|
||||
// placeholder directories, enumerating them, and then creating a placeholder file and hydrating it.
|
||||
|
||||
string fileName = Path.Combine("GVFS", "GVFS.FunctionalTests", "Tests", "LongRunningEnlistment", "GitMoveRenameTests.cs");
|
||||
string virtualPath = this.Enlistment.GetVirtualPathTo(fileName);
|
||||
|
||||
Exception readException = null;
|
||||
|
||||
Thread[] threads = new Thread[128];
|
||||
for (int i = 0; i < threads.Length; ++i)
|
||||
{
|
||||
threads[i] = new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
FileSystemRunner.DefaultRunner.ReadAllText(virtualPath).ShouldBeNonEmpty();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
readException = e;
|
||||
}
|
||||
});
|
||||
|
||||
threads[i].Start();
|
||||
}
|
||||
|
||||
for (int i = 0; i < threads.Length; ++i)
|
||||
{
|
||||
threads[i].Join();
|
||||
}
|
||||
|
||||
readException.ShouldBeNull("At least one of the reads failed");
|
||||
}
|
||||
|
||||
[TestCase, Order(2)]
|
||||
public void CanReadHydratedPlaceholderInParallel()
|
||||
{
|
||||
FileSystemRunner fileSystem = FileSystemRunner.DefaultRunner;
|
||||
string fileName = Path.Combine("GVFS", "GVFS.FunctionalTests", "Tests", "LongRunningEnlistment", "WorkingDirectoryTests.cs");
|
||||
string virtualPath = this.Enlistment.GetVirtualPathTo(fileName);
|
||||
virtualPath.ShouldBeAFile(fileSystem);
|
||||
|
||||
// Not using the runner because reading specific bytes isn't common
|
||||
// Can't use ReadAllText because it will remove some bytes that the stream won't.
|
||||
byte[] actualContents = File.ReadAllBytes(virtualPath);
|
||||
|
||||
Thread[] threads = new Thread[4];
|
||||
|
||||
// Readers
|
||||
bool keepRunning = true;
|
||||
for (int i = 0; i < threads.Length; ++i)
|
||||
{
|
||||
int myIndex = i;
|
||||
threads[i] = new Thread(() =>
|
||||
{
|
||||
// Create random seeks (seeded for repeatability)
|
||||
Random randy = new Random(myIndex);
|
||||
|
||||
// Small buffer so we hit the drive a lot.
|
||||
// Block larger than the buffer to hit the drive more
|
||||
const int SmallBufferSize = 128;
|
||||
const int LargerBlockSize = SmallBufferSize * 10;
|
||||
|
||||
using (Stream reader = new FileStream(virtualPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, SmallBufferSize, false))
|
||||
{
|
||||
while (keepRunning)
|
||||
{
|
||||
byte[] block = new byte[LargerBlockSize];
|
||||
|
||||
// Always try to grab a full block (easier for asserting)
|
||||
int position = randy.Next((int)reader.Length - block.Length - 1);
|
||||
|
||||
reader.Position = position;
|
||||
reader.Read(block, 0, block.Length).ShouldEqual(block.Length);
|
||||
block.ShouldEqual(actualContents, position, block.Length);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
threads[i].Start();
|
||||
}
|
||||
|
||||
Thread.Sleep(2500);
|
||||
keepRunning = false;
|
||||
|
||||
for (int i = 0; i < threads.Length; ++i)
|
||||
{
|
||||
threads[i].Join();
|
||||
}
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileSystemRunner), nameof(FileSystemRunner.Runners))]
|
||||
[Order(3)]
|
||||
public void CanReadWriteAFileInParallel(FileSystemRunner fileSystem)
|
||||
{
|
||||
string fileName = @"CanReadWriteAFileInParallel";
|
||||
string virtualPath = this.Enlistment.GetVirtualPathTo(fileName);
|
||||
|
||||
// Create the file new each time.
|
||||
virtualPath.ShouldNotExistOnDisk(fileSystem);
|
||||
File.Create(virtualPath).Dispose();
|
||||
|
||||
bool keepRunning = true;
|
||||
Thread[] threads = new Thread[4];
|
||||
StringBuilder[] fileContents = new StringBuilder[4];
|
||||
|
||||
// Writer
|
||||
fileContents[0] = new StringBuilder();
|
||||
threads[0] = new Thread(() =>
|
||||
{
|
||||
DateTime start = DateTime.Now;
|
||||
Random r = new Random(0); // Seeded for repeatability
|
||||
while ((DateTime.Now - start).TotalSeconds < 2.5)
|
||||
{
|
||||
string newChar = r.Next(10).ToString();
|
||||
fileSystem.AppendAllText(virtualPath, newChar);
|
||||
fileContents[0].Append(newChar);
|
||||
Thread.Yield();
|
||||
}
|
||||
|
||||
keepRunning = false;
|
||||
});
|
||||
|
||||
// Readers
|
||||
for (int i = 1; i < threads.Length; ++i)
|
||||
{
|
||||
int myIndex = i;
|
||||
fileContents[i] = new StringBuilder();
|
||||
threads[i] = new Thread(() =>
|
||||
{
|
||||
using (Stream readStream = File.Open(virtualPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
using (StreamReader reader = new StreamReader(readStream, true))
|
||||
{
|
||||
while (keepRunning)
|
||||
{
|
||||
Thread.Yield();
|
||||
fileContents[myIndex].Append(reader.ReadToEnd());
|
||||
}
|
||||
|
||||
// Catch the last write that might have escaped us
|
||||
fileContents[myIndex].Append(reader.ReadToEnd());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
foreach (Thread thread in threads)
|
||||
{
|
||||
thread.Start();
|
||||
}
|
||||
|
||||
foreach (Thread thread in threads)
|
||||
{
|
||||
thread.Join();
|
||||
}
|
||||
|
||||
for (int i = 1; i < threads.Length; ++i)
|
||||
{
|
||||
fileContents[i].ToString().ShouldEqual(fileContents[0].ToString());
|
||||
}
|
||||
|
||||
fileSystem.DeleteFile(virtualPath);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,407 +0,0 @@
|
|||
using GVFS.FunctionalTests.FileSystemRunners;
|
||||
using GVFS.FunctionalTests.Tools;
|
||||
using GVFS.Tests.Should;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
||||
{
|
||||
[TestFixtureSource(typeof(FileSystemRunner), nameof(FileSystemRunner.Runners))]
|
||||
public class ParallelHydrationTests : TestsWithEnlistmentPerFixture
|
||||
{
|
||||
private FileSystemRunner fileSystem;
|
||||
|
||||
public ParallelHydrationTests(FileSystemRunner fileSystem)
|
||||
: base(forcePerRepoObjectCache: true)
|
||||
{
|
||||
this.fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
[Category(Categories.ExtraCoverage)]
|
||||
public void HydrateRepoInParallel()
|
||||
{
|
||||
GitProcess.Invoke(this.Enlistment.RepoRoot, $"checkout -f {FileConstants.CommitId}");
|
||||
|
||||
ConcurrentBag<string> collection = new ConcurrentBag<string>();
|
||||
List<Thread> threads = new List<Thread>();
|
||||
foreach (string path in FileConstants.Paths)
|
||||
{
|
||||
Thread thread = new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
this.fileSystem.ReadAllText(this.Enlistment.GetVirtualPathTo(path));
|
||||
collection.Add(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
collection.Add($"Exception while hydrating {path}: {e.Message}");
|
||||
}
|
||||
});
|
||||
threads.Add(thread);
|
||||
thread.Start();
|
||||
}
|
||||
|
||||
foreach (Thread thread in threads)
|
||||
{
|
||||
thread.Join();
|
||||
}
|
||||
|
||||
for (int i = 0; i < FileConstants.Paths.Count; i++)
|
||||
{
|
||||
collection.TryTake(out string value).ShouldBeTrue();
|
||||
FileConstants.Paths.Contains(value).ShouldBeTrue(message: value);
|
||||
}
|
||||
}
|
||||
|
||||
private class FileConstants
|
||||
{
|
||||
public static readonly string CommitId = "b76df49a1e02465ef1c27d9c2a1720b337de99c8";
|
||||
|
||||
/// <summary>
|
||||
/// Generate in Git Bash using command:
|
||||
/// git ls-tree --full-tree -r --name-only HEAD | awk '{print "\""$1"\","}'
|
||||
/// </summary>
|
||||
public static HashSet<string> Paths = new HashSet<string>()
|
||||
{
|
||||
".gitattributes",
|
||||
".gitignore",
|
||||
"AuthoringTests.md",
|
||||
"DeleteFileWithNameAheadOfDotAndSwitchCommits/(1).txt",
|
||||
"DeleteFileWithNameAheadOfDotAndSwitchCommits/1",
|
||||
"DeleteFileWithNameAheadOfDotAndSwitchCommits/test.txt",
|
||||
"EnumerateAndReadTestFiles/.B",
|
||||
"EnumerateAndReadTestFiles/._",
|
||||
"EnumerateAndReadTestFiles/._a",
|
||||
"EnumerateAndReadTestFiles/.~B",
|
||||
"EnumerateAndReadTestFiles/.~_B",
|
||||
"EnumerateAndReadTestFiles/A_100.txt",
|
||||
"EnumerateAndReadTestFiles/_C",
|
||||
"EnumerateAndReadTestFiles/_a",
|
||||
"EnumerateAndReadTestFiles/_aB",
|
||||
"EnumerateAndReadTestFiles/a",
|
||||
"EnumerateAndReadTestFiles/a.txt",
|
||||
"EnumerateAndReadTestFiles/a_1.txt",
|
||||
"EnumerateAndReadTestFiles/a_10.txt",
|
||||
"EnumerateAndReadTestFiles/a_3.txt",
|
||||
"EnumerateAndReadTestFiles/ab_",
|
||||
"EnumerateAndReadTestFiles/z_test.txt",
|
||||
"EnumerateAndReadTestFiles/zctest.txt",
|
||||
"EnumerateAndReadTestFiles/~B",
|
||||
"ErrorWhenPathTreatsFileAsFolderMatchesNTFS/full",
|
||||
"ErrorWhenPathTreatsFileAsFolderMatchesNTFS/partial",
|
||||
"ErrorWhenPathTreatsFileAsFolderMatchesNTFS/virtual",
|
||||
"GVFS.sln",
|
||||
"GVFS/FastFetch/App.config",
|
||||
"GVFS/FastFetch/CheckoutFetchHelper.cs",
|
||||
"GVFS/FastFetch/FastFetch.csproj",
|
||||
"GVFS/FastFetch/FastFetchVerb.cs",
|
||||
"GVFS/FastFetch/FetchHelper.cs",
|
||||
"GVFS/FastFetch/Git/DiffHelper.cs",
|
||||
"GVFS/FastFetch/Git/GitPackIndex.cs",
|
||||
"GVFS/FastFetch/Git/LibGit2Helpers.cs",
|
||||
"GVFS/FastFetch/Git/LibGit2Repo.cs",
|
||||
"GVFS/FastFetch/Git/LsTreeHelper.cs",
|
||||
"GVFS/FastFetch/Git/RefSpecHelpers.cs",
|
||||
"GVFS/FastFetch/Git/UpdateRefsHelper.cs",
|
||||
"GVFS/FastFetch/GitEnlistment.cs",
|
||||
"GVFS/FastFetch/Jobs/BatchObjectDownloadJob.cs",
|
||||
"GVFS/FastFetch/Jobs/CheckoutJob.cs",
|
||||
"GVFS/FastFetch/Jobs/Data/BlobDownloadRequest.cs",
|
||||
"GVFS/FastFetch/Jobs/Data/IndexPackRequest.cs",
|
||||
"GVFS/FastFetch/Jobs/Data/TreeSearchRequest.cs",
|
||||
"GVFS/FastFetch/Jobs/FindMissingBlobsJob.cs",
|
||||
"GVFS/FastFetch/Jobs/IndexPackJob.cs",
|
||||
"GVFS/FastFetch/Jobs/Job.cs",
|
||||
"GVFS/FastFetch/Program.cs",
|
||||
"GVFS/FastFetch/Properties/AssemblyInfo.cs",
|
||||
"GVFS/FastFetch/packages.config",
|
||||
"GVFS/GVFS.Common/AntiVirusExclusions.cs",
|
||||
"GVFS/GVFS.Common/BatchedLooseObjects/BatchedLooseObjectDeserializer.cs",
|
||||
"GVFS/GVFS.Common/CallbackResult.cs",
|
||||
"GVFS/GVFS.Common/ConcurrentHashSet.cs",
|
||||
"GVFS/GVFS.Common/Enlistment.cs",
|
||||
"GVFS/GVFS.Common/GVFS.Common.csproj",
|
||||
"GVFS/GVFS.Common/GVFSConstants.cs",
|
||||
"GVFS/GVFS.Common/GVFSContext.cs",
|
||||
"GVFS/GVFS.Common/GVFSEnlistment.cs",
|
||||
"GVFS/GVFS.Common/GVFSLock.cs",
|
||||
"GVFS/GVFS.Common/Git/CatFileTimeoutException.cs",
|
||||
"GVFS/GVFS.Common/Git/DiffTreeResult.cs",
|
||||
"GVFS/GVFS.Common/Git/GVFSConfigResponse.cs",
|
||||
"GVFS/GVFS.Common/Git/GitCatFileBatchCheckProcess.cs",
|
||||
"GVFS/GVFS.Common/Git/GitCatFileBatchProcess.cs",
|
||||
"GVFS/GVFS.Common/Git/GitCatFileProcess.cs",
|
||||
"GVFS/GVFS.Common/Git/GitObjects.cs",
|
||||
"GVFS/GVFS.Common/Git/GitPathConverter.cs",
|
||||
"GVFS/GVFS.Common/Git/GitProcess.cs",
|
||||
"GVFS/GVFS.Common/Git/GitRefs.cs",
|
||||
"GVFS/GVFS.Common/Git/GitTreeEntry.cs",
|
||||
"GVFS/GVFS.Common/Git/GitVersion.cs",
|
||||
"GVFS/GVFS.Common/Git/HttpGitObjects.cs",
|
||||
"GVFS/GVFS.Common/GitHelper.cs",
|
||||
"GVFS/GVFS.Common/HeartbeatThread.cs",
|
||||
"GVFS/GVFS.Common/IBackgroundOperation.cs",
|
||||
"GVFS/GVFS.Common/InvalidRepoException.cs",
|
||||
"GVFS/GVFS.Common/MountParameters.cs",
|
||||
"GVFS/GVFS.Common/NamedPipes/BrokenPipeException.cs",
|
||||
"GVFS/GVFS.Common/NamedPipes/NamedPipeClient.cs",
|
||||
"GVFS/GVFS.Common/NamedPipes/NamedPipeMessages.cs",
|
||||
"GVFS/GVFS.Common/NamedPipes/NamedPipeServer.cs",
|
||||
"GVFS/GVFS.Common/NativeMethods.cs",
|
||||
"GVFS/GVFS.Common/Physical/FileSystem/DirectoryItemInfo.cs",
|
||||
"GVFS/GVFS.Common/Physical/FileSystem/FileProperties.cs",
|
||||
"GVFS/GVFS.Common/Physical/FileSystem/PhysicalFileSystem.cs",
|
||||
"GVFS/GVFS.Common/Physical/FileSystem/StreamReaderExtensions.cs",
|
||||
"GVFS/GVFS.Common/Physical/Git/BigEndianReader.cs",
|
||||
"GVFS/GVFS.Common/Physical/Git/CopyBlobContentTimeoutException.cs",
|
||||
"GVFS/GVFS.Common/Physical/Git/EndianHelper.cs",
|
||||
"GVFS/GVFS.Common/Physical/Git/GVFSGitObjects.cs",
|
||||
"GVFS/GVFS.Common/Physical/Git/GitIndex.cs",
|
||||
"GVFS/GVFS.Common/Physical/Git/GitRepo.cs",
|
||||
"GVFS/GVFS.Common/Physical/RegistryUtils.cs",
|
||||
"GVFS/GVFS.Common/Physical/RepoMetadata.cs",
|
||||
"GVFS/GVFS.Common/PrefetchPacks/PrefetchPacksDeserializer.cs",
|
||||
"GVFS/GVFS.Common/ProcessHelper.cs",
|
||||
"GVFS/GVFS.Common/ProcessPool.cs",
|
||||
"GVFS/GVFS.Common/ProcessResult.cs",
|
||||
"GVFS/GVFS.Common/Properties/AssemblyInfo.cs",
|
||||
"GVFS/GVFS.Common/ReliableBackgroundOperations.cs",
|
||||
"GVFS/GVFS.Common/RetryWrapper.cs",
|
||||
"GVFS/GVFS.Common/RetryableException.cs",
|
||||
"GVFS/GVFS.Common/ReturnCode.cs",
|
||||
"GVFS/GVFS.Common/TaskExtensions.cs",
|
||||
"GVFS/GVFS.Common/Tracing/ConsoleEventListener.cs",
|
||||
"GVFS/GVFS.Common/Tracing/EventMetadata.cs",
|
||||
"GVFS/GVFS.Common/Tracing/ITracer.cs",
|
||||
"GVFS/GVFS.Common/Tracing/InProcEventListener.cs",
|
||||
"GVFS/GVFS.Common/Tracing/JsonEtwTracer.cs",
|
||||
"GVFS/GVFS.Common/Tracing/Keywords.cs",
|
||||
"GVFS/GVFS.Common/Tracing/LogFileEventListener.cs",
|
||||
"GVFS/GVFS.Common/WindowsProcessJob.cs",
|
||||
"GVFS/GVFS.Common/packages.config",
|
||||
"GVFS/GVFS.FunctionalTests/Category/CategoryConstants.cs",
|
||||
"GVFS/GVFS.FunctionalTests/FileSystemRunners/BashRunner.cs",
|
||||
"GVFS/GVFS.FunctionalTests/FileSystemRunners/CmdRunner.cs",
|
||||
"GVFS/GVFS.FunctionalTests/FileSystemRunners/FileSystemRunner.cs",
|
||||
"GVFS/GVFS.FunctionalTests/FileSystemRunners/PowerShellRunner.cs",
|
||||
"GVFS/GVFS.FunctionalTests/FileSystemRunners/ShellRunner.cs",
|
||||
"GVFS/GVFS.FunctionalTests/FileSystemRunners/SystemIORunner.cs",
|
||||
"GVFS/GVFS.FunctionalTests/GVFS.FunctionalTests.csproj",
|
||||
"GVFS/GVFS.FunctionalTests/Program.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Properties/AssemblyInfo.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Properties/Settings.Designer.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Properties/Settings.settings",
|
||||
"GVFS/GVFS.FunctionalTests/Should/FileSystemShouldExtensions.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerFixture/DiagnoseTests.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerFixture/GitCommandsTests.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerFixture/GitFilesTests.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerFixture/MountTests.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerFixture/MoveRenameFileTests.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerFixture/MoveRenameFileTests_2.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerFixture/MoveRenameFolderTests.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerFixture/TestsWithEnlistmentPerFixture.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerFixture/WorkingDirectoryTests.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerTestCase/CaseOnlyFolderRenameTests.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerTestCase/PersistedSparseExcludeTests.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerTestCase/PersistedWorkingDirectoryTests.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerTestCase/PrefetchVerbTests.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerTestCase/RebaseTests.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tests/EnlistmentPerTestCase/TestsWithEnlistmentPerTestCase.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tests/FastFetchTests.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tests/LongRunningEnlistment/GitMoveRenameTests.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tests/LongRunningEnlistment/GitObjectManipulationTests.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tests/LongRunningEnlistment/GitReadAndGitLockTests.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tests/LongRunningEnlistment/LongRunningSetup.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tests/LongRunningEnlistment/MultithreadedReadWriteTests.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tests/LongRunningEnlistment/TestsWithLongRunningEnlistment.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tests/LongRunningEnlistment/WorkingDirectoryTests.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tests/PrintTestCaseStats.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tools/ControlGitRepo.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tools/GVFSFunctionalTestEnlistment.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tools/GVFSProcess.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tools/GitHelpers.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tools/GitProcess.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tools/NativeMethods.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tools/ProcessHelper.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tools/ProcessResult.cs",
|
||||
"GVFS/GVFS.FunctionalTests/Tools/TestConstants.cs",
|
||||
"GVFS/GVFS.FunctionalTests/app.config",
|
||||
"GVFS/GVFS.FunctionalTests/packages.config",
|
||||
"GVFS/GVFS.Hooks/App.config",
|
||||
"GVFS/GVFS.Hooks/GVFS.Hooks.csproj",
|
||||
"GVFS/GVFS.Hooks/KnownGitCommands.cs",
|
||||
"GVFS/GVFS.Hooks/Program.cs",
|
||||
"GVFS/GVFS.Hooks/Properties/AssemblyInfo.cs",
|
||||
"GVFS/GVFS.Hooks/packages.config",
|
||||
"GVFS/GVFS.Mount/GVFS.Mount.csproj",
|
||||
"GVFS/GVFS.Mount/InProcessMount.cs",
|
||||
"GVFS/GVFS.Mount/MountAbortedException.cs",
|
||||
"GVFS/GVFS.Mount/MountVerb.cs",
|
||||
"GVFS/GVFS.Mount/Program.cs",
|
||||
"GVFS/GVFS.Mount/Properties/AssemblyInfo.cs",
|
||||
"GVFS/GVFS.Mount/packages.config",
|
||||
"GVFS/GVFS.NativeTests/GVFS.NativeTests.vcxproj",
|
||||
"GVFS/GVFS.NativeTests/GVFS.NativeTests.vcxproj.filters",
|
||||
"GVFS/GVFS.NativeTests/ReadMe.txt",
|
||||
"GVFS/GVFS.NativeTests/include/NtFunctions.h",
|
||||
"GVFS/GVFS.NativeTests/include/SafeHandle.h",
|
||||
"GVFS/GVFS.NativeTests/include/SafeOverlapped.h",
|
||||
"GVFS/GVFS.NativeTests/include/Should.h",
|
||||
"GVFS/GVFS.NativeTests/include/TestException.h",
|
||||
"GVFS/GVFS.NativeTests/include/TestHelpers.h",
|
||||
"GVFS/GVFS.NativeTests/include/TestVerifiers.h",
|
||||
"GVFS/GVFS.NativeTests/include/gvflt.h",
|
||||
"GVFS/GVFS.NativeTests/include/gvlib_internal.h",
|
||||
"GVFS/GVFS.NativeTests/include/stdafx.h",
|
||||
"GVFS/GVFS.NativeTests/include/targetver.h",
|
||||
"GVFS/GVFS.NativeTests/interface/GVFlt_BugRegressionTest.h",
|
||||
"GVFS/GVFS.NativeTests/interface/GVFlt_DeleteFileTest.h",
|
||||
"GVFS/GVFS.NativeTests/interface/GVFlt_DeleteFolderTest.h",
|
||||
"GVFS/GVFS.NativeTests/interface/GVFlt_DirEnumTest.h",
|
||||
"GVFS/GVFS.NativeTests/interface/GVFlt_FileAttributeTest.h",
|
||||
"GVFS/GVFS.NativeTests/interface/GVFlt_FileEATest.h",
|
||||
"GVFS/GVFS.NativeTests/interface/GVFlt_FileOperationTest.h",
|
||||
"GVFS/GVFS.NativeTests/interface/GVFlt_MoveFileTest.h",
|
||||
"GVFS/GVFS.NativeTests/interface/GVFlt_MoveFolderTest.h",
|
||||
"GVFS/GVFS.NativeTests/interface/GVFlt_MultiThreadsTest.h",
|
||||
"GVFS/GVFS.NativeTests/interface/GVFlt_SetLinkTest.h",
|
||||
"GVFS/GVFS.NativeTests/interface/NtQueryDirectoryFileTests.h",
|
||||
"GVFS/GVFS.NativeTests/interface/PlaceholderUtils.h",
|
||||
"GVFS/GVFS.NativeTests/interface/ReadAndWriteTests.h",
|
||||
"GVFS/GVFS.NativeTests/interface/TrailingSlashTests.h",
|
||||
"GVFS/GVFS.NativeTests/source/GVFlt_BugRegressionTest.cpp",
|
||||
"GVFS/GVFS.NativeTests/source/GVFlt_DeleteFileTest.cpp",
|
||||
"GVFS/GVFS.NativeTests/source/GVFlt_DeleteFolderTest.cpp",
|
||||
"GVFS/GVFS.NativeTests/source/GVFlt_DirEnumTest.cpp",
|
||||
"GVFS/GVFS.NativeTests/source/GVFlt_FileAttributeTest.cpp",
|
||||
"GVFS/GVFS.NativeTests/source/GVFlt_FileEATest.cpp",
|
||||
"GVFS/GVFS.NativeTests/source/GVFlt_FileOperationTest.cpp",
|
||||
"GVFS/GVFS.NativeTests/source/GVFlt_MoveFileTest.cpp",
|
||||
"GVFS/GVFS.NativeTests/source/GVFlt_MoveFolderTest.cpp",
|
||||
"GVFS/GVFS.NativeTests/source/GVFlt_MultiThreadTest.cpp",
|
||||
"GVFS/GVFS.NativeTests/source/GVFlt_SetLinkTest.cpp",
|
||||
"GVFS/GVFS.NativeTests/source/NtFunctions.cpp",
|
||||
"GVFS/GVFS.NativeTests/source/NtQueryDirectoryFileTests.cpp",
|
||||
"GVFS/GVFS.NativeTests/source/PlaceholderUtils.cpp",
|
||||
"GVFS/GVFS.NativeTests/source/ReadAndWriteTests.cpp",
|
||||
"GVFS/GVFS.NativeTests/source/TrailingSlashTests.cpp",
|
||||
"GVFS/GVFS.NativeTests/source/dllmain.cpp",
|
||||
"GVFS/GVFS.NativeTests/source/stdafx.cpp",
|
||||
"GVFS/GVFS.ReadObjectHook/GVFS.ReadObjectHook.vcxproj",
|
||||
"GVFS/GVFS.ReadObjectHook/GVFS.ReadObjectHook.vcxproj.filters",
|
||||
"GVFS/GVFS.ReadObjectHook/Version.rc",
|
||||
"GVFS/GVFS.ReadObjectHook/main.cpp",
|
||||
"GVFS/GVFS.ReadObjectHook/resource.h",
|
||||
"GVFS/GVFS.ReadObjectHook/stdafx.cpp",
|
||||
"GVFS/GVFS.ReadObjectHook/stdafx.h",
|
||||
"GVFS/GVFS.ReadObjectHook/targetver.h",
|
||||
"GVFS/GVFS.Service/GVFS.Service.csproj",
|
||||
"GVFS/GVFS.Service/GvfsService.cs",
|
||||
"GVFS/GVFS.Service/GvfsServiceInstaller.cs",
|
||||
"GVFS/GVFS.Service/Program.cs",
|
||||
"GVFS/GVFS.Service/Properties/AssemblyInfo.cs",
|
||||
"GVFS/GVFS.Service/packages.config",
|
||||
"GVFS/GVFS.Tests/GVFS.Tests.csproj",
|
||||
"GVFS/GVFS.Tests/NUnitRunner.cs",
|
||||
"GVFS/GVFS.Tests/Properties/AssemblyInfo.cs",
|
||||
"GVFS/GVFS.Tests/Should/EnumerableShouldExtensions.cs",
|
||||
"GVFS/GVFS.Tests/Should/StringExtensions.cs",
|
||||
"GVFS/GVFS.Tests/Should/StringShouldExtensions.cs",
|
||||
"GVFS/GVFS.Tests/Should/ValueShouldExtensions.cs",
|
||||
"GVFS/GVFS.Tests/packages.config",
|
||||
"GVFS/GVFS.UnitTests/App.config",
|
||||
"GVFS/GVFS.UnitTests/Category/CategoryContants.cs",
|
||||
"GVFS/GVFS.UnitTests/Common/GitHelperTests.cs",
|
||||
"GVFS/GVFS.UnitTests/Common/GitPathConverterTests.cs",
|
||||
"GVFS/GVFS.UnitTests/Common/GitVersionTests.cs",
|
||||
"GVFS/GVFS.UnitTests/Common/JsonEtwTracerTests.cs",
|
||||
"GVFS/GVFS.UnitTests/Common/ProcessHelperTests.cs",
|
||||
"GVFS/GVFS.UnitTests/Common/RetryWrapperTests.cs",
|
||||
"GVFS/GVFS.UnitTests/Common/SHA1UtilTests.cs",
|
||||
"GVFS/GVFS.UnitTests/Data/backward.txt",
|
||||
"GVFS/GVFS.UnitTests/Data/forward.txt",
|
||||
"GVFS/GVFS.UnitTests/Data/index_v2",
|
||||
"GVFS/GVFS.UnitTests/Data/index_v3",
|
||||
"GVFS/GVFS.UnitTests/Data/index_v4",
|
||||
"GVFS/GVFS.UnitTests/FastFetch/BatchObjectDownloadJobTests.cs",
|
||||
"GVFS/GVFS.UnitTests/FastFetch/DiffHelperTests.cs",
|
||||
"GVFS/GVFS.UnitTests/FastFetch/FastFetchTracingTests.cs",
|
||||
"GVFS/GVFS.UnitTests/GVFS.UnitTests.csproj",
|
||||
"GVFS/GVFS.UnitTests/GVFlt/DotGit/ExcludeFileTests.cs",
|
||||
"GVFS/GVFS.UnitTests/GVFlt/DotGit/GitConfigFileUtilsTests.cs",
|
||||
"GVFS/GVFS.UnitTests/GVFlt/GVFltActiveEnumerationTests.cs",
|
||||
"GVFS/GVFS.UnitTests/GVFlt/GVFltCallbacksTests.cs",
|
||||
"GVFS/GVFS.UnitTests/GVFlt/PathUtilTests.cs",
|
||||
"GVFS/GVFS.UnitTests/GVFlt/Physical/FileSerializerTests.cs",
|
||||
"GVFS/GVFS.UnitTests/Mock/Common/MockEnlistment.cs",
|
||||
"GVFS/GVFS.UnitTests/Mock/Common/MockPhysicalGitObjects.cs",
|
||||
"GVFS/GVFS.UnitTests/Mock/Common/MockTracer.cs",
|
||||
"GVFS/GVFS.UnitTests/Mock/Physical/FileSystem/MassiveMockFileSystem.cs",
|
||||
"GVFS/GVFS.UnitTests/Mock/Physical/FileSystem/MockDirectory.cs",
|
||||
"GVFS/GVFS.UnitTests/Mock/Physical/FileSystem/MockFile.cs",
|
||||
"GVFS/GVFS.UnitTests/Mock/Physical/FileSystem/MockFileSystem.cs",
|
||||
"GVFS/GVFS.UnitTests/Mock/Physical/FileSystem/MockSafeHandle.cs",
|
||||
"GVFS/GVFS.UnitTests/Mock/Physical/Git/MockBatchHttpGitObjects.cs",
|
||||
"GVFS/GVFS.UnitTests/Mock/Physical/Git/MockGVFSGitObjects.cs",
|
||||
"GVFS/GVFS.UnitTests/Mock/Physical/Git/MockGitIndex.cs",
|
||||
"GVFS/GVFS.UnitTests/Mock/Physical/Git/MockGitRepo.cs",
|
||||
"GVFS/GVFS.UnitTests/Mock/Physical/Git/MockHttpGitObjects.cs",
|
||||
"GVFS/GVFS.UnitTests/Mock/Physical/ReusableMemoryStream.cs",
|
||||
"GVFS/GVFS.UnitTests/Physical/Git/GitCatFileBatchProcessTests.cs",
|
||||
"GVFS/GVFS.UnitTests/Physical/Git/PhysicalGitObjectsTests.cs",
|
||||
"GVFS/GVFS.UnitTests/Prefetch/PrefetchPacksDeserializerTests.cs",
|
||||
"GVFS/GVFS.UnitTests/Program.cs",
|
||||
"GVFS/GVFS.UnitTests/Properties/AssemblyInfo.cs",
|
||||
"GVFS/GVFS.UnitTests/Should/StringShouldExtensions.cs",
|
||||
"GVFS/GVFS.UnitTests/Virtual/CommonRepoSetup.cs",
|
||||
"GVFS/GVFS.UnitTests/Virtual/DotGit/GitIndexTests.cs",
|
||||
"GVFS/GVFS.UnitTests/Virtual/TestsWithCommonRepo.cs",
|
||||
"GVFS/GVFS.UnitTests/packages.config",
|
||||
"GVFS/GVFS/App.config",
|
||||
"GVFS/GVFS/CommandLine/CloneHelper.cs",
|
||||
"GVFS/GVFS/CommandLine/CloneVerb.cs",
|
||||
"GVFS/GVFS/CommandLine/DiagnoseVerb.cs",
|
||||
"GVFS/GVFS/CommandLine/GVFSVerb.cs",
|
||||
"GVFS/GVFS/CommandLine/LogVerb.cs",
|
||||
"GVFS/GVFS/CommandLine/MountVerb.cs",
|
||||
"GVFS/GVFS/CommandLine/PrefetchHelper.cs",
|
||||
"GVFS/GVFS/CommandLine/PrefetchVerb.cs",
|
||||
"GVFS/GVFS/CommandLine/StatusVerb.cs",
|
||||
"GVFS/GVFS/CommandLine/UnmountVerb.cs",
|
||||
"GVFS/GVFS/GVFS.csproj",
|
||||
"GVFS/GVFS/GitVirtualFileSystem.ico",
|
||||
"GVFS/GVFS/Program.cs",
|
||||
"GVFS/GVFS/Properties/AssemblyInfo.cs",
|
||||
"GVFS/GVFS/Setup.iss",
|
||||
"GVFS/GVFS/packages.config",
|
||||
"GitCommandsTests/CheckoutNewBranchFromStartingPointTest/test1.txt",
|
||||
"GitCommandsTests/CheckoutNewBranchFromStartingPointTest/test2.txt",
|
||||
"GitCommandsTests/CheckoutOrhpanBranchFromStartingPointTest/test1.txt",
|
||||
"GitCommandsTests/CheckoutOrhpanBranchFromStartingPointTest/test2.txt",
|
||||
"GitCommandsTests/DeleteFileTests/1/#test",
|
||||
"GitCommandsTests/DeleteFileTests/2/$test",
|
||||
"GitCommandsTests/DeleteFileTests/3/)",
|
||||
"GitCommandsTests/DeleteFileTests/4/+.test",
|
||||
"GitCommandsTests/DeleteFileTests/5/-.test",
|
||||
"GitCommandsTests/RenameFileTests/1/#test",
|
||||
"GitCommandsTests/RenameFileTests/2/$test",
|
||||
"GitCommandsTests/RenameFileTests/3/)",
|
||||
"GitCommandsTests/RenameFileTests/4/+.test",
|
||||
"GitCommandsTests/RenameFileTests/5/-.test",
|
||||
"Protocol.md",
|
||||
"Readme.md",
|
||||
"Scripts/CreateCommonAssemblyVersion.bat",
|
||||
"Scripts/CreateCommonCliAssemblyVersion.bat",
|
||||
"Scripts/CreateCommonVersionHeader.bat",
|
||||
"Scripts/RunFunctionalTests.bat",
|
||||
"Scripts/RunUnitTests.bat",
|
||||
"Settings.StyleCop",
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,7 +15,6 @@ namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
|||
public class PrefetchVerbTests : TestsWithEnlistmentPerFixture
|
||||
{
|
||||
private const string PrefetchCommitsAndTreesLock = "prefetch-commits-trees.lock";
|
||||
private const string MultiPackIndexLock = "multi-pack-index.lock";
|
||||
private const string LsTreeTypeInPathBranchName = "FunctionalTests/20181105_LsTreeTypeInPath";
|
||||
|
||||
private FileSystemRunner fileSystem;
|
||||
|
@ -137,27 +136,6 @@ namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
|||
prefetchCommitsLockFile.ShouldNotExistOnDisk(this.fileSystem);
|
||||
}
|
||||
|
||||
[TestCase, Order(11)]
|
||||
[Category(Categories.MacTODO.TestNeedsToLockFile)] // PostFetchStepShouldComplete waits for a lock file
|
||||
public void PrefetchCleansUpPackDir()
|
||||
{
|
||||
string multiPackIndexLockFile = Path.Combine(this.Enlistment.GetPackRoot(this.fileSystem), MultiPackIndexLock);
|
||||
string oldGitTempFile = Path.Combine(this.Enlistment.GetPackRoot(this.fileSystem), "tmp_midx_XXXX");
|
||||
string oldKeepFile = Path.Combine(this.Enlistment.GetPackRoot(this.fileSystem), "prefetch-00000000-HASH.keep");
|
||||
|
||||
this.fileSystem.WriteAllText(multiPackIndexLockFile, this.Enlistment.EnlistmentRoot);
|
||||
this.fileSystem.WriteAllText(oldGitTempFile, this.Enlistment.EnlistmentRoot);
|
||||
this.fileSystem.WriteAllText(oldKeepFile, this.Enlistment.EnlistmentRoot);
|
||||
|
||||
this.Enlistment.Prefetch("--commits");
|
||||
this.Enlistment.PostFetchStep();
|
||||
oldGitTempFile.ShouldNotExistOnDisk(this.fileSystem);
|
||||
oldKeepFile.ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
this.PostFetchStepShouldComplete();
|
||||
multiPackIndexLockFile.ShouldNotExistOnDisk(this.fileSystem);
|
||||
}
|
||||
|
||||
[TestCase, Order(12)]
|
||||
public void PrefetchFilesFromFileListFile()
|
||||
{
|
||||
|
@ -236,9 +214,6 @@ namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
|||
}
|
||||
while (this.fileSystem.FileExists(objectCacheLock));
|
||||
|
||||
ProcessResult midxResult = GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "multi-pack-index verify --object-dir=\"" + objectDir + "\"");
|
||||
midxResult.ExitCode.ShouldEqual(0);
|
||||
|
||||
// A commit graph is not always generated, but if it is, then we want to ensure it is in a good state
|
||||
if (this.fileSystem.FileExists(Path.Combine(objectDir, "info", "commit-graphs", "commit-graph-chain")))
|
||||
{
|
||||
|
|
|
@ -71,12 +71,6 @@ namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
|||
this.fileSystem.DeleteFile(idxPath);
|
||||
idxPath.ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
// Remove midx that contains references to the pack
|
||||
string midxPath = Path.Combine(this.Enlistment.GetObjectRoot(this.fileSystem), "pack", "multi-pack-index");
|
||||
File.SetAttributes(midxPath, FileAttributes.Normal);
|
||||
this.fileSystem.DeleteFile(midxPath);
|
||||
midxPath.ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
// Prefetch should rebuild the missing idx
|
||||
this.Enlistment.Prefetch("--commits");
|
||||
this.PostFetchJobShouldComplete();
|
||||
|
@ -296,13 +290,10 @@ namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
|||
[TestCase, Order(9)]
|
||||
public void PrefetchCleansUpOphanedLockFiles()
|
||||
{
|
||||
// Multi-pack-index write happens even if the prefetch downloads nothing, while
|
||||
// the commit-graph write happens only when the prefetch downloads at least one pack
|
||||
|
||||
string graphPath = Path.Combine(this.Enlistment.GetObjectRoot(this.fileSystem), "info", "commit-graphs", "commit-graph-chain");
|
||||
string graphLockPath = graphPath + ".lock";
|
||||
string midxPath = Path.Combine(this.PackRoot, "multi-pack-index");
|
||||
string midxLockPath = midxPath + ".lock";
|
||||
|
||||
this.fileSystem.CreateEmptyFile(graphLockPath);
|
||||
|
||||
|
@ -312,7 +303,6 @@ namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
|||
// Force deleting the prefetch packs to make the prefetch non-trivial.
|
||||
this.fileSystem.DeleteDirectory(this.PackRoot);
|
||||
this.fileSystem.CreateDirectory(this.PackRoot);
|
||||
this.fileSystem.CreateEmptyFile(midxLockPath);
|
||||
|
||||
// Re-mount so the post-fetch job runs
|
||||
this.Enlistment.MountGVFS();
|
||||
|
@ -321,9 +311,7 @@ namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
|||
this.PostFetchJobShouldComplete();
|
||||
|
||||
this.fileSystem.FileExists(graphLockPath).ShouldBeFalse(nameof(graphLockPath));
|
||||
this.fileSystem.FileExists(midxLockPath).ShouldBeFalse(nameof(midxLockPath));
|
||||
this.fileSystem.FileExists(graphPath).ShouldBeTrue(nameof(graphPath));
|
||||
this.fileSystem.FileExists(midxPath).ShouldBeTrue(nameof(midxPath));
|
||||
}
|
||||
|
||||
private void PackShouldHaveIdxFile(string pathPath)
|
||||
|
@ -406,9 +394,6 @@ namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
|||
Thread.Sleep(500);
|
||||
}
|
||||
|
||||
ProcessResult midxResult = GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "multi-pack-index verify --object-dir=\"" + objectDir + "\"");
|
||||
midxResult.ExitCode.ShouldEqual(0);
|
||||
|
||||
ProcessResult graphResult = GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "commit-graph verify --shallow --object-dir=\"" + objectDir + "\"");
|
||||
graphResult.ExitCode.ShouldEqual(0);
|
||||
}
|
||||
|
|
|
@ -1,195 +0,0 @@
|
|||
using GVFS.FunctionalTests.FileSystemRunners;
|
||||
using GVFS.FunctionalTests.Should;
|
||||
using GVFS.FunctionalTests.Tools;
|
||||
using GVFS.Tests.Should;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
||||
{
|
||||
[TestFixture]
|
||||
[Category(Categories.GitCommands)]
|
||||
public class UpdatePlaceholderTests : TestsWithEnlistmentPerFixture
|
||||
{
|
||||
private const string TestParentFolderName = "Test_EPF_UpdatePlaceholderTests";
|
||||
private const string OldCommitId = "5d7a7d4db1734fb468a4094469ec58d26301b59d";
|
||||
private const string NewFilesAndChangesCommitId = "fec239ea12de1eda6ae5329d4f345784d5b61ff9";
|
||||
private FileSystemRunner fileSystem;
|
||||
|
||||
public UpdatePlaceholderTests()
|
||||
{
|
||||
this.fileSystem = new SystemIORunner();
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public virtual void SetupForTest()
|
||||
{
|
||||
// Start each test at NewFilesAndChangesCommitId
|
||||
this.GitCheckoutCommitId(NewFilesAndChangesCommitId);
|
||||
this.GitStatusShouldBeClean(NewFilesAndChangesCommitId);
|
||||
}
|
||||
|
||||
[TestCase, Order(1)]
|
||||
public void LockWithFullShareUpdateAndDelete()
|
||||
{
|
||||
string testFileUpdate4Contents = "Commit2LockToPreventUpdateAndDelete4 \r\n";
|
||||
string testFileDelete4Contents = "PreventDelete4 \r\n";
|
||||
string testFileUpdate4OldContents = "TestFileLockToPreventUpdateAndDelete4 \r\n";
|
||||
|
||||
string testFileUpdate4Name = "test4.txt";
|
||||
string testFileDelete4Name = "test_delete4.txt";
|
||||
|
||||
string testFileUpdate4Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "LockToPreventUpdateAndDelete", testFileUpdate4Name));
|
||||
string testFileDelete4Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "LockToPreventUpdateAndDelete", testFileDelete4Name));
|
||||
|
||||
testFileUpdate4Path.ShouldBeAFile(this.fileSystem).WithContents(testFileUpdate4Contents);
|
||||
testFileDelete4Path.ShouldBeAFile(this.fileSystem).WithContents(testFileDelete4Contents);
|
||||
|
||||
if (this.CanUpdateAndDeletePlaceholdersWithOpenHandles())
|
||||
{
|
||||
using (FileStream testFileUpdate4 = File.Open(testFileUpdate4Path, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete))
|
||||
using (FileStream testFileDelete4 = File.Open(testFileDelete4Path, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete))
|
||||
{
|
||||
this.GitCheckoutCommitId(OldCommitId);
|
||||
this.GitStatusShouldBeClean(OldCommitId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (FileStream testFileUpdate4Handle = File.Open(testFileUpdate4Path, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete))
|
||||
using (FileStream testFileDelete4Handle = File.Open(testFileDelete4Path, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete))
|
||||
{
|
||||
ProcessResult checkoutResult = GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "checkout " + OldCommitId);
|
||||
checkoutResult.Errors.ShouldContain(
|
||||
"HEAD is now at " + OldCommitId,
|
||||
"GVFS was unable to update the following files. To recover, close all handles to the files and run these commands:",
|
||||
"git checkout -- Test_EPF_UpdatePlaceholderTests/LockToPreventUpdateAndDelete/test4.txt");
|
||||
|
||||
GitHelpers.CheckGitCommandAgainstGVFSRepo(
|
||||
this.Enlistment.RepoRoot,
|
||||
"status",
|
||||
"HEAD detached at " + OldCommitId,
|
||||
"modified: Test_EPF_UpdatePlaceholderTests/LockToPreventUpdateAndDelete/test4.txt",
|
||||
"no changes added to commit (use \"git add\" and/or \"git commit -a\")\n");
|
||||
}
|
||||
|
||||
this.GitCheckoutToDiscardChanges(TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileUpdate4Name);
|
||||
this.GitStatusShouldBeClean(OldCommitId);
|
||||
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, TestParentFolderName + "/LockToPreventUpdateAndDelete/" + testFileUpdate4Name);
|
||||
}
|
||||
|
||||
testFileUpdate4Path.ShouldBeAFile(this.fileSystem).WithContents(testFileUpdate4OldContents);
|
||||
testFileDelete4Path.ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
this.GitCheckoutCommitId(NewFilesAndChangesCommitId);
|
||||
|
||||
this.GitStatusShouldBeClean(NewFilesAndChangesCommitId);
|
||||
testFileUpdate4Path.ShouldBeAFile(this.fileSystem).WithContents(testFileUpdate4Contents);
|
||||
testFileDelete4Path.ShouldBeAFile(this.fileSystem).WithContents(testFileDelete4Contents);
|
||||
}
|
||||
|
||||
[TestCase, Order(2)]
|
||||
public void FileProjectedAfterPlaceholderDeleteFileAndCheckout()
|
||||
{
|
||||
string testFile1Contents = "ProjectAfterDeleteAndCheckout \r\n";
|
||||
string testFile2Contents = "ProjectAfterDeleteAndCheckout2 \r\n";
|
||||
string testFile3Contents = "ProjectAfterDeleteAndCheckout3 \r\n";
|
||||
|
||||
string testFile1Name = "test.txt";
|
||||
string testFile2Name = "test2.txt";
|
||||
string testFile3Name = "test3.txt";
|
||||
|
||||
string testFile1Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "FileProjectedAfterPlaceholderDeleteFileAndCheckout", testFile1Name));
|
||||
string testFile2Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "FileProjectedAfterPlaceholderDeleteFileAndCheckout", testFile2Name));
|
||||
string testFile3Path = this.Enlistment.GetVirtualPathTo(Path.Combine(TestParentFolderName, "FileProjectedAfterPlaceholderDeleteFileAndCheckout", testFile3Name));
|
||||
|
||||
testFile1Path.ShouldBeAFile(this.fileSystem).WithContents(testFile1Contents);
|
||||
testFile2Path.ShouldBeAFile(this.fileSystem).WithContents(testFile2Contents);
|
||||
testFile3Path.ShouldBeAFile(this.fileSystem).WithContents(testFile3Contents);
|
||||
|
||||
this.GitCheckoutCommitId(OldCommitId);
|
||||
this.GitStatusShouldBeClean(OldCommitId);
|
||||
|
||||
testFile1Path.ShouldNotExistOnDisk(this.fileSystem);
|
||||
testFile2Path.ShouldNotExistOnDisk(this.fileSystem);
|
||||
testFile3Path.ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, TestParentFolderName + "/FileProjectedAfterPlaceholderDeleteFileAndCheckout/" + testFile1Name);
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, TestParentFolderName + "/FileProjectedAfterPlaceholderDeleteFileAndCheckout/" + testFile2Name);
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, TestParentFolderName + "/FileProjectedAfterPlaceholderDeleteFileAndCheckout/" + testFile3Name);
|
||||
|
||||
this.GitCheckoutCommitId(NewFilesAndChangesCommitId);
|
||||
this.GitStatusShouldBeClean(NewFilesAndChangesCommitId);
|
||||
|
||||
testFile1Path.ShouldBeAFile(this.fileSystem).WithContents(testFile1Contents);
|
||||
testFile2Path.ShouldBeAFile(this.fileSystem).WithContents(testFile2Contents);
|
||||
testFile3Path.ShouldBeAFile(this.fileSystem).WithContents(testFile3Contents);
|
||||
}
|
||||
|
||||
[TestCase, Order(3)]
|
||||
public void FullFilesDontAffectThePlaceholderDatabase()
|
||||
{
|
||||
string testFile = Path.Combine(this.Enlistment.RepoRoot, "FullFilesDontAffectThePlaceholderDatabase");
|
||||
|
||||
string placeholderDatabase = Path.Combine(this.Enlistment.DotGVFSRoot, "databases", "VFSForGit.sqlite");
|
||||
string placeholdersBefore = GVFSHelpers.GetAllSQLitePlaceholdersAsString(placeholderDatabase);
|
||||
|
||||
this.fileSystem.CreateEmptyFile(testFile);
|
||||
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
GVFSHelpers.GetAllSQLitePlaceholdersAsString(placeholderDatabase).ShouldEqual(placeholdersBefore);
|
||||
|
||||
this.fileSystem.DeleteFile(testFile);
|
||||
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
GVFSHelpers.GetAllSQLitePlaceholdersAsString(placeholderDatabase).ShouldEqual(placeholdersBefore);
|
||||
}
|
||||
|
||||
private ProcessResult InvokeGitAgainstGVFSRepo(string command)
|
||||
{
|
||||
return GitHelpers.InvokeGitAgainstGVFSRepo(this.Enlistment.RepoRoot, command);
|
||||
}
|
||||
|
||||
private void GitStatusShouldBeClean(string commitId)
|
||||
{
|
||||
GitHelpers.CheckGitCommandAgainstGVFSRepo(
|
||||
this.Enlistment.RepoRoot,
|
||||
"status",
|
||||
"HEAD detached at " + commitId,
|
||||
"nothing to commit, working tree clean");
|
||||
}
|
||||
|
||||
private void GitCheckoutToDiscardChanges(string gitPath)
|
||||
{
|
||||
GitHelpers.CheckGitCommandAgainstGVFSRepo(this.Enlistment.RepoRoot, "checkout -- " + gitPath);
|
||||
}
|
||||
|
||||
private void GitCheckoutCommitId(string commitId)
|
||||
{
|
||||
this.InvokeGitAgainstGVFSRepo("checkout " + commitId).Errors.ShouldContain("HEAD is now at " + commitId);
|
||||
}
|
||||
|
||||
private bool CanUpdateAndDeletePlaceholdersWithOpenHandles()
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724429(v=vs.85).aspx
|
||||
FileVersionInfo kernel32Info = FileVersionInfo.GetVersionInfo(Path.Combine(Environment.SystemDirectory, "kernel32.dll"));
|
||||
|
||||
// 16248 is first build with support - see 12658248 for details
|
||||
if (kernel32Info.FileBuildPart >= 16248)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,673 +0,0 @@
|
|||
using GVFS.FunctionalTests.FileSystemRunners;
|
||||
using GVFS.FunctionalTests.Should;
|
||||
using GVFS.FunctionalTests.Tools;
|
||||
using GVFS.Tests.Should;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.MemoryMappedFiles;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace GVFS.FunctionalTests.Tests.EnlistmentPerFixture
|
||||
{
|
||||
[TestFixtureSource(typeof(FileSystemRunner), nameof(FileSystemRunner.Runners))]
|
||||
public class WorkingDirectoryTests : TestsWithEnlistmentPerFixture
|
||||
{
|
||||
public const string TestFileContents =
|
||||
@"// dllmain.cpp : Defines the entry point for the DLL application.
|
||||
#include ""stdafx.h""
|
||||
|
||||
BOOL APIENTRY DllMain( HMODULE hModule,
|
||||
DWORD ul_reason_for_call,
|
||||
LPVOID lpReserved
|
||||
)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(hModule);
|
||||
UNREFERENCED_PARAMETER(lpReserved);
|
||||
|
||||
switch (ul_reason_for_call)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
case DLL_PROCESS_DETACH:
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
";
|
||||
private const int CurrentPlaceholderVersion = 1;
|
||||
|
||||
private FileSystemRunner fileSystem;
|
||||
|
||||
public WorkingDirectoryTests(FileSystemRunner fileSystem)
|
||||
: base(forcePerRepoObjectCache: true)
|
||||
{
|
||||
this.fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
[TestCase, Order(1)]
|
||||
public void ProjectedFileHasExpectedContents()
|
||||
{
|
||||
this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "ProjectedFileHasExpectedContents.cpp")
|
||||
.ShouldBeAFile(this.fileSystem)
|
||||
.WithContents(TestFileContents);
|
||||
}
|
||||
|
||||
[TestCase, Order(2)]
|
||||
public void StreamAccessReadWriteMemoryMappedProjectedFile()
|
||||
{
|
||||
string fileVirtualPath = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "StreamAccessReadWriteMemoryMappedProjectedFile.cs");
|
||||
string contents = fileVirtualPath.ShouldBeAFile(this.fileSystem).WithContents();
|
||||
StringBuilder contentsBuilder = new StringBuilder(contents);
|
||||
|
||||
using (MemoryMappedFile mmf = MemoryMappedFile.CreateFromFile(fileVirtualPath))
|
||||
{
|
||||
// Length of the Byte-order-mark that will be at the start of the memory mapped file.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/dd374101(v=vs.85).aspx
|
||||
int bomOffset = 3;
|
||||
|
||||
// offset -> Number of bytes from the start of the file where the view starts
|
||||
int offset = 64;
|
||||
int size = contents.Length;
|
||||
string newContent = "**NEWCONTENT**";
|
||||
|
||||
using (MemoryMappedViewStream streamAccessor = mmf.CreateViewStream(offset, size - offset + bomOffset))
|
||||
{
|
||||
streamAccessor.CanRead.ShouldEqual(true);
|
||||
streamAccessor.CanWrite.ShouldEqual(true);
|
||||
|
||||
for (int i = offset; i < size - offset; ++i)
|
||||
{
|
||||
streamAccessor.ReadByte().ShouldEqual(contents[i - bomOffset]);
|
||||
}
|
||||
|
||||
// Reset to the start of the stream (which will place the streamAccessor at offset in the memory file)
|
||||
streamAccessor.Seek(0, SeekOrigin.Begin);
|
||||
byte[] newContentBuffer = Encoding.ASCII.GetBytes(newContent);
|
||||
|
||||
streamAccessor.Write(newContentBuffer, 0, newContent.Length);
|
||||
|
||||
for (int i = 0; i < newContent.Length; ++i)
|
||||
{
|
||||
contentsBuilder[offset + i - bomOffset] = newContent[i];
|
||||
}
|
||||
|
||||
contents = contentsBuilder.ToString();
|
||||
}
|
||||
|
||||
// Verify the file has the new contents inserted into it
|
||||
using (MemoryMappedViewStream streamAccessor = mmf.CreateViewStream(offset: 0, size: size + bomOffset))
|
||||
{
|
||||
// Skip the BOM
|
||||
for (int i = 0; i < bomOffset; ++i)
|
||||
{
|
||||
streamAccessor.ReadByte();
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
streamAccessor.ReadByte().ShouldEqual(contents[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Confirm the new contents was written to disk
|
||||
fileVirtualPath.ShouldBeAFile(this.fileSystem).WithContents(contents);
|
||||
}
|
||||
|
||||
[TestCase, Order(3)]
|
||||
public void RandomAccessReadWriteMemoryMappedProjectedFile()
|
||||
{
|
||||
string fileVirtualPath = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "RandomAccessReadWriteMemoryMappedProjectedFile.cs");
|
||||
|
||||
string contents = fileVirtualPath.ShouldBeAFile(this.fileSystem).WithContents();
|
||||
StringBuilder contentsBuilder = new StringBuilder(contents);
|
||||
|
||||
using (MemoryMappedFile mmf = MemoryMappedFile.CreateFromFile(fileVirtualPath))
|
||||
{
|
||||
// Length of the Byte-order-mark that will be at the start of the memory mapped file.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/dd374101(v=vs.85).aspx
|
||||
int bomOffset = 3;
|
||||
|
||||
// offset -> Number of bytes from the start of the file where the view starts
|
||||
int offset = 64;
|
||||
int size = contents.Length;
|
||||
string newContent = "**NEWCONTENT**";
|
||||
|
||||
using (MemoryMappedViewAccessor randomAccessor = mmf.CreateViewAccessor(offset, size - offset + bomOffset))
|
||||
{
|
||||
randomAccessor.CanRead.ShouldEqual(true);
|
||||
randomAccessor.CanWrite.ShouldEqual(true);
|
||||
|
||||
for (int i = 0; i < size - offset; ++i)
|
||||
{
|
||||
((char)randomAccessor.ReadByte(i)).ShouldEqual(contents[i + offset - bomOffset]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < newContent.Length; ++i)
|
||||
{
|
||||
// Convert to byte before writing rather than writing as char, because char version will write a 16-bit
|
||||
// unicode char
|
||||
randomAccessor.Write(i, Convert.ToByte(newContent[i]));
|
||||
((char)randomAccessor.ReadByte(i)).ShouldEqual(newContent[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < newContent.Length; ++i)
|
||||
{
|
||||
contentsBuilder[offset + i - bomOffset] = newContent[i];
|
||||
}
|
||||
|
||||
contents = contentsBuilder.ToString();
|
||||
}
|
||||
|
||||
// Verify the file has the new contents inserted into it
|
||||
using (MemoryMappedViewAccessor randomAccessor = mmf.CreateViewAccessor(offset: 0, size: size + bomOffset))
|
||||
{
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
((char)randomAccessor.ReadByte(i + bomOffset)).ShouldEqual(contents[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Confirm the new contents was written to disk
|
||||
fileVirtualPath.ShouldBeAFile(this.fileSystem).WithContents(contents);
|
||||
}
|
||||
|
||||
[TestCase, Order(4)]
|
||||
public void StreamAndRandomAccessReadWriteMemoryMappedProjectedFile()
|
||||
{
|
||||
string fileVirtualPath = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "StreamAndRandomAccessReadWriteMemoryMappedProjectedFile.cs");
|
||||
|
||||
StringBuilder contentsBuilder = new StringBuilder();
|
||||
|
||||
// Length of the Byte-order-mark that will be at the start of the memory mapped file.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/dd374101(v=vs.85).aspx
|
||||
int bomOffset = 3;
|
||||
|
||||
using (MemoryMappedFile mmf = MemoryMappedFile.CreateFromFile(fileVirtualPath))
|
||||
{
|
||||
// The text length of StreamAndRandomAccessReadWriteMemoryMappedProjectedFile.cs was determined
|
||||
// outside of this test so that the test would not hydrate the file before we access via MemoryMappedFile
|
||||
int fileTextLength = 13762;
|
||||
|
||||
int size = bomOffset + fileTextLength;
|
||||
|
||||
int streamAccessWriteOffset = 64;
|
||||
int randomAccessWriteOffset = 128;
|
||||
|
||||
string newStreamAccessContent = "**NEW_STREAM_CONTENT**";
|
||||
string newRandomAccessConents = "&&NEW_RANDOM_CONTENT&&";
|
||||
|
||||
// Read (and modify) contents using stream accessor
|
||||
using (MemoryMappedViewStream streamAccessor = mmf.CreateViewStream(offset: 0, size: size))
|
||||
{
|
||||
streamAccessor.CanRead.ShouldEqual(true);
|
||||
streamAccessor.CanWrite.ShouldEqual(true);
|
||||
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
contentsBuilder.Append((char)streamAccessor.ReadByte());
|
||||
}
|
||||
|
||||
// Reset to the start of the stream (which will place the streamAccessor at offset in the memory file)
|
||||
streamAccessor.Seek(streamAccessWriteOffset, SeekOrigin.Begin);
|
||||
byte[] newContentBuffer = Encoding.ASCII.GetBytes(newStreamAccessContent);
|
||||
|
||||
streamAccessor.Write(newContentBuffer, 0, newStreamAccessContent.Length);
|
||||
|
||||
for (int i = 0; i < newStreamAccessContent.Length; ++i)
|
||||
{
|
||||
contentsBuilder[streamAccessWriteOffset + i] = newStreamAccessContent[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Read (and modify) contents using random accessor
|
||||
using (MemoryMappedViewAccessor randomAccessor = mmf.CreateViewAccessor(offset: 0, size: size))
|
||||
{
|
||||
randomAccessor.CanRead.ShouldEqual(true);
|
||||
randomAccessor.CanWrite.ShouldEqual(true);
|
||||
|
||||
// Confirm the random accessor reads the same content that was read (and written) by the stream
|
||||
// accessor
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
((char)randomAccessor.ReadByte(i)).ShouldEqual(contentsBuilder[i]);
|
||||
}
|
||||
|
||||
// Write some new content
|
||||
for (int i = 0; i < newRandomAccessConents.Length; ++i)
|
||||
{
|
||||
// Convert to byte before writing rather than writing as char, because char version will write a 16-bit
|
||||
// unicode char
|
||||
randomAccessor.Write(i + randomAccessWriteOffset, Convert.ToByte(newRandomAccessConents[i]));
|
||||
((char)randomAccessor.ReadByte(i + randomAccessWriteOffset)).ShouldEqual(newRandomAccessConents[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < newRandomAccessConents.Length; ++i)
|
||||
{
|
||||
contentsBuilder[randomAccessWriteOffset + i] = newRandomAccessConents[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Verify the file one more time with a stream accessor
|
||||
using (MemoryMappedViewStream streamAccessor = mmf.CreateViewStream(offset: 0, size: size))
|
||||
{
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
streamAccessor.ReadByte().ShouldEqual(contentsBuilder[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the BOM before comparing with the contents of the file on disk
|
||||
contentsBuilder.Remove(0, bomOffset);
|
||||
|
||||
// Confirm the new contents was written to the file
|
||||
fileVirtualPath.ShouldBeAFile(this.fileSystem).WithContents(contentsBuilder.ToString());
|
||||
}
|
||||
|
||||
[TestCase, Order(5)]
|
||||
public void MoveProjectedFileToInvalidFolder()
|
||||
{
|
||||
string targetFolderName = "test_folder";
|
||||
string targetFolderVirtualPath = this.Enlistment.GetVirtualPathTo(targetFolderName);
|
||||
targetFolderVirtualPath.ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
string sourceFolderName = "Test_EPF_WorkingDirectoryTests";
|
||||
string testFileName = "MoveProjectedFileToInvalidFolder.config";
|
||||
string sourcePath = Path.Combine(sourceFolderName, testFileName);
|
||||
string sourceVirtualPath = this.Enlistment.GetVirtualPathTo(sourcePath);
|
||||
|
||||
string newTestFileVirtualPath = Path.Combine(targetFolderVirtualPath, testFileName);
|
||||
|
||||
this.fileSystem.MoveFileShouldFail(sourceVirtualPath, newTestFileVirtualPath);
|
||||
newTestFileVirtualPath.ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
sourceVirtualPath.ShouldBeAFile(this.fileSystem);
|
||||
|
||||
targetFolderVirtualPath.ShouldNotExistOnDisk(this.fileSystem);
|
||||
}
|
||||
|
||||
[TestCase, Order(6)]
|
||||
public void EnumerateAndReadDoesNotChangeEnumerationOrder()
|
||||
{
|
||||
string folderVirtualPath = this.Enlistment.GetVirtualPathTo("EnumerateAndReadTestFiles");
|
||||
this.EnumerateAndReadShouldNotChangeEnumerationOrder(folderVirtualPath);
|
||||
folderVirtualPath.ShouldBeADirectory(this.fileSystem);
|
||||
folderVirtualPath.ShouldBeADirectory(this.fileSystem).WithItems();
|
||||
}
|
||||
|
||||
[TestCase, Order(7)]
|
||||
public void HydratingFileUsesNameCaseFromRepo()
|
||||
{
|
||||
string fileName = "Readme.md";
|
||||
string parentFolderPath = this.Enlistment.GetVirtualPathTo(Path.GetDirectoryName(fileName));
|
||||
parentFolderPath.ShouldBeADirectory(this.fileSystem).WithItems().ShouldContainSingle(info => info.Name.Equals(fileName, StringComparison.Ordinal));
|
||||
|
||||
// Hydrate file with a request using different file name case
|
||||
string wrongCaseFilePath = this.Enlistment.GetVirtualPathTo(fileName.ToUpper());
|
||||
string fileContents = wrongCaseFilePath.ShouldBeAFile(this.fileSystem).WithContents();
|
||||
|
||||
// File on disk should have original case projected from repo
|
||||
parentFolderPath.ShouldBeADirectory(this.fileSystem).WithItems().ShouldContainSingle(info => info.Name.Equals(fileName, StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
[TestCase, Order(8)]
|
||||
public void HydratingNestedFileUsesNameCaseFromRepo()
|
||||
{
|
||||
string filePath = Path.Combine("GVFS", "FastFetch", "Properties", "AssemblyInfo.cs");
|
||||
string filePathAllCaps = filePath.ToUpper();
|
||||
string parentFolderVirtualPathAllCaps = this.Enlistment.GetVirtualPathTo(Path.GetDirectoryName(filePathAllCaps));
|
||||
parentFolderVirtualPathAllCaps.ShouldBeADirectory(this.fileSystem).WithItems().ShouldContainSingle(info => info.Name.Equals(Path.GetFileName(filePath), StringComparison.Ordinal));
|
||||
|
||||
// Hydrate file with a request using different file name case
|
||||
string wrongCaseFilePath = this.Enlistment.GetVirtualPathTo(filePathAllCaps);
|
||||
string fileContents = wrongCaseFilePath.ShouldBeAFile(this.fileSystem).WithContents();
|
||||
|
||||
// File on disk should have original case projected from repo
|
||||
string parentFolderVirtualPath = this.Enlistment.GetVirtualPathTo(Path.GetDirectoryName(filePath));
|
||||
parentFolderVirtualPath.ShouldBeADirectory(this.fileSystem).WithItems().ShouldContainSingle(info => info.Name.Equals(Path.GetFileName(filePath), StringComparison.Ordinal));
|
||||
|
||||
// Confirm all folders up to root have the correct case
|
||||
string parentFolderPath = Path.GetDirectoryName(filePath);
|
||||
while (!string.IsNullOrWhiteSpace(parentFolderPath))
|
||||
{
|
||||
string folderName = Path.GetFileName(parentFolderPath);
|
||||
parentFolderPath = Path.GetDirectoryName(parentFolderPath);
|
||||
this.Enlistment.GetVirtualPathTo(parentFolderPath).ShouldBeADirectory(this.fileSystem).WithItems().ShouldContainSingle(info => info.Name.Equals(folderName, StringComparison.Ordinal));
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase, Order(9)]
|
||||
public void AppendToHydratedFileAfterRemount()
|
||||
{
|
||||
string fileToAppendEntry = "Test_EPF_WorkingDirectoryTests/WriteToHydratedFileAfterRemount.cpp";
|
||||
string virtualFilePath = this.Enlistment.GetVirtualPathTo(fileToAppendEntry);
|
||||
string fileContents = virtualFilePath.ShouldBeAFile(this.fileSystem).WithContents();
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, fileToAppendEntry);
|
||||
|
||||
// Remount
|
||||
this.Enlistment.UnmountGVFS();
|
||||
this.Enlistment.MountGVFS();
|
||||
|
||||
string appendedText = "Text to append";
|
||||
this.fileSystem.AppendAllText(virtualFilePath, appendedText);
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, fileToAppendEntry);
|
||||
virtualFilePath.ShouldBeAFile(this.fileSystem).WithContents(fileContents + appendedText);
|
||||
}
|
||||
|
||||
[TestCase, Order(10)]
|
||||
public void ReadDeepProjectedFile()
|
||||
{
|
||||
string testFilePath = Path.Combine("Test_EPF_WorkingDirectoryTests", "1", "2", "3", "4", "ReadDeepProjectedFile.cpp");
|
||||
this.Enlistment.GetVirtualPathTo(testFilePath).ShouldBeAFile(this.fileSystem).WithContents(TestFileContents);
|
||||
}
|
||||
|
||||
[TestCase, Order(11)]
|
||||
public void FilePlaceHolderHasVersionInfo()
|
||||
{
|
||||
string sha = "BB1C8B9ADA90D6B8F6C88F12C6DDB07C186155BD";
|
||||
string virtualFilePath = this.Enlistment.GetVirtualPathTo("GVFlt_BugRegressionTest", "GVFlt_ModifyFileInScratchAndDir", "ModifyFileInScratchAndDir.txt");
|
||||
virtualFilePath.ShouldBeAFile(this.fileSystem).WithContents();
|
||||
|
||||
ProcessResult revParseHeadResult = GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "rev-parse HEAD");
|
||||
string commitID = revParseHeadResult.Output.Trim();
|
||||
|
||||
this.PlaceholderHasVersionInfo(virtualFilePath, CurrentPlaceholderVersion, sha).ShouldEqual(true);
|
||||
}
|
||||
|
||||
[TestCase, Order(12), Ignore("Results in an access violation in the functional test on the build server")]
|
||||
public void FolderPlaceHolderHasVersionInfo()
|
||||
{
|
||||
string virtualFilePath = this.Enlistment.GetVirtualPathTo("GVFlt_BugRegressionTest", "GVFlt_ModifyFileInScratchAndDir");
|
||||
|
||||
ProcessResult revParseHeadResult = GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "rev-parse HEAD");
|
||||
string commitID = revParseHeadResult.Output.Trim();
|
||||
|
||||
this.PlaceholderHasVersionInfo(virtualFilePath, CurrentPlaceholderVersion, string.Empty).ShouldEqual(true);
|
||||
}
|
||||
|
||||
[TestCase, Order(13)]
|
||||
[Category(Categories.GitCommands)]
|
||||
[Category(Categories.MacTODO.NeedsNewFolderCreateNotification)]
|
||||
public void FolderContentsProjectedAfterFolderCreateAndCheckout()
|
||||
{
|
||||
string folderName = "GVFlt_MultiThreadTest";
|
||||
|
||||
// 54ea499de78eafb4dfd30b90e0bd4bcec26c4349 did not have the folder GVFlt_MultiThreadTest
|
||||
GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "checkout 54ea499de78eafb4dfd30b90e0bd4bcec26c4349");
|
||||
|
||||
// Confirm that no other test has created GVFlt_MultiThreadTest or put it in the modified files
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, folderName);
|
||||
|
||||
string virtualFolderPath = this.Enlistment.GetVirtualPathTo(folderName);
|
||||
virtualFolderPath.ShouldNotExistOnDisk(this.fileSystem);
|
||||
this.fileSystem.CreateDirectory(virtualFolderPath);
|
||||
|
||||
// b3ddcf43b997cba3fbf9d2341b297e22bf48601a was the commit prior to deleting GVFLT_MultiThreadTest
|
||||
// 692765: Note that test also validates case insensitivity as GVFlt_MultiThreadTest is named GVFLT_MultiThreadTest
|
||||
// in this commit
|
||||
GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "checkout b3ddcf43b997cba3fbf9d2341b297e22bf48601a");
|
||||
|
||||
this.Enlistment.GetVirtualPathTo(folderName + "\\OpenForReadsSameTime\\test").ShouldBeAFile(this.fileSystem).WithContents("123 \r\n");
|
||||
this.Enlistment.GetVirtualPathTo(folderName + "\\OpenForWritesSameTime\\test").ShouldBeAFile(this.fileSystem).WithContents("123 \r\n");
|
||||
}
|
||||
|
||||
[TestCase, Order(14)]
|
||||
[Category(Categories.GitCommands)]
|
||||
public void FolderContentsCorrectAfterCreateNewFolderRenameAndCheckoutCommitWithSameFolder()
|
||||
{
|
||||
// 3a55d3b760c87642424e834228a3408796501e7c is the commit prior to adding Test_EPF_MoveRenameFileTests
|
||||
GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "checkout 3a55d3b760c87642424e834228a3408796501e7c");
|
||||
|
||||
// Confirm that no other test has created this folder or put it in the modified files
|
||||
string folderName = "Test_EPF_MoveRenameFileTests";
|
||||
string folder = this.Enlistment.GetVirtualPathTo(folderName);
|
||||
folder.ShouldNotExistOnDisk(this.fileSystem);
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, folderName);
|
||||
|
||||
// Confirm modified paths picks up renamed folder
|
||||
string newFolder = this.Enlistment.GetVirtualPathTo("newFolder");
|
||||
this.fileSystem.CreateDirectory(newFolder);
|
||||
this.fileSystem.MoveDirectory(newFolder, folder);
|
||||
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, folderName + "/");
|
||||
|
||||
// Switch back to this.ControlGitRepo.Commitish and confirm that folder contents are correct
|
||||
GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "checkout " + Properties.Settings.Default.Commitish);
|
||||
|
||||
folder.ShouldBeADirectory(this.fileSystem);
|
||||
Path.Combine(folder, "ChangeNestedUnhydratedFileNameCase", "Program.cs").ShouldBeAFile(this.fileSystem).WithContents(MoveRenameFileTests.TestFileContents);
|
||||
Path.Combine(folder, "ChangeUnhydratedFileName", "Program.cs").ShouldBeAFile(this.fileSystem).WithContents(MoveRenameFileTests.TestFileContents);
|
||||
Path.Combine(folder, "MoveUnhydratedFileToDotGitFolder", "Program.cs").ShouldBeAFile(this.fileSystem).WithContents(MoveRenameFileTests.TestFileContents);
|
||||
}
|
||||
|
||||
[TestCase, Order(15)]
|
||||
public void FilterNonUTF8FileName()
|
||||
{
|
||||
string encodingFilename = "ريلٌأكتوبرûمارسأغسطسºٰٰۂْٗ۵ريلٌأك.txt";
|
||||
string folderVirtualPath = this.Enlistment.GetVirtualPathTo("FilenameEncoding");
|
||||
|
||||
this.FolderEnumerationShouldHaveSingleEntry(folderVirtualPath, encodingFilename, null);
|
||||
this.FolderEnumerationShouldHaveSingleEntry(folderVirtualPath, encodingFilename, "ريلٌأكتوبرûمارسأغسطسºٰٰۂْٗ۵ريلٌأك.txt");
|
||||
this.FolderEnumerationShouldHaveSingleEntry(folderVirtualPath, encodingFilename, "ريلٌأكتوبرûمارسأغسطسºٰٰۂْٗ۵ريلٌأك*");
|
||||
this.FolderEnumerationShouldHaveSingleEntry(folderVirtualPath, encodingFilename, "ريلٌأكتوبر*.TXT");
|
||||
|
||||
folderVirtualPath.ShouldBeADirectory(this.fileSystem).WithNoItems("test*");
|
||||
folderVirtualPath.ShouldBeADirectory(this.fileSystem).WithNoItems("ريلٌأكتوب.TXT");
|
||||
}
|
||||
|
||||
[TestCase, Order(16)]
|
||||
public void AllNullObjectRedownloaded()
|
||||
{
|
||||
GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "checkout " + this.Enlistment.Commitish);
|
||||
ProcessResult revParseResult = GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "rev-parse :Test_EPF_WorkingDirectoryTests/AllNullObjectRedownloaded.txt");
|
||||
string sha = revParseResult.Output.Trim();
|
||||
sha.Length.ShouldEqual(40);
|
||||
string objectPath = Path.Combine(this.Enlistment.GetObjectRoot(this.fileSystem), sha.Substring(0, 2), sha.Substring(2, 38));
|
||||
objectPath.ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
// At this point there should be no corrupt objects
|
||||
string corruptObjectFolderPath = Path.Combine(this.Enlistment.DotGVFSRoot, "CorruptObjects");
|
||||
corruptObjectFolderPath.ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
// Read a copy of AllNullObjectRedownloaded.txt to force the object to be downloaded
|
||||
GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "rev-parse :Test_EPF_WorkingDirectoryTests/AllNullObjectRedownloaded_copy.txt").Output.Trim().ShouldEqual(sha);
|
||||
string testFileContents = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "AllNullObjectRedownloaded_copy.txt").ShouldBeAFile(this.fileSystem).WithContents();
|
||||
objectPath.ShouldBeAFile(this.fileSystem);
|
||||
|
||||
// Set the contents of objectPath to all NULL
|
||||
FileInfo objectFileInfo = new FileInfo(objectPath);
|
||||
File.WriteAllBytes(objectPath, Enumerable.Repeat<byte>(0, (int)objectFileInfo.Length).ToArray());
|
||||
|
||||
// Read the original path and verify its contents are correct
|
||||
this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "AllNullObjectRedownloaded.txt").ShouldBeAFile(this.fileSystem).WithContents(testFileContents);
|
||||
|
||||
// Confirm there's a new item in the corrupt objects folder
|
||||
corruptObjectFolderPath.ShouldBeADirectory(this.fileSystem);
|
||||
FileSystemInfo badObject = corruptObjectFolderPath.ShouldBeADirectory(this.fileSystem).WithOneItem();
|
||||
(badObject as FileInfo).ShouldNotBeNull().Length.ShouldEqual(objectFileInfo.Length);
|
||||
}
|
||||
|
||||
[TestCase, Order(17)]
|
||||
public void TruncatedObjectRedownloaded()
|
||||
{
|
||||
GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "checkout " + this.Enlistment.Commitish);
|
||||
ProcessResult revParseResult = GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "rev-parse :Test_EPF_WorkingDirectoryTests/TruncatedObjectRedownloaded.txt");
|
||||
string sha = revParseResult.Output.Trim();
|
||||
sha.Length.ShouldEqual(40);
|
||||
string objectPath = Path.Combine(this.Enlistment.GetObjectRoot(this.fileSystem), sha.Substring(0, 2), sha.Substring(2, 38));
|
||||
objectPath.ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
string corruptObjectFolderPath = Path.Combine(this.Enlistment.DotGVFSRoot, "CorruptObjects");
|
||||
int initialCorruptObjectCount = 0;
|
||||
if (this.fileSystem.DirectoryExists(corruptObjectFolderPath))
|
||||
{
|
||||
initialCorruptObjectCount = new DirectoryInfo(corruptObjectFolderPath).EnumerateFileSystemInfos().Count();
|
||||
}
|
||||
|
||||
// Read a copy of TruncatedObjectRedownloaded.txt to force the object to be downloaded
|
||||
GitProcess.InvokeProcess(this.Enlistment.RepoRoot, "rev-parse :Test_EPF_WorkingDirectoryTests/TruncatedObjectRedownloaded_copy.txt").Output.Trim().ShouldEqual(sha);
|
||||
string testFileContents = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "TruncatedObjectRedownloaded_copy.txt").ShouldBeAFile(this.fileSystem).WithContents();
|
||||
objectPath.ShouldBeAFile(this.fileSystem);
|
||||
string modifedFile = "Test_EPF_WorkingDirectoryTests/TruncatedObjectRedownloaded.txt";
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, modifedFile);
|
||||
|
||||
// Truncate the contents of objectPath
|
||||
string tempTruncatedObjectPath = objectPath + "truncated";
|
||||
FileInfo objectFileInfo = new FileInfo(objectPath);
|
||||
long objectLength = objectFileInfo.Length;
|
||||
using (FileStream objectStream = new FileStream(objectPath, FileMode.Open))
|
||||
using (FileStream truncatedObjectStream = new FileStream(tempTruncatedObjectPath, FileMode.CreateNew))
|
||||
{
|
||||
for (int i = 0; i < (objectStream.Length - 16); ++i)
|
||||
{
|
||||
truncatedObjectStream.WriteByte((byte)objectStream.ReadByte());
|
||||
}
|
||||
}
|
||||
|
||||
this.fileSystem.DeleteFile(objectPath);
|
||||
this.fileSystem.MoveFile(tempTruncatedObjectPath, objectPath);
|
||||
tempTruncatedObjectPath.ShouldNotExistOnDisk(this.fileSystem);
|
||||
objectPath.ShouldBeAFile(this.fileSystem);
|
||||
new FileInfo(objectPath).Length.ShouldEqual(objectLength - 16);
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
// Mac can't correct corrupt objects, but it should detect them and add to ModifiedPaths.dat
|
||||
this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "TruncatedObjectRedownloaded.txt").ShouldBeAFile(this.fileSystem);
|
||||
|
||||
GVFSHelpers.ModifiedPathsShouldContain(this.Enlistment, this.fileSystem, modifedFile);
|
||||
GitHelpers.CheckGitCommandAgainstGVFSRepo(
|
||||
this.Enlistment.RepoRoot,
|
||||
"status",
|
||||
$"modified: {modifedFile}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Windows should correct a corrupt obect
|
||||
// Read the original path and verify its contents are correct
|
||||
this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "TruncatedObjectRedownloaded.txt").ShouldBeAFile(this.fileSystem).WithContents(testFileContents);
|
||||
|
||||
// Confirm there's a new item in the corrupt objects folder
|
||||
corruptObjectFolderPath.ShouldBeADirectory(this.fileSystem).WithItems().Count().ShouldEqual(initialCorruptObjectCount + 1);
|
||||
|
||||
// File should not be in ModifiedPaths.dat
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.fileSystem, "Test_EPF_WorkingDirectoryTests/TruncatedObjectRedownloaded.txt");
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase, Order(18)]
|
||||
public void CreateFileAfterTryOpenNonExistentFile()
|
||||
{
|
||||
string filePath = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "CreateFileAfterTryOpenNonExistentFile_NotProjected.txt");
|
||||
string fileContents = "CreateFileAfterTryOpenNonExistentFile file contents";
|
||||
filePath.ShouldNotExistOnDisk(this.fileSystem);
|
||||
this.fileSystem.WriteAllText(filePath, fileContents);
|
||||
filePath.ShouldBeAFile(this.fileSystem).WithContents(fileContents);
|
||||
}
|
||||
|
||||
[TestCase, Order(19)]
|
||||
public void RenameFileAfterTryOpenNonExistentFile()
|
||||
{
|
||||
string filePath = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "RenameFileAfterTryOpenNonExistentFile_NotProjected.txt");
|
||||
string fileContents = "CreateFileAfterTryOpenNonExistentFile file contents";
|
||||
filePath.ShouldNotExistOnDisk(this.fileSystem);
|
||||
|
||||
string newFilePath = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "RenameFileAfterTryOpenNonExistentFile_NewFile.txt");
|
||||
this.fileSystem.WriteAllText(newFilePath, fileContents);
|
||||
newFilePath.ShouldBeAFile(this.fileSystem).WithContents(fileContents);
|
||||
|
||||
this.fileSystem.MoveFile(newFilePath, filePath);
|
||||
filePath.ShouldBeAFile(this.fileSystem).WithContents(fileContents);
|
||||
}
|
||||
|
||||
[TestCase, Order(20)]
|
||||
public void VerifyFileSize()
|
||||
{
|
||||
string filePath = this.Enlistment.GetVirtualPathTo("Test_EPF_WorkingDirectoryTests", "ProjectedFileHasExpectedContents.cpp");
|
||||
long fileSize = this.fileSystem.FileSize(filePath);
|
||||
fileSize.ShouldEqual(536);
|
||||
}
|
||||
|
||||
private void FolderEnumerationShouldHaveSingleEntry(string folderVirtualPath, string expectedEntryName, string searchPatten)
|
||||
{
|
||||
IEnumerable<FileSystemInfo> folderEntries;
|
||||
if (string.IsNullOrEmpty(searchPatten))
|
||||
{
|
||||
folderEntries = folderVirtualPath.ShouldBeADirectory(this.fileSystem).WithItems();
|
||||
}
|
||||
else
|
||||
{
|
||||
folderEntries = folderVirtualPath.ShouldBeADirectory(this.fileSystem).WithItems(searchPatten);
|
||||
}
|
||||
|
||||
folderEntries.Count().ShouldEqual(1);
|
||||
FileSystemInfo singleEntry = folderEntries.First();
|
||||
singleEntry.Name.ShouldEqual(expectedEntryName, $"Actual name: {singleEntry.Name} does not equal expected name {expectedEntryName}");
|
||||
}
|
||||
|
||||
private void EnumerateAndReadShouldNotChangeEnumerationOrder(string folderRelativePath)
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
NativeTests.EnumerateAndReadDoesNotChangeEnumerationOrder(folderRelativePath).ShouldEqual(true);
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
string[] entries = Directory.GetFileSystemEntries(folderRelativePath);
|
||||
foreach (string entry in entries)
|
||||
{
|
||||
File.ReadAllText(entry);
|
||||
}
|
||||
|
||||
string[] postReadEntries = Directory.GetFileSystemEntries(folderRelativePath);
|
||||
Enumerable.SequenceEqual(entries, postReadEntries)
|
||||
.ShouldBeTrue($"Entries are not the same after reading. Orignial list:\n{string.Join(",", entries)}\n\nAfter read:\n{string.Join(",", postReadEntries)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Fail("Unsupported platform");
|
||||
}
|
||||
}
|
||||
|
||||
private bool PlaceholderHasVersionInfo(string relativePath, int version, string sha)
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return NativeTests.PlaceHolderHasVersionInfo(relativePath, version, sha);
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
// TODO(#1360): Add a version of PlaceHolderHasVersionInfo that works on Mac
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Fail("Unsupported platform");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private class NativeTests
|
||||
{
|
||||
[DllImport("GVFS.NativeTests.dll")]
|
||||
public static extern bool EnumerateAndReadDoesNotChangeEnumerationOrder(string folderVirtualPath);
|
||||
|
||||
[DllImport("GVFS.NativeTests.dll", CharSet = CharSet.Ansi)]
|
||||
public static extern bool PlaceHolderHasVersionInfo(
|
||||
string virtualPath,
|
||||
int version,
|
||||
[MarshalAs(UnmanagedType.LPWStr)]string sha);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,109 +0,0 @@
|
|||
using GVFS.FunctionalTests.FileSystemRunners;
|
||||
using GVFS.FunctionalTests.Should;
|
||||
using GVFS.FunctionalTests.Tools;
|
||||
using GVFS.Tests.Should;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace GVFS.FunctionalTests.Tests.EnlistmentPerTestCase
|
||||
{
|
||||
public abstract class DiskLayoutUpgradeTests : TestsWithEnlistmentPerTestCase
|
||||
{
|
||||
protected static readonly string PlaceholderListDatabaseContent = $@"A .gitignore{GVFSHelpers.PlaceholderFieldDelimiter}E9630E4CF715315FC90D4AEC98E16A7398F8BF64
|
||||
A Readme.md{GVFSHelpers.PlaceholderFieldDelimiter}583F1A56DB7CC884D54534C5D9C56B93A1E00A2B
|
||||
A Scripts{GVFSHelpers.PlaceholderFieldDelimiter}{TestConstants.PartialFolderPlaceholderDatabaseValue}
|
||||
A Scripts{Path.DirectorySeparatorChar}RunUnitTests.bat{GVFSHelpers.PlaceholderFieldDelimiter}0112E0DD6FC64BF57C4735F4D7D6E018C0F34B6D
|
||||
A GVFS{GVFSHelpers.PlaceholderFieldDelimiter}{TestConstants.PartialFolderPlaceholderDatabaseValue}
|
||||
A GVFS{Path.DirectorySeparatorChar}GVFS.Common{GVFSHelpers.PlaceholderFieldDelimiter}{TestConstants.PartialFolderPlaceholderDatabaseValue}
|
||||
A GVFS{Path.DirectorySeparatorChar}GVFS.Common{Path.DirectorySeparatorChar}Git{GVFSHelpers.PlaceholderFieldDelimiter}{TestConstants.PartialFolderPlaceholderDatabaseValue}
|
||||
A GVFS{Path.DirectorySeparatorChar}GVFS.Common{Path.DirectorySeparatorChar}Git{Path.DirectorySeparatorChar}GitRefs.cs{GVFSHelpers.PlaceholderFieldDelimiter}37595A9C6C7E00A8AFDE306765896770F2508927
|
||||
A GVFS{Path.DirectorySeparatorChar}GVFS.Tests{GVFSHelpers.PlaceholderFieldDelimiter}{TestConstants.PartialFolderPlaceholderDatabaseValue}
|
||||
A GVFS{Path.DirectorySeparatorChar}GVFS.Tests{Path.DirectorySeparatorChar}Properties{GVFSHelpers.PlaceholderFieldDelimiter}{TestConstants.PartialFolderPlaceholderDatabaseValue}
|
||||
A GVFS{Path.DirectorySeparatorChar}GVFS.Tests{Path.DirectorySeparatorChar}Properties{Path.DirectorySeparatorChar}AssemblyInfo.cs{GVFSHelpers.PlaceholderFieldDelimiter}5911485CFE87E880F64B300BA5A289498622DBC1
|
||||
D GVFS{Path.DirectorySeparatorChar}GVFS.Tests{Path.DirectorySeparatorChar}Properties{Path.DirectorySeparatorChar}AssemblyInfo.cs
|
||||
";
|
||||
|
||||
protected FileSystemRunner fileSystem = new SystemIORunner();
|
||||
|
||||
private const string PlaceholderTableFilePathType = "0";
|
||||
private const string PlaceholderTablePartialFolderPathType = "1";
|
||||
|
||||
public abstract int GetCurrentDiskLayoutMajorVersion();
|
||||
public abstract int GetCurrentDiskLayoutMinorVersion();
|
||||
|
||||
protected void PlaceholderDatabaseShouldIncludeCommonLines(string[] placeholderLines)
|
||||
{
|
||||
placeholderLines.ShouldContain(x => x.Contains(this.FilePlaceholderString("Readme.md")));
|
||||
placeholderLines.ShouldContain(x => x.Contains(this.FilePlaceholderString("Scripts", "RunUnitTests.bat")));
|
||||
placeholderLines.ShouldContain(x => x.Contains(this.FilePlaceholderString("GVFS", "GVFS.Common", "Git", "GitRefs.cs")));
|
||||
placeholderLines.ShouldContain(x => x.Contains(this.FilePlaceholderString(".gitignore")));
|
||||
placeholderLines.ShouldContain(x => x == this.PartialFolderPlaceholderString("Scripts"));
|
||||
placeholderLines.ShouldContain(x => x == this.PartialFolderPlaceholderString("GVFS"));
|
||||
placeholderLines.ShouldContain(x => x == this.PartialFolderPlaceholderString("GVFS", "GVFS.Common"));
|
||||
placeholderLines.ShouldContain(x => x == this.PartialFolderPlaceholderString("GVFS", "GVFS.Common", "Git"));
|
||||
placeholderLines.ShouldContain(x => x == this.PartialFolderPlaceholderString("GVFS", "GVFS.Tests"));
|
||||
}
|
||||
|
||||
protected void WriteOldPlaceholderListDatabase()
|
||||
{
|
||||
this.fileSystem.WriteAllText(Path.Combine(this.Enlistment.DotGVFSRoot, GVFSHelpers.PlaceholderListFile), PlaceholderListDatabaseContent);
|
||||
}
|
||||
|
||||
protected void PerformIOBeforePlaceholderDatabaseUpgradeTest()
|
||||
{
|
||||
// Create some placeholder data
|
||||
this.fileSystem.ReadAllText(Path.Combine(this.Enlistment.RepoRoot, "Readme.md"));
|
||||
this.fileSystem.ReadAllText(Path.Combine(this.Enlistment.RepoRoot, "Scripts", "RunUnitTests.bat"));
|
||||
this.fileSystem.ReadAllText(Path.Combine(this.Enlistment.RepoRoot, "GVFS", "GVFS.Common", "Git", "GitRefs.cs"));
|
||||
|
||||
// Create a full folder
|
||||
this.fileSystem.CreateDirectory(Path.Combine(this.Enlistment.RepoRoot, "GVFS", "FullFolder"));
|
||||
this.fileSystem.WriteAllText(Path.Combine(this.Enlistment.RepoRoot, "GVFS", "FullFolder", "test.txt"), "Test contents");
|
||||
|
||||
// Create a tombstone
|
||||
this.fileSystem.DeleteDirectory(Path.Combine(this.Enlistment.RepoRoot, "GVFS", "GVFS.Tests", "Properties"));
|
||||
|
||||
string junctionTarget = Path.Combine(this.Enlistment.EnlistmentRoot, "DirJunction");
|
||||
string symLinkTarget = Path.Combine(this.Enlistment.EnlistmentRoot, "DirSymLink");
|
||||
Directory.CreateDirectory(junctionTarget);
|
||||
Directory.CreateDirectory(symLinkTarget);
|
||||
|
||||
string junctionLink = Path.Combine(this.Enlistment.RepoRoot, "DirJunction");
|
||||
string symLink = Path.Combine(this.Enlistment.RepoRoot, "DirLink");
|
||||
ProcessHelper.Run("CMD.exe", "/C mklink /J " + junctionLink + " " + junctionTarget);
|
||||
ProcessHelper.Run("CMD.exe", "/C mklink /D " + symLink + " " + symLinkTarget);
|
||||
|
||||
string target = Path.Combine(this.Enlistment.EnlistmentRoot, "GVFS", "GVFS", "GVFS.UnitTests");
|
||||
string link = Path.Combine(this.Enlistment.RepoRoot, "UnitTests");
|
||||
ProcessHelper.Run("CMD.exe", "/C mklink /J " + link + " " + target);
|
||||
target = Path.Combine(this.Enlistment.EnlistmentRoot, "GVFS", "GVFS", "GVFS.Installer");
|
||||
link = Path.Combine(this.Enlistment.RepoRoot, "Installer");
|
||||
ProcessHelper.Run("CMD.exe", "/C mklink /D " + link + " " + target);
|
||||
}
|
||||
|
||||
protected string FilePlaceholderString(params string[] pathParts)
|
||||
{
|
||||
return $"{Path.Combine(pathParts)}{GVFSHelpers.PlaceholderFieldDelimiter}{PlaceholderTableFilePathType}{GVFSHelpers.PlaceholderFieldDelimiter}";
|
||||
}
|
||||
|
||||
protected string PartialFolderPlaceholderString(params string[] pathParts)
|
||||
{
|
||||
return $"{Path.Combine(pathParts)}{GVFSHelpers.PlaceholderFieldDelimiter}{PlaceholderTablePartialFolderPathType}{GVFSHelpers.PlaceholderFieldDelimiter}";
|
||||
}
|
||||
|
||||
protected void ValidatePersistedVersionMatchesCurrentVersion()
|
||||
{
|
||||
string majorVersion;
|
||||
string minorVersion;
|
||||
GVFSHelpers.GetPersistedDiskLayoutVersion(this.Enlistment.DotGVFSRoot, out majorVersion, out minorVersion);
|
||||
|
||||
majorVersion
|
||||
.ShouldBeAnInt("Disk layout version should always be an int")
|
||||
.ShouldEqual(this.GetCurrentDiskLayoutMajorVersion(), "Disk layout version should be upgraded to the latest");
|
||||
|
||||
minorVersion
|
||||
.ShouldBeAnInt("Disk layout version should always be an int")
|
||||
.ShouldEqual(this.GetCurrentDiskLayoutMinorVersion(), "Disk layout version should be upgraded to the latest");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
using GVFS.FunctionalTests.Should;
|
||||
using GVFS.FunctionalTests.Tools;
|
||||
using GVFS.Tests.Should;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace GVFS.FunctionalTests.Tests.EnlistmentPerTestCase
|
||||
{
|
||||
[TestFixture]
|
||||
[Category(Categories.ExtraCoverage)]
|
||||
[Category(Categories.MacOnly)]
|
||||
public class MacDiskLayoutUpgradeTests : DiskLayoutUpgradeTests
|
||||
{
|
||||
public const int CurrentDiskLayoutMajorVersion = 19;
|
||||
public const int CurrentDiskLayoutMinorVersion = 0;
|
||||
|
||||
public override int GetCurrentDiskLayoutMajorVersion() => CurrentDiskLayoutMajorVersion;
|
||||
public override int GetCurrentDiskLayoutMinorVersion() => CurrentDiskLayoutMinorVersion;
|
||||
|
||||
[TestCase]
|
||||
public void MountUpgradesPlaceholderListDatabaseToSQLite()
|
||||
{
|
||||
this.Enlistment.UnmountGVFS();
|
||||
|
||||
this.fileSystem.DeleteFile(Path.Combine(this.Enlistment.DotGVFSRoot, TestConstants.Databases.VFSForGit));
|
||||
this.WriteOldPlaceholderListDatabase();
|
||||
|
||||
GVFSHelpers.SaveDiskLayoutVersion(this.Enlistment.DotGVFSRoot, "18", "0");
|
||||
|
||||
this.Enlistment.MountGVFS();
|
||||
this.Enlistment.UnmountGVFS();
|
||||
|
||||
// Validate the placeholders are in the SQLite placeholder database now
|
||||
string placeholderDatabasePath = Path.Combine(this.Enlistment.DotGVFSRoot, TestConstants.Databases.VFSForGit);
|
||||
placeholderDatabasePath.ShouldBeAFile(this.fileSystem);
|
||||
string[] lines = GVFSHelpers.GetAllSQLitePlaceholdersAsString(placeholderDatabasePath).Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
lines.Length.ShouldEqual(10);
|
||||
this.PlaceholderDatabaseShouldIncludeCommonLines(lines);
|
||||
lines.ShouldContain(x => x == this.PartialFolderPlaceholderString("GVFS", "GVFS.Tests", "Properties"));
|
||||
|
||||
this.ValidatePersistedVersionMatchesCurrentVersion();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,224 +0,0 @@
|
|||
using GVFS.FunctionalTests.FileSystemRunners;
|
||||
using GVFS.FunctionalTests.Should;
|
||||
using GVFS.FunctionalTests.Tools;
|
||||
using GVFS.Tests.Should;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace GVFS.FunctionalTests.Tests.EnlistmentPerTestCase
|
||||
{
|
||||
[TestFixture]
|
||||
public class ModifiedPathsTests : TestsWithEnlistmentPerTestCase
|
||||
{
|
||||
private static readonly string FileToAdd = Path.Combine("GVFS", "TestAddFile.txt");
|
||||
private static readonly string FileToUpdate = Path.Combine("GVFS", "GVFS", "Program.cs");
|
||||
private static readonly string FileToDelete = "Readme.md";
|
||||
private static readonly string FileToRename = Path.Combine("GVFS", "GVFS.Mount", "MountVerb.cs");
|
||||
private static readonly string RenameFileTarget = Path.Combine("GVFS", "GVFS.Mount", "MountVerb2.cs");
|
||||
private static readonly string FolderToCreate = $"{nameof(ModifiedPathsTests)}_NewFolder";
|
||||
private static readonly string FolderToRename = $"{nameof(ModifiedPathsTests)}_NewFolderForRename";
|
||||
private static readonly string RenameFolderTarget = $"{nameof(ModifiedPathsTests)}_NewFolderForRename2";
|
||||
private static readonly string DotGitFileToCreate = Path.Combine(".git", "TestFileFromDotGit.txt");
|
||||
private static readonly string RenameNewDotGitFileTarget = "TestFileFromDotGit.txt";
|
||||
private static readonly string FileToCreateOutsideRepo = $"{nameof(ModifiedPathsTests)}_outsideRepo.txt";
|
||||
private static readonly string FolderToCreateOutsideRepo = $"{nameof(ModifiedPathsTests)}_outsideFolder";
|
||||
private static readonly string FolderToDelete = "Scripts";
|
||||
private static readonly string[] ExpectedModifiedFilesContentsAfterRemount =
|
||||
{
|
||||
$"A .gitattributes",
|
||||
$"A {GVFSHelpers.ConvertPathToGitFormat(FileToAdd)}",
|
||||
$"A {GVFSHelpers.ConvertPathToGitFormat(FileToUpdate)}",
|
||||
$"A {FileToDelete}",
|
||||
$"A {GVFSHelpers.ConvertPathToGitFormat(FileToRename)}",
|
||||
$"A {GVFSHelpers.ConvertPathToGitFormat(RenameFileTarget)}",
|
||||
$"A {FolderToCreate}/",
|
||||
$"A {RenameNewDotGitFileTarget}",
|
||||
$"A {FileToCreateOutsideRepo}",
|
||||
$"A {FolderToCreateOutsideRepo}/",
|
||||
$"A {FolderToDelete}/",
|
||||
};
|
||||
|
||||
[TestCaseSource(typeof(FileSystemRunner), nameof(FileSystemRunner.Runners))]
|
||||
public void DeletedTempFileIsRemovedFromModifiedFiles(FileSystemRunner fileSystem)
|
||||
{
|
||||
string tempFile = this.CreateFile(fileSystem, "temp.txt");
|
||||
fileSystem.DeleteFile(tempFile);
|
||||
tempFile.ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, fileSystem, "temp.txt");
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileSystemRunner), nameof(FileSystemRunner.Runners))]
|
||||
public void DeletedTempFolderIsRemovedFromModifiedFiles(FileSystemRunner fileSystem)
|
||||
{
|
||||
string tempFolder = this.CreateDirectory(fileSystem, "Temp");
|
||||
fileSystem.DeleteDirectory(tempFolder);
|
||||
tempFolder.ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, fileSystem, "Temp/");
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileSystemRunner), nameof(FileSystemRunner.Runners))]
|
||||
public void DeletedTempFolderDeletesFilesFromModifiedFiles(FileSystemRunner fileSystem)
|
||||
{
|
||||
string tempFolder = this.CreateDirectory(fileSystem, "Temp");
|
||||
string tempFile1 = this.CreateFile(fileSystem, Path.Combine("Temp", "temp1.txt"));
|
||||
string tempFile2 = this.CreateFile(fileSystem, Path.Combine("Temp", "temp2.txt"));
|
||||
fileSystem.DeleteDirectory(tempFolder);
|
||||
tempFolder.ShouldNotExistOnDisk(fileSystem);
|
||||
tempFile1.ShouldNotExistOnDisk(fileSystem);
|
||||
tempFile2.ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, fileSystem, "Temp/", "Temp/temp1.txt", "Temp/temp2.txt");
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileSystemRunner), nameof(FileSystemRunner.Runners))]
|
||||
[Category(Categories.MacTODO.NeedsNewFolderCreateNotification)]
|
||||
public void ModifiedPathsSavedAfterRemount(FileSystemRunner fileSystem)
|
||||
{
|
||||
string fileToAdd = this.Enlistment.GetVirtualPathTo(FileToAdd);
|
||||
fileSystem.WriteAllText(fileToAdd, "Contents for the new file");
|
||||
|
||||
string fileToUpdate = this.Enlistment.GetVirtualPathTo(FileToUpdate);
|
||||
fileSystem.AppendAllText(fileToUpdate, "// Testing");
|
||||
|
||||
string fileToDelete = this.Enlistment.GetVirtualPathTo(FileToDelete);
|
||||
fileSystem.DeleteFile(fileToDelete);
|
||||
fileToDelete.ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
string fileToRename = this.Enlistment.GetVirtualPathTo(FileToRename);
|
||||
fileSystem.MoveFile(fileToRename, this.Enlistment.GetVirtualPathTo(RenameFileTarget));
|
||||
|
||||
string folderToCreate = this.Enlistment.GetVirtualPathTo(FolderToCreate);
|
||||
fileSystem.CreateDirectory(folderToCreate);
|
||||
|
||||
string folderToRename = this.Enlistment.GetVirtualPathTo(FolderToRename);
|
||||
fileSystem.CreateDirectory(folderToRename);
|
||||
string folderToRenameTarget = this.Enlistment.GetVirtualPathTo(RenameFolderTarget);
|
||||
fileSystem.MoveDirectory(folderToRename, folderToRenameTarget);
|
||||
|
||||
// Moving the new folder out of the repo will remove it from the modified paths file
|
||||
string folderTargetOutsideSrc = Path.Combine(this.Enlistment.EnlistmentRoot, RenameFolderTarget);
|
||||
folderTargetOutsideSrc.ShouldNotExistOnDisk(fileSystem);
|
||||
fileSystem.MoveDirectory(folderToRenameTarget, folderTargetOutsideSrc);
|
||||
folderTargetOutsideSrc.ShouldBeADirectory(fileSystem);
|
||||
folderToRenameTarget.ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
// Moving a file from the .git folder to the working directory should add the file to the modified paths
|
||||
string dotGitfileToAdd = this.Enlistment.GetVirtualPathTo(DotGitFileToCreate);
|
||||
fileSystem.WriteAllText(dotGitfileToAdd, "Contents for the new file in dot git");
|
||||
fileSystem.MoveFile(dotGitfileToAdd, this.Enlistment.GetVirtualPathTo(RenameNewDotGitFileTarget));
|
||||
|
||||
// Move a file from outside of src into src
|
||||
string fileToCreateOutsideRepoPath = Path.Combine(this.Enlistment.EnlistmentRoot, FileToCreateOutsideRepo);
|
||||
fileSystem.WriteAllText(fileToCreateOutsideRepoPath, "Contents for the new file outside of repo");
|
||||
string fileToCreateOutsideRepoTargetPath = this.Enlistment.GetVirtualPathTo(FileToCreateOutsideRepo);
|
||||
fileToCreateOutsideRepoTargetPath.ShouldNotExistOnDisk(fileSystem);
|
||||
fileSystem.MoveFile(fileToCreateOutsideRepoPath, fileToCreateOutsideRepoTargetPath);
|
||||
fileToCreateOutsideRepoTargetPath.ShouldBeAFile(fileSystem);
|
||||
fileToCreateOutsideRepoPath.ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
// Move a folder from outside of src into src
|
||||
string folderToCreateOutsideRepoPath = Path.Combine(this.Enlistment.EnlistmentRoot, FolderToCreateOutsideRepo);
|
||||
fileSystem.CreateDirectory(folderToCreateOutsideRepoPath);
|
||||
folderToCreateOutsideRepoPath.ShouldBeADirectory(fileSystem);
|
||||
string folderToCreateOutsideRepoTargetPath = this.Enlistment.GetVirtualPathTo(FolderToCreateOutsideRepo);
|
||||
folderToCreateOutsideRepoTargetPath.ShouldNotExistOnDisk(fileSystem);
|
||||
fileSystem.MoveDirectory(folderToCreateOutsideRepoPath, folderToCreateOutsideRepoTargetPath);
|
||||
folderToCreateOutsideRepoTargetPath.ShouldBeADirectory(fileSystem);
|
||||
folderToCreateOutsideRepoPath.ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
string folderToDeleteFullPath = this.Enlistment.GetVirtualPathTo(FolderToDelete);
|
||||
fileSystem.WriteAllText(Path.Combine(folderToDeleteFullPath, "NewFile.txt"), "Contents for new file");
|
||||
string newFileToDelete = Path.Combine(folderToDeleteFullPath, "NewFileToDelete.txt");
|
||||
fileSystem.WriteAllText(newFileToDelete, "Contents for new file");
|
||||
fileSystem.DeleteFile(newFileToDelete);
|
||||
fileSystem.WriteAllText(Path.Combine(folderToDeleteFullPath, "CreateCommonVersionHeader.bat"), "Changing the file contents");
|
||||
fileSystem.DeleteFile(Path.Combine(folderToDeleteFullPath, "RunUnitTests.bat"));
|
||||
|
||||
fileSystem.DeleteDirectory(folderToDeleteFullPath);
|
||||
folderToDeleteFullPath.ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
// Remount
|
||||
this.Enlistment.UnmountGVFS();
|
||||
this.Enlistment.MountGVFS();
|
||||
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
|
||||
string modifiedPathsDatabase = Path.Combine(this.Enlistment.DotGVFSRoot, TestConstants.Databases.ModifiedPaths);
|
||||
modifiedPathsDatabase.ShouldBeAFile(fileSystem);
|
||||
using (StreamReader reader = new StreamReader(File.Open(modifiedPathsDatabase, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
|
||||
{
|
||||
reader.ReadToEnd().Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).OrderBy(x => x)
|
||||
.ShouldMatchInOrder(ExpectedModifiedFilesContentsAfterRemount.OrderBy(x => x));
|
||||
}
|
||||
}
|
||||
|
||||
[TestCaseSource(typeof(FileSystemRunner), nameof(FileSystemRunner.Runners))]
|
||||
public void ModifiedPathsCorrectAfterHardLinking(FileSystemRunner fileSystem)
|
||||
{
|
||||
string[] expectedModifiedFilesContentsAfterHardlinks =
|
||||
{
|
||||
"A .gitattributes",
|
||||
"A LinkToReadme.md",
|
||||
"A LinkToFileOutsideSrc.txt",
|
||||
"A Readme.md",
|
||||
"A GVFS/GVFS/Program.cs",
|
||||
};
|
||||
|
||||
// Create a link from src\LinkToReadme.md to src\Readme.md
|
||||
string existingFileInRepoPath = this.Enlistment.GetVirtualPathTo("Readme.md");
|
||||
string contents = existingFileInRepoPath.ShouldBeAFile(fileSystem).WithContents();
|
||||
string hardLinkToFileInRepoPath = this.Enlistment.GetVirtualPathTo("LinkToReadme.md");
|
||||
hardLinkToFileInRepoPath.ShouldNotExistOnDisk(fileSystem);
|
||||
fileSystem.CreateHardLink(hardLinkToFileInRepoPath, existingFileInRepoPath);
|
||||
hardLinkToFileInRepoPath.ShouldBeAFile(fileSystem).WithContents(contents);
|
||||
|
||||
// Create a link from src\LinkToFileOutsideSrc.txt to FileOutsideRepo.txt
|
||||
string fileOutsideOfRepoPath = Path.Combine(this.Enlistment.EnlistmentRoot, "FileOutsideRepo.txt");
|
||||
string fileOutsideOfRepoContents = "File outside of repo";
|
||||
fileOutsideOfRepoPath.ShouldNotExistOnDisk(fileSystem);
|
||||
fileSystem.WriteAllText(fileOutsideOfRepoPath, fileOutsideOfRepoContents);
|
||||
string hardLinkToFileOutsideRepoPath = this.Enlistment.GetVirtualPathTo("LinkToFileOutsideSrc.txt");
|
||||
hardLinkToFileOutsideRepoPath.ShouldNotExistOnDisk(fileSystem);
|
||||
fileSystem.CreateHardLink(hardLinkToFileOutsideRepoPath, fileOutsideOfRepoPath);
|
||||
hardLinkToFileOutsideRepoPath.ShouldBeAFile(fileSystem).WithContents(fileOutsideOfRepoContents);
|
||||
|
||||
// Create a link from LinkOutsideSrcToInsideSrc.cs to src\GVFS\GVFS\Program.cs
|
||||
string secondFileInRepoPath = this.Enlistment.GetVirtualPathTo("GVFS", "GVFS", "Program.cs");
|
||||
contents = secondFileInRepoPath.ShouldBeAFile(fileSystem).WithContents();
|
||||
string hardLinkOutsideRepoToFileInRepoPath = Path.Combine(this.Enlistment.EnlistmentRoot, "LinkOutsideSrcToInsideSrc.cs");
|
||||
hardLinkOutsideRepoToFileInRepoPath.ShouldNotExistOnDisk(fileSystem);
|
||||
fileSystem.CreateHardLink(hardLinkOutsideRepoToFileInRepoPath, secondFileInRepoPath);
|
||||
hardLinkOutsideRepoToFileInRepoPath.ShouldBeAFile(fileSystem).WithContents(contents);
|
||||
|
||||
this.Enlistment.WaitForBackgroundOperations();
|
||||
|
||||
string modifiedPathsDatabase = Path.Combine(this.Enlistment.DotGVFSRoot, TestConstants.Databases.ModifiedPaths);
|
||||
modifiedPathsDatabase.ShouldBeAFile(fileSystem);
|
||||
using (StreamReader reader = new StreamReader(File.Open(modifiedPathsDatabase, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
|
||||
{
|
||||
reader.ReadToEnd().Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).OrderBy(x => x)
|
||||
.ShouldMatchInOrder(expectedModifiedFilesContentsAfterHardlinks.OrderBy(x => x));
|
||||
}
|
||||
}
|
||||
|
||||
private string CreateDirectory(FileSystemRunner fileSystem, string relativePath)
|
||||
{
|
||||
string tempFolder = this.Enlistment.GetVirtualPathTo(relativePath);
|
||||
fileSystem.CreateDirectory(tempFolder);
|
||||
tempFolder.ShouldBeADirectory(fileSystem);
|
||||
return tempFolder;
|
||||
}
|
||||
|
||||
private string CreateFile(FileSystemRunner fileSystem, string relativePath)
|
||||
{
|
||||
string tempFile = this.Enlistment.GetVirtualPathTo(relativePath);
|
||||
fileSystem.WriteAllText(tempFile, $"Contents for the {relativePath} file");
|
||||
tempFile.ShouldBeAFile(fileSystem);
|
||||
return tempFile;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,177 +0,0 @@
|
|||
using GVFS.FunctionalTests.FileSystemRunners;
|
||||
using GVFS.FunctionalTests.Should;
|
||||
using GVFS.Tests.Should;
|
||||
using NUnit.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace GVFS.FunctionalTests.Tests.EnlistmentPerTestCase
|
||||
{
|
||||
[TestFixture]
|
||||
[Category(Categories.ExtraCoverage)]
|
||||
public class PersistedWorkingDirectoryTests : TestsWithEnlistmentPerTestCase
|
||||
{
|
||||
[TestCaseSource(typeof(FileSystemRunner), nameof(FileSystemRunner.Runners))]
|
||||
public void PersistedDirectoryLazyLoad(FileSystemRunner fileSystem)
|
||||
{
|
||||
string enumerateDirectoryName = Path.Combine("GVFS", "GVFS");
|
||||
|
||||
string[] subFolders = new string[]
|
||||
{
|
||||
Path.Combine(enumerateDirectoryName, "Properties"),
|
||||
Path.Combine(enumerateDirectoryName, "CommandLine")
|
||||
};
|
||||
|
||||
string[] subFiles = new string[]
|
||||
{
|
||||
Path.Combine(enumerateDirectoryName, "App.config"),
|
||||
Path.Combine(enumerateDirectoryName, "GitVirtualFileSystem.ico"),
|
||||
Path.Combine(enumerateDirectoryName, "GVFS.csproj"),
|
||||
Path.Combine(enumerateDirectoryName, "packages.config"),
|
||||
Path.Combine(enumerateDirectoryName, "Program.cs"),
|
||||
Path.Combine(enumerateDirectoryName, "Setup.iss")
|
||||
};
|
||||
|
||||
string enumerateDirectoryPath = this.Enlistment.GetVirtualPathTo(enumerateDirectoryName);
|
||||
fileSystem.DirectoryExists(enumerateDirectoryPath).ShouldEqual(true);
|
||||
|
||||
foreach (string folder in subFolders)
|
||||
{
|
||||
string directoryPath = this.Enlistment.GetVirtualPathTo(folder);
|
||||
fileSystem.DirectoryExists(directoryPath).ShouldEqual(true);
|
||||
}
|
||||
|
||||
foreach (string file in subFiles)
|
||||
{
|
||||
string filePath = this.Enlistment.GetVirtualPathTo(file);
|
||||
fileSystem.FileExists(filePath).ShouldEqual(true);
|
||||
}
|
||||
|
||||
this.Enlistment.UnmountGVFS();
|
||||
this.Enlistment.MountGVFS();
|
||||
|
||||
foreach (string folder in subFolders)
|
||||
{
|
||||
string directoryPath = this.Enlistment.GetVirtualPathTo(folder);
|
||||
fileSystem.DirectoryExists(directoryPath).ShouldEqual(true);
|
||||
}
|
||||
|
||||
foreach (string file in subFiles)
|
||||
{
|
||||
string filePath = this.Enlistment.GetVirtualPathTo(file);
|
||||
fileSystem.FileExists(filePath).ShouldEqual(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This test is intentionally one monolithic test. Because we have to mount/remount to
|
||||
/// test persistence, we want to save as much time in tests runs as possible by only
|
||||
/// remounting once.
|
||||
/// </summary>
|
||||
[TestCaseSource(typeof(FileSystemRunner), nameof(FileSystemRunner.Runners))]
|
||||
public void PersistedDirectoryTests(FileSystemRunner fileSystem)
|
||||
{
|
||||
// Delete File Setup
|
||||
string deleteFileName = ".gitattributes";
|
||||
string deleteFilepath = this.Enlistment.GetVirtualPathTo(deleteFileName);
|
||||
fileSystem.DeleteFile(deleteFilepath);
|
||||
|
||||
// Delete Folder Setup
|
||||
string deleteFolderName = Path.Combine("GVFS", "GVFS");
|
||||
string deleteFolderPath = this.Enlistment.GetVirtualPathTo(deleteFolderName);
|
||||
fileSystem.DeleteDirectory(deleteFolderPath);
|
||||
|
||||
// Add File Setup
|
||||
string fileToAdd = "NewFile.txt";
|
||||
string fileToAddContent = "This is new file text.";
|
||||
string fileToAddPath = this.Enlistment.GetVirtualPathTo(fileToAdd);
|
||||
fileSystem.WriteAllText(fileToAddPath, fileToAddContent);
|
||||
|
||||
// Add Folder Setup
|
||||
string directoryToAdd = "NewDirectory";
|
||||
string directoryToAddPath = this.Enlistment.GetVirtualPathTo(directoryToAdd);
|
||||
fileSystem.CreateDirectory(directoryToAddPath);
|
||||
|
||||
// Move File Setup
|
||||
string fileToMove = this.Enlistment.GetVirtualPathTo("FileToMove.txt");
|
||||
string fileToMoveNewPath = this.Enlistment.GetVirtualPathTo("MovedFile.txt");
|
||||
string fileToMoveContent = "This is new file text.";
|
||||
fileSystem.WriteAllText(fileToMove, fileToMoveContent);
|
||||
fileSystem.MoveFile(fileToMove, fileToMoveNewPath);
|
||||
|
||||
// Replace File Setup
|
||||
string fileToReplace = this.Enlistment.GetVirtualPathTo("FileToReplace.txt");
|
||||
string fileToReplaceNewPath = this.Enlistment.GetVirtualPathTo("ReplacedFile.txt");
|
||||
string fileToReplaceContent = "This is new file text.";
|
||||
string fileToReplaceOldContent = "This is very different file text.";
|
||||
fileSystem.WriteAllText(fileToReplace, fileToReplaceContent);
|
||||
fileSystem.WriteAllText(fileToReplaceNewPath, fileToReplaceOldContent);
|
||||
fileSystem.ReplaceFile(fileToReplace, fileToReplaceNewPath);
|
||||
|
||||
// MoveFolderPersistsOnRemount Setup
|
||||
string directoryToMove = this.Enlistment.GetVirtualPathTo("MoveDirectory");
|
||||
string directoryMoveTarget = this.Enlistment.GetVirtualPathTo("MoveDirectoryTarget");
|
||||
string newDirectory = Path.Combine(directoryMoveTarget, "MoveDirectory_renamed");
|
||||
string childFile = Path.Combine(directoryToMove, "MoveFile.txt");
|
||||
string movedChildFile = Path.Combine(newDirectory, "MoveFile.txt");
|
||||
string moveFileContents = "This text file is getting moved";
|
||||
fileSystem.CreateDirectory(directoryToMove);
|
||||
fileSystem.CreateDirectory(directoryMoveTarget);
|
||||
fileSystem.WriteAllText(childFile, moveFileContents);
|
||||
fileSystem.MoveDirectory(directoryToMove, newDirectory);
|
||||
|
||||
// NestedLoadAndWriteAfterMount Setup
|
||||
// Write a file to GVFS to ensure it has a physical folder
|
||||
string childFileToAdd = Path.Combine("GVFS", "ChildFileToAdd.txt");
|
||||
string childFileToAddContent = "This is new child file in the GVFS folder.";
|
||||
string childFileToAddPath = this.Enlistment.GetVirtualPathTo(childFileToAdd);
|
||||
fileSystem.WriteAllText(childFileToAddPath, childFileToAddContent);
|
||||
|
||||
// Remount
|
||||
this.Enlistment.UnmountGVFS();
|
||||
this.Enlistment.MountGVFS();
|
||||
|
||||
// Delete File Validation
|
||||
deleteFilepath.ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
// Delete Folder Validation
|
||||
deleteFolderPath.ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
// Add File Validation
|
||||
fileToAddPath.ShouldBeAFile(fileSystem).WithContents().ShouldEqual(fileToAddContent);
|
||||
|
||||
// Add Folder Validation
|
||||
directoryToAddPath.ShouldBeADirectory(fileSystem);
|
||||
|
||||
// Move File Validation
|
||||
fileToMove.ShouldNotExistOnDisk(fileSystem);
|
||||
fileToMoveNewPath.ShouldBeAFile(fileSystem).WithContents().ShouldEqual(fileToMoveContent);
|
||||
|
||||
// Replace File Validation
|
||||
fileToReplace.ShouldNotExistOnDisk(fileSystem);
|
||||
fileToReplaceNewPath.ShouldBeAFile(fileSystem).WithContents().ShouldEqual(fileToReplaceContent);
|
||||
|
||||
// MoveFolderPersistsOnRemount Validation
|
||||
directoryToMove.ShouldNotExistOnDisk(fileSystem);
|
||||
|
||||
directoryMoveTarget.ShouldBeADirectory(fileSystem);
|
||||
newDirectory.ShouldBeADirectory(fileSystem);
|
||||
movedChildFile.ShouldBeAFile(fileSystem).WithContents().ShouldEqual(moveFileContents);
|
||||
|
||||
// NestedLoadAndWriteAfterMount Validation
|
||||
childFileToAddPath.ShouldBeAFile(fileSystem).WithContents().ShouldEqual(childFileToAddContent);
|
||||
string childFolder = Path.Combine("GVFS", "GVFS.FunctionalTests");
|
||||
string childFolderPath = this.Enlistment.GetVirtualPathTo(childFolder);
|
||||
childFolderPath.ShouldBeADirectory(fileSystem);
|
||||
string postMountChildFile = "PostMountChildFile.txt";
|
||||
string postMountChildFileContent = "This is new child file added after the mount";
|
||||
string postMountChildFilePath = this.Enlistment.GetVirtualPathTo(Path.Combine(childFolder, postMountChildFile));
|
||||
fileSystem.WriteAllText(postMountChildFilePath, postMountChildFileContent); // Verify we can create files in subfolders of GVFS
|
||||
postMountChildFilePath.ShouldBeAFile(fileSystem).WithContents().ShouldEqual(postMountChildFileContent);
|
||||
|
||||
// 663045 - Ensure that folder can be deleted after a new file is added and GVFS is remounted
|
||||
fileSystem.DeleteDirectory(childFolderPath);
|
||||
childFolderPath.ShouldNotExistOnDisk(fileSystem);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,532 +0,0 @@
|
|||
using GVFS.FunctionalTests.FileSystemRunners;
|
||||
using GVFS.FunctionalTests.Properties;
|
||||
using GVFS.FunctionalTests.Should;
|
||||
using GVFS.FunctionalTests.Tools;
|
||||
using GVFS.Tests.Should;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace GVFS.FunctionalTests.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
[Category(Categories.FastFetch)]
|
||||
[Category(Categories.ExtraCoverage)]
|
||||
public class FastFetchTests
|
||||
{
|
||||
private const string LsTreeTypeInPathBranchName = "FunctionalTests/20181105_LsTreeTypeInPath";
|
||||
|
||||
private readonly string fastFetchRepoRoot = Settings.Default.FastFetchRoot;
|
||||
private readonly string fastFetchControlRoot = Settings.Default.FastFetchControl;
|
||||
private readonly string fastFetchBaseRoot = Settings.Default.FastFetchBaseRoot;
|
||||
|
||||
[OneTimeSetUp]
|
||||
public void InitControlRepo()
|
||||
{
|
||||
Directory.CreateDirectory(this.fastFetchControlRoot);
|
||||
GitProcess.Invoke(this.fastFetchBaseRoot, "clone -b " + Settings.Default.Commitish + " " + GVFSTestConfig.RepoToClone + " " + this.fastFetchControlRoot);
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void InitRepo()
|
||||
{
|
||||
// Just in case Teardown did not run. Say when debugging...
|
||||
if (Directory.Exists(this.fastFetchRepoRoot))
|
||||
{
|
||||
this.TearDownTests();
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(this.fastFetchRepoRoot);
|
||||
GitProcess.Invoke(this.fastFetchRepoRoot, "init");
|
||||
GitProcess.Invoke(this.fastFetchRepoRoot, "remote add origin " + GVFSTestConfig.RepoToClone);
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDownTests()
|
||||
{
|
||||
RepositoryHelpers.DeleteTestDirectory(this.fastFetchRepoRoot);
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public void DeleteControlRepo()
|
||||
{
|
||||
RepositoryHelpers.DeleteTestDirectory(this.fastFetchControlRoot);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CanFetchIntoEmptyGitRepoAndCheckoutWithGit()
|
||||
{
|
||||
this.RunFastFetch("-b " + Settings.Default.Commitish);
|
||||
|
||||
this.GetRefTreeSha("remotes/origin/" + Settings.Default.Commitish).ShouldNotBeNull();
|
||||
|
||||
ProcessResult checkoutResult = GitProcess.InvokeProcess(this.fastFetchRepoRoot, "checkout " + Settings.Default.Commitish);
|
||||
checkoutResult.Errors.ShouldEqual("Switched to a new branch '" + Settings.Default.Commitish + "'\r\n");
|
||||
checkoutResult.Output.ShouldEqual("Branch '" + Settings.Default.Commitish + "' set up to track remote branch '" + Settings.Default.Commitish + "' from 'origin'.\n");
|
||||
|
||||
// When checking out with git, must manually update shallow.
|
||||
ProcessResult updateRefResult = GitProcess.InvokeProcess(this.fastFetchRepoRoot, "update-ref shallow " + Settings.Default.Commitish);
|
||||
updateRefResult.ExitCode.ShouldEqual(0);
|
||||
updateRefResult.Errors.ShouldBeEmpty();
|
||||
updateRefResult.Output.ShouldBeEmpty();
|
||||
|
||||
this.CurrentBranchShouldEqual(Settings.Default.Commitish);
|
||||
|
||||
this.fastFetchRepoRoot.ShouldBeADirectory(FileSystemRunner.DefaultRunner)
|
||||
.WithDeepStructure(FileSystemRunner.DefaultRunner, this.fastFetchControlRoot);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CanFetchAndCheckoutASingleFolderIntoEmptyGitRepo()
|
||||
{
|
||||
this.RunFastFetch("--checkout --folders \"/GVFS\" -b " + Settings.Default.Commitish);
|
||||
|
||||
this.CurrentBranchShouldEqual(Settings.Default.Commitish);
|
||||
|
||||
this.fastFetchRepoRoot.ShouldBeADirectory(FileSystemRunner.DefaultRunner);
|
||||
List<string> dirs = Directory.EnumerateFileSystemEntries(this.fastFetchRepoRoot).ToList();
|
||||
dirs.SequenceEqual(new[]
|
||||
{
|
||||
Path.Combine(this.fastFetchRepoRoot, ".git"),
|
||||
Path.Combine(this.fastFetchRepoRoot, "GVFS"),
|
||||
Path.Combine(this.fastFetchRepoRoot, "GVFS.sln")
|
||||
});
|
||||
|
||||
Directory.EnumerateFileSystemEntries(Path.Combine(this.fastFetchRepoRoot, "GVFS"), "*", SearchOption.AllDirectories)
|
||||
.Count()
|
||||
.ShouldEqual(345);
|
||||
|
||||
this.AllFetchedFilePathsShouldPassCheck(path => path.StartsWith("GVFS", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CanFetchAndCheckoutMultipleTimesUsingForceCheckoutFlag()
|
||||
{
|
||||
this.RunFastFetch($"--checkout --folders \"/GVFS\" -b {Settings.Default.Commitish}");
|
||||
|
||||
this.CurrentBranchShouldEqual(Settings.Default.Commitish);
|
||||
|
||||
this.fastFetchRepoRoot.ShouldBeADirectory(FileSystemRunner.DefaultRunner);
|
||||
List<string> dirs = Directory.EnumerateFileSystemEntries(this.fastFetchRepoRoot).ToList();
|
||||
dirs.SequenceEqual(new[]
|
||||
{
|
||||
Path.Combine(this.fastFetchRepoRoot, ".git"),
|
||||
Path.Combine(this.fastFetchRepoRoot, "GVFS"),
|
||||
Path.Combine(this.fastFetchRepoRoot, "GVFS.sln")
|
||||
});
|
||||
|
||||
Directory.EnumerateFileSystemEntries(Path.Combine(this.fastFetchRepoRoot, "GVFS"), "*", SearchOption.AllDirectories)
|
||||
.Count()
|
||||
.ShouldEqual(345);
|
||||
this.AllFetchedFilePathsShouldPassCheck(path => path.StartsWith("GVFS", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
// Run a second time in the same repo on the same branch with more folders.
|
||||
this.RunFastFetch($"--checkout --folders \"/GVFS;/Scripts\" -b {Settings.Default.Commitish} --force-checkout");
|
||||
dirs = Directory.EnumerateFileSystemEntries(this.fastFetchRepoRoot).ToList();
|
||||
dirs.SequenceEqual(new[]
|
||||
{
|
||||
Path.Combine(this.fastFetchRepoRoot, ".git"),
|
||||
Path.Combine(this.fastFetchRepoRoot, "GVFS"),
|
||||
Path.Combine(this.fastFetchRepoRoot, "Scripts"),
|
||||
Path.Combine(this.fastFetchRepoRoot, "GVFS.sln")
|
||||
});
|
||||
Directory.EnumerateFileSystemEntries(Path.Combine(this.fastFetchRepoRoot, "Scripts"), "*", SearchOption.AllDirectories)
|
||||
.Count()
|
||||
.ShouldEqual(5);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void ForceCheckoutRequiresCheckout()
|
||||
{
|
||||
this.RunFastFetch($"--checkout --folders \"/Scripts\" -b {Settings.Default.Commitish}");
|
||||
|
||||
// Run a second time in the same repo on the same branch with more folders but expect an error.
|
||||
ProcessResult result = this.RunFastFetch($"--force-checkout --folders \"/GVFS;/Scripts\" -b {Settings.Default.Commitish}");
|
||||
|
||||
string[] expectedResults = new string[] { "Cannot use --force-checkout option without --checkout option." };
|
||||
result.Output.ShouldContain(expectedResults);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void FastFetchFolderWithOnlyOneFile()
|
||||
{
|
||||
string folderPath = Path.Combine("GVFS", "GVFS", "Properties");
|
||||
this.RunFastFetch("--checkout --folders " + folderPath + " -b " + Settings.Default.Commitish);
|
||||
|
||||
this.CurrentBranchShouldEqual(Settings.Default.Commitish);
|
||||
|
||||
this.fastFetchRepoRoot.ShouldBeADirectory(FileSystemRunner.DefaultRunner);
|
||||
List<string> dirs = Directory.EnumerateFileSystemEntries(this.fastFetchRepoRoot).ToList();
|
||||
dirs.SequenceEqual(new[]
|
||||
{
|
||||
Path.Combine(this.fastFetchRepoRoot, ".git"),
|
||||
Path.Combine(this.fastFetchRepoRoot, "GVFS"),
|
||||
Path.Combine(this.fastFetchRepoRoot, "GVFS.sln")
|
||||
});
|
||||
|
||||
dirs = Directory.EnumerateFileSystemEntries(Path.Combine(this.fastFetchRepoRoot, "GVFS"), "*", SearchOption.AllDirectories).ToList();
|
||||
dirs.SequenceEqual(new[]
|
||||
{
|
||||
Path.Combine(this.fastFetchRepoRoot, "GVFS", "GVFS"),
|
||||
Path.Combine(this.fastFetchRepoRoot, "GVFS", "GVFS", "Properties"),
|
||||
Path.Combine(this.fastFetchRepoRoot, "GVFS", "GVFS", "Properties", "AssemblyInfo.cs"),
|
||||
});
|
||||
|
||||
this.AllFetchedFilePathsShouldPassCheck(path => path.StartsWith("GVFS", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CanFetchAndCheckoutBranchIntoEmptyGitRepo()
|
||||
{
|
||||
this.RunFastFetch("--checkout -b " + Settings.Default.Commitish);
|
||||
|
||||
this.CurrentBranchShouldEqual(Settings.Default.Commitish);
|
||||
|
||||
this.fastFetchRepoRoot.ShouldBeADirectory(FileSystemRunner.DefaultRunner)
|
||||
.WithDeepStructure(FileSystemRunner.DefaultRunner, this.fastFetchControlRoot);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CanUpdateIndex()
|
||||
{
|
||||
// Testing index versions 2, 3 and 4. Not bothering to test version 1; it's not in use anymore.
|
||||
this.CanUpdateIndex(2, indexSigningOff: true);
|
||||
this.CanUpdateIndex(3, indexSigningOff: true);
|
||||
this.CanUpdateIndex(4, indexSigningOff: true);
|
||||
|
||||
this.CanUpdateIndex(2, indexSigningOff: false);
|
||||
this.CanUpdateIndex(3, indexSigningOff: false);
|
||||
this.CanUpdateIndex(4, indexSigningOff: false);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CanFetchAndCheckoutAfterDeletingIndex()
|
||||
{
|
||||
this.RunFastFetch("--checkout -b " + Settings.Default.Commitish);
|
||||
|
||||
File.Delete(Path.Combine(this.fastFetchRepoRoot, ".git", "index"));
|
||||
this.RunFastFetch("--checkout -b " + Settings.Default.Commitish);
|
||||
|
||||
this.CurrentBranchShouldEqual(Settings.Default.Commitish);
|
||||
|
||||
this.fastFetchRepoRoot.ShouldBeADirectory(FileSystemRunner.DefaultRunner)
|
||||
.WithDeepStructure(FileSystemRunner.DefaultRunner, this.fastFetchControlRoot);
|
||||
}
|
||||
|
||||
public void CanUpdateIndex(int indexVersion, bool indexSigningOff)
|
||||
{
|
||||
// Initialize the repo
|
||||
GitProcess.Invoke(this.fastFetchRepoRoot, "config --local --add core.gvfs " + (indexSigningOff ? 1 : 0));
|
||||
this.CanFetchAndCheckoutBranchIntoEmptyGitRepo();
|
||||
string lsfilesAfterFirstFetch = GitProcess.Invoke(this.fastFetchRepoRoot, "ls-files --debug");
|
||||
lsfilesAfterFirstFetch.ShouldBeNonEmpty();
|
||||
|
||||
// Reset the index and use 'git status' to get baseline.
|
||||
GitProcess.Invoke(this.fastFetchRepoRoot, $"-c index.version={indexVersion} read-tree HEAD");
|
||||
string lsfilesBeforeStatus = GitProcess.Invoke(this.fastFetchRepoRoot, "ls-files --debug");
|
||||
lsfilesBeforeStatus.ShouldBeNonEmpty();
|
||||
|
||||
GitProcess.Invoke(this.fastFetchRepoRoot, "status");
|
||||
string lsfilesAfterStatus = GitProcess.Invoke(this.fastFetchRepoRoot, "ls-files --debug");
|
||||
lsfilesAfterStatus.ShouldBeNonEmpty();
|
||||
lsfilesAfterStatus.ShouldNotBeSameAs(lsfilesBeforeStatus, "Ensure 'git status' updates index");
|
||||
|
||||
// Reset the index and use fastfetch to update the index. Compare against 'git status' baseline.
|
||||
GitProcess.Invoke(this.fastFetchRepoRoot, $"-c index.version= {indexVersion} read-tree HEAD");
|
||||
ProcessResult fastFetchResult = this.RunFastFetch("--checkout --Allow-index-metadata-update-from-working-tree");
|
||||
string lsfilesAfterUpdate = GitProcess.Invoke(this.fastFetchRepoRoot, "ls-files --debug");
|
||||
lsfilesAfterUpdate.ShouldEqual(lsfilesAfterStatus, "git status and fastfetch didn't result in the same index");
|
||||
|
||||
// Don't reset the index and use 'git status' to update again. Should be same results.
|
||||
this.RunFastFetch("--checkout --Allow-index-metadata-update-from-working-tree");
|
||||
string lsfilesAfterUpdate2 = GitProcess.Invoke(this.fastFetchRepoRoot, "ls-files --debug");
|
||||
lsfilesAfterUpdate2.ShouldEqual(lsfilesAfterUpdate, "Incremental update should not change index");
|
||||
|
||||
// Verify that the final results are the same as the intial fetch results
|
||||
lsfilesAfterUpdate2.ShouldEqual(lsfilesAfterFirstFetch, "Incremental update should not change index");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void IncrementalChangesLeaveGoodStatus()
|
||||
{
|
||||
// Specific commits taken from branch FunctionalTests/20170206_Conflict_Source
|
||||
// These commits have adds, edits and removals
|
||||
const string BaseCommit = "db95d631e379d366d26d899523f8136a77441914";
|
||||
const string UpdateCommit = "51d15f7584e81d59d44c1511ce17d7c493903390";
|
||||
|
||||
GitProcess.Invoke(this.fastFetchRepoRoot, "config --local --add core.gvfs 1");
|
||||
|
||||
this.RunFastFetch($"--checkout -c {BaseCommit}");
|
||||
string status = GitProcess.Invoke(this.fastFetchRepoRoot, "status --porcelain");
|
||||
status.ShouldBeEmpty("Status shows unexpected files changed");
|
||||
|
||||
this.RunFastFetch($"--checkout -c {UpdateCommit}");
|
||||
status = GitProcess.Invoke(this.fastFetchRepoRoot, "status --porcelain");
|
||||
status.ShouldBeEmpty("Status shows unexpected files changed");
|
||||
|
||||
// Now that we have the content, verify that these commits meet our needs...
|
||||
string changes = GitProcess.Invoke(this.fastFetchRepoRoot, $"diff-tree -r --name-status {BaseCommit}..{UpdateCommit}");
|
||||
|
||||
// There must be modified files in these commits. Modified files must
|
||||
// be updated with valid metadata (times, sizes) or 'git status' will
|
||||
// show them as modified when they were not actually modified.
|
||||
Regex.IsMatch(changes, @"^M\s", RegexOptions.Multiline).ShouldEqual(true, "Data does not meet requirements");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CanFetchAndCheckoutBetweenTwoBranchesIntoEmptyGitRepo()
|
||||
{
|
||||
this.RunFastFetch("--checkout -b " + Settings.Default.Commitish);
|
||||
this.CurrentBranchShouldEqual(Settings.Default.Commitish);
|
||||
|
||||
// Switch to another branch
|
||||
this.RunFastFetch("--checkout -b FunctionalTests/20170602");
|
||||
this.CurrentBranchShouldEqual("FunctionalTests/20170602");
|
||||
|
||||
// And back
|
||||
this.RunFastFetch("--checkout -b " + Settings.Default.Commitish);
|
||||
this.CurrentBranchShouldEqual(Settings.Default.Commitish);
|
||||
|
||||
this.fastFetchRepoRoot.ShouldBeADirectory(FileSystemRunner.DefaultRunner)
|
||||
.WithDeepStructure(FileSystemRunner.DefaultRunner, this.fastFetchControlRoot);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CanDetectAlreadyUpToDate()
|
||||
{
|
||||
this.RunFastFetch("--checkout -b " + Settings.Default.Commitish);
|
||||
this.CurrentBranchShouldEqual(Settings.Default.Commitish);
|
||||
|
||||
this.RunFastFetch(" -b " + Settings.Default.Commitish).Output.ShouldContain("\"TotalMissingObjects\":0");
|
||||
this.RunFastFetch("--checkout -b " + Settings.Default.Commitish).Output.ShouldContain("\"RequiredBlobsCount\":0");
|
||||
|
||||
this.CurrentBranchShouldEqual(Settings.Default.Commitish);
|
||||
this.fastFetchRepoRoot.ShouldBeADirectory(FileSystemRunner.DefaultRunner)
|
||||
.WithDeepStructure(FileSystemRunner.DefaultRunner, this.fastFetchControlRoot);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void SuccessfullyChecksOutCaseChanges()
|
||||
{
|
||||
// The delta between these two is the same as the UnitTest "caseChange.txt" data file.
|
||||
this.RunFastFetch("--checkout -c b3ddcf43b997cba3fbf9d2341b297e22bf48601a");
|
||||
this.RunFastFetch("--checkout -c e637c874f6a914ae83cd5668bcdd07293fef961d");
|
||||
|
||||
GitProcess.Invoke(this.fastFetchControlRoot, "checkout e637c874f6a914ae83cd5668bcdd07293fef961d");
|
||||
|
||||
try
|
||||
{
|
||||
this.fastFetchRepoRoot.ShouldBeADirectory(FileSystemRunner.DefaultRunner)
|
||||
.WithDeepStructure(FileSystemRunner.DefaultRunner, this.fastFetchControlRoot, ignoreCase: true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
GitProcess.Invoke(this.fastFetchControlRoot, "checkout " + Settings.Default.Commitish);
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void SuccessfullyChecksOutDirectoryToFileToDirectory()
|
||||
{
|
||||
// This test switches between two branches and verifies specific transitions occured
|
||||
this.RunFastFetch("--checkout -b FunctionalTests/20171103_DirectoryFileTransitionsPart1");
|
||||
|
||||
// Delta of interest - Check initial state
|
||||
// renamed: foo.cpp\foo.cpp -> foo.cpp
|
||||
// where the top level "foo.cpp" is a folder with a file, then becomes just a file
|
||||
// note that folder\file names picked illustrate a real example
|
||||
Path.Combine(this.fastFetchRepoRoot, "foo.cpp", "foo.cpp")
|
||||
.ShouldBeAFile(FileSystemRunner.DefaultRunner);
|
||||
|
||||
// Delta of interest - Check initial state
|
||||
// renamed: a\a <-> b && b <-> a
|
||||
// where a\a contains "file contents one"
|
||||
// and b contains "file contents two"
|
||||
// This tests two types of renames crossing into each other
|
||||
Path.Combine(this.fastFetchRepoRoot, "a", "a")
|
||||
.ShouldBeAFile(FileSystemRunner.DefaultRunner).WithContents("file contents one");
|
||||
Path.Combine(this.fastFetchRepoRoot, "b")
|
||||
.ShouldBeAFile(FileSystemRunner.DefaultRunner).WithContents("file contents two");
|
||||
|
||||
// Delta of interest - Check initial state
|
||||
// renamed: c\c <-> d\c && d\d <-> c\d
|
||||
// where c\c contains "file contents c"
|
||||
// and d\d contains "file contents d"
|
||||
// This tests two types of renames crossing into each other
|
||||
Path.Combine(this.fastFetchRepoRoot, "c", "c")
|
||||
.ShouldBeAFile(FileSystemRunner.DefaultRunner).WithContents("file contents c");
|
||||
Path.Combine(this.fastFetchRepoRoot, "d", "d")
|
||||
.ShouldBeAFile(FileSystemRunner.DefaultRunner).WithContents("file contents d");
|
||||
|
||||
// Now switch to second branch, part2 and verify transitions
|
||||
this.RunFastFetch("--checkout -b FunctionalTests/20171103_DirectoryFileTransitionsPart2");
|
||||
|
||||
// Delta of interest - Verify change
|
||||
// renamed: foo.cpp\foo.cpp -> foo.cpp
|
||||
Path.Combine(this.fastFetchRepoRoot, "foo.cpp")
|
||||
.ShouldBeAFile(FileSystemRunner.DefaultRunner);
|
||||
|
||||
// Delta of interest - Verify change
|
||||
// renamed: a\a <-> b && b <-> a
|
||||
Path.Combine(this.fastFetchRepoRoot, "a")
|
||||
.ShouldBeAFile(FileSystemRunner.DefaultRunner).WithContents("file contents two");
|
||||
Path.Combine(this.fastFetchRepoRoot, "b")
|
||||
.ShouldBeAFile(FileSystemRunner.DefaultRunner).WithContents("file contents one");
|
||||
|
||||
// Delta of interest - Verify change
|
||||
// renamed: c\c <-> d\c && d\d <-> c\d
|
||||
Path.Combine(this.fastFetchRepoRoot, "c", "d")
|
||||
.ShouldBeAFile(FileSystemRunner.DefaultRunner).WithContents("file contents d");
|
||||
Path.Combine(this.fastFetchRepoRoot, "d", "c")
|
||||
.ShouldBeAFile(FileSystemRunner.DefaultRunner).WithContents("file contents c");
|
||||
Path.Combine(this.fastFetchRepoRoot, "c", "c")
|
||||
.ShouldNotExistOnDisk(FileSystemRunner.DefaultRunner);
|
||||
Path.Combine(this.fastFetchRepoRoot, "d", "d")
|
||||
.ShouldNotExistOnDisk(FileSystemRunner.DefaultRunner);
|
||||
|
||||
// And back again
|
||||
this.RunFastFetch("--checkout -b FunctionalTests/20171103_DirectoryFileTransitionsPart1");
|
||||
|
||||
// Delta of interest - Final validation
|
||||
// renamed: foo.cpp\foo.cpp -> foo.cpp
|
||||
Path.Combine(this.fastFetchRepoRoot, "foo.cpp", "foo.cpp")
|
||||
.ShouldBeAFile(FileSystemRunner.DefaultRunner);
|
||||
|
||||
// Delta of interest - Final validation
|
||||
// renamed: a\a <-> b && b <-> a
|
||||
Path.Combine(this.fastFetchRepoRoot, "a", "a")
|
||||
.ShouldBeAFile(FileSystemRunner.DefaultRunner).WithContents("file contents one");
|
||||
Path.Combine(this.fastFetchRepoRoot, "b")
|
||||
.ShouldBeAFile(FileSystemRunner.DefaultRunner).WithContents("file contents two");
|
||||
|
||||
// Delta of interest - Final validation
|
||||
// renamed: c\c <-> d\c && d\d <-> c\d
|
||||
Path.Combine(this.fastFetchRepoRoot, "c", "c")
|
||||
.ShouldBeAFile(FileSystemRunner.DefaultRunner).WithContents("file contents c");
|
||||
Path.Combine(this.fastFetchRepoRoot, "d", "d")
|
||||
.ShouldBeAFile(FileSystemRunner.DefaultRunner).WithContents("file contents d");
|
||||
Path.Combine(this.fastFetchRepoRoot, "c", "d")
|
||||
.ShouldNotExistOnDisk(FileSystemRunner.DefaultRunner);
|
||||
Path.Combine(this.fastFetchRepoRoot, "d", "c")
|
||||
.ShouldNotExistOnDisk(FileSystemRunner.DefaultRunner);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CanFetchPathsWithLsTreeTypes()
|
||||
{
|
||||
this.RunFastFetch("--checkout -b " + LsTreeTypeInPathBranchName);
|
||||
Path.Combine(this.fastFetchRepoRoot, "Test_LsTree_Issues", "file with tree in name.txt")
|
||||
.ShouldBeAFile(FileSystemRunner.DefaultRunner).WithContents("File with \" tree \" in name caused issues with ls tree diff logic.");
|
||||
Path.Combine(this.fastFetchRepoRoot, "Test_LsTree_Issues", "directory with blob in path")
|
||||
.ShouldBeADirectory(FileSystemRunner.DefaultRunner);
|
||||
Path.Combine(this.fastFetchRepoRoot, "Test_LsTree_Issues", "directory with blob in path", "file with tree in name.txt")
|
||||
.ShouldBeAFile(FileSystemRunner.DefaultRunner).WithContents("File with \" tree \" in name caused issues with ls tree diff logic. This is another example.");
|
||||
}
|
||||
|
||||
private void AllFetchedFilePathsShouldPassCheck(Func<string, bool> checkPath)
|
||||
{
|
||||
// Form a cache map of sha => path
|
||||
string[] allObjects = GitProcess.Invoke(this.fastFetchRepoRoot, "cat-file --batch-check --batch-all-objects").Split('\n');
|
||||
string[] gitlsLines = GitProcess.Invoke(this.fastFetchRepoRoot, "ls-tree -r HEAD").Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
Dictionary<string, List<string>> allPaths = new Dictionary<string, List<string>>();
|
||||
foreach (string line in gitlsLines)
|
||||
{
|
||||
string sha = this.GetShaFromLsLine(line);
|
||||
string path = this.GetPathFromLsLine(line);
|
||||
|
||||
if (!allPaths.ContainsKey(sha))
|
||||
{
|
||||
allPaths.Add(sha, new List<string>());
|
||||
}
|
||||
|
||||
allPaths[sha].Add(path);
|
||||
}
|
||||
|
||||
foreach (string sha in allObjects.Where(line => line.Contains(" blob ")).Select(line => line.Substring(0, 40)))
|
||||
{
|
||||
allPaths.ContainsKey(sha).ShouldEqual(true, "Found a blob that wasn't in the tree: " + sha);
|
||||
|
||||
// A single blob should map to multiple files, so if any pass for a single sha, we have to give a pass.
|
||||
allPaths[sha].Any(path => checkPath(path))
|
||||
.ShouldEqual(true, "Downloaded extra paths:\r\n" + string.Join("\r\n", allPaths[sha]));
|
||||
}
|
||||
}
|
||||
|
||||
private void CurrentBranchShouldEqual(string commitish)
|
||||
{
|
||||
// Ensure remote branch has been created
|
||||
this.GetRefTreeSha("remotes/origin/" + commitish).ShouldNotBeNull();
|
||||
|
||||
// And head has been updated to local branch, which are both updated
|
||||
this.GetRefTreeSha("HEAD")
|
||||
.ShouldNotBeNull()
|
||||
.ShouldEqual(this.GetRefTreeSha(commitish));
|
||||
|
||||
// Ensure no errors are thrown with git log
|
||||
GitHelpers.CheckGitCommand(this.fastFetchRepoRoot, "log");
|
||||
}
|
||||
|
||||
private string GetRefTreeSha(string refName)
|
||||
{
|
||||
string headInfo = GitProcess.Invoke(this.fastFetchRepoRoot, "cat-file -p " + refName);
|
||||
if (string.IsNullOrEmpty(headInfo) || headInfo.EndsWith("missing"))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
string[] headInfoLines = headInfo.Split('\n');
|
||||
headInfoLines[0].StartsWith("tree").ShouldEqual(true);
|
||||
int firstSpace = headInfoLines[0].IndexOf(' ');
|
||||
string headTreeSha = headInfoLines[0].Substring(firstSpace + 1);
|
||||
headTreeSha.Length.ShouldEqual(40);
|
||||
return headTreeSha;
|
||||
}
|
||||
|
||||
private ProcessResult RunFastFetch(string args)
|
||||
{
|
||||
args = args + " --verbose";
|
||||
|
||||
string fastfetch;
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
fastfetch = Path.Combine(Settings.Default.CurrentDirectory, "netcoreapp2.1", "fastfetch.dll");
|
||||
}
|
||||
else
|
||||
{
|
||||
fastfetch = Path.Combine(Settings.Default.CurrentDirectory, "fastfetch.dll");
|
||||
}
|
||||
|
||||
File.Exists(fastfetch).ShouldBeTrue($"{fastfetch} did not exist.");
|
||||
Console.WriteLine($"Using {fastfetch}");
|
||||
|
||||
ProcessStartInfo processInfo = new ProcessStartInfo("dotnet");
|
||||
processInfo.Arguments = $"{fastfetch} {args}";
|
||||
processInfo.WorkingDirectory = this.fastFetchRepoRoot;
|
||||
processInfo.UseShellExecute = false;
|
||||
processInfo.RedirectStandardOutput = true;
|
||||
processInfo.RedirectStandardError = true;
|
||||
|
||||
ProcessResult result = ProcessHelper.Run(processInfo);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private string GetShaFromLsLine(string line)
|
||||
{
|
||||
string output = line.Substring(line.LastIndexOf('\t') - 40, 40);
|
||||
return output;
|
||||
}
|
||||
|
||||
private string GetPathFromLsLine(string line)
|
||||
{
|
||||
int idx = line.LastIndexOf('\t') + 1;
|
||||
string output = line.Substring(idx, line.Length - idx);
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,960 +0,0 @@
|
|||
using GVFS.FunctionalTests.Properties;
|
||||
using GVFS.FunctionalTests.Should;
|
||||
using GVFS.FunctionalTests.Tools;
|
||||
using GVFS.Tests.Should;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GVFS.FunctionalTests.Tests.GitCommands
|
||||
{
|
||||
[TestFixtureSource(typeof(GitRepoTests), nameof(GitRepoTests.ValidateWorkingTree))]
|
||||
[Category(Categories.GitCommands)]
|
||||
public class CheckoutTests : GitRepoTests
|
||||
{
|
||||
public CheckoutTests(Settings.ValidateWorkingTreeMode validateWorkingTree)
|
||||
: base(enlistmentPerTest: true, validateWorkingTree: validateWorkingTree)
|
||||
{
|
||||
}
|
||||
|
||||
private enum NativeFileAttributes : uint
|
||||
{
|
||||
FILE_ATTRIBUTE_READONLY = 1,
|
||||
FILE_ATTRIBUTE_HIDDEN = 2,
|
||||
FILE_ATTRIBUTE_SYSTEM = 4,
|
||||
FILE_ATTRIBUTE_DIRECTORY = 16,
|
||||
FILE_ATTRIBUTE_ARCHIVE = 32,
|
||||
FILE_ATTRIBUTE_DEVICE = 64,
|
||||
FILE_ATTRIBUTE_NORMAL = 128,
|
||||
FILE_ATTRIBUTE_TEMPORARY = 256,
|
||||
FILE_ATTRIBUTE_SPARSEFILE = 512,
|
||||
FILE_ATTRIBUTE_REPARSEPOINT = 1024,
|
||||
FILE_ATTRIBUTE_COMPRESSED = 2048,
|
||||
FILE_ATTRIBUTE_OFFLINE = 4096,
|
||||
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 8192,
|
||||
FILE_ATTRIBUTE_ENCRYPTED = 16384,
|
||||
FILE_FLAG_FIRST_PIPE_INSTANCE = 524288,
|
||||
FILE_FLAG_OPEN_NO_RECALL = 1048576,
|
||||
FILE_FLAG_OPEN_REPARSE_POINT = 2097152,
|
||||
FILE_FLAG_POSIX_SEMANTICS = 16777216,
|
||||
FILE_FLAG_BACKUP_SEMANTICS = 33554432,
|
||||
FILE_FLAG_DELETE_ON_CLOSE = 67108864,
|
||||
FILE_FLAG_SEQUENTIAL_SCAN = 134217728,
|
||||
FILE_FLAG_RANDOM_ACCESS = 268435456,
|
||||
FILE_FLAG_NO_BUFFERING = 536870912,
|
||||
FILE_FLAG_OVERLAPPED = 1073741824,
|
||||
FILE_FLAG_WRITE_THROUGH = 2147483648
|
||||
}
|
||||
|
||||
private enum NativeFileAccess : uint
|
||||
{
|
||||
FILE_READ_DATA = 1,
|
||||
FILE_LIST_DIRECTORY = 1,
|
||||
FILE_WRITE_DATA = 2,
|
||||
FILE_ADD_FILE = 2,
|
||||
FILE_APPEND_DATA = 4,
|
||||
FILE_ADD_SUBDIRECTORY = 4,
|
||||
FILE_CREATE_PIPE_INSTANCE = 4,
|
||||
FILE_READ_EA = 8,
|
||||
FILE_WRITE_EA = 16,
|
||||
FILE_EXECUTE = 32,
|
||||
FILE_TRAVERSE = 32,
|
||||
FILE_DELETE_CHILD = 64,
|
||||
FILE_READ_ATTRIBUTES = 128,
|
||||
FILE_WRITE_ATTRIBUTES = 256,
|
||||
SPECIFIC_RIGHTS_ALL = 65535,
|
||||
DELETE = 65536,
|
||||
READ_CONTROL = 131072,
|
||||
STANDARD_RIGHTS_READ = 131072,
|
||||
STANDARD_RIGHTS_WRITE = 131072,
|
||||
STANDARD_RIGHTS_EXECUTE = 131072,
|
||||
WRITE_DAC = 262144,
|
||||
WRITE_OWNER = 524288,
|
||||
STANDARD_RIGHTS_REQUIRED = 983040,
|
||||
SYNCHRONIZE = 1048576,
|
||||
FILE_GENERIC_READ = 1179785,
|
||||
FILE_GENERIC_EXECUTE = 1179808,
|
||||
FILE_GENERIC_WRITE = 1179926,
|
||||
STANDARD_RIGHTS_ALL = 2031616,
|
||||
FILE_ALL_ACCESS = 2032127,
|
||||
ACCESS_SYSTEM_SECURITY = 16777216,
|
||||
MAXIMUM_ALLOWED = 33554432,
|
||||
GENERIC_ALL = 268435456,
|
||||
GENERIC_EXECUTE = 536870912,
|
||||
GENERIC_WRITE = 1073741824,
|
||||
GENERIC_READ = 2147483648
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void ReadDeepFilesAfterCheckout()
|
||||
{
|
||||
// In commit 8df701986dea0a5e78b742d2eaf9348825b14d35 the CheckoutNewBranchFromStartingPointTest files were not present
|
||||
this.ValidateGitCommand("checkout 8df701986dea0a5e78b742d2eaf9348825b14d35");
|
||||
|
||||
// In commit cd5c55fea4d58252bb38058dd3818da75aff6685 the CheckoutNewBranchFromStartingPointTest files were present
|
||||
this.ValidateGitCommand("checkout cd5c55fea4d58252bb38058dd3818da75aff6685");
|
||||
|
||||
this.FileShouldHaveContents("TestFile1 \r\n", "GitCommandsTests", "CheckoutNewBranchFromStartingPointTest", "test1.txt");
|
||||
this.FileShouldHaveContents("TestFile2 \r\n", "GitCommandsTests", "CheckoutNewBranchFromStartingPointTest", "test2.txt");
|
||||
|
||||
this.ValidateGitCommand("status");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CheckoutNewBranchFromStartingPointTest()
|
||||
{
|
||||
// In commit 8df701986dea0a5e78b742d2eaf9348825b14d35 the CheckoutNewBranchFromStartingPointTest files were not present
|
||||
this.ValidateGitCommand("checkout 8df701986dea0a5e78b742d2eaf9348825b14d35");
|
||||
this.ShouldNotExistOnDisk("GitCommandsTests", "CheckoutNewBranchFromStartingPointTest", "test1.txt");
|
||||
this.ShouldNotExistOnDisk("GitCommandsTests", "CheckoutNewBranchFromStartingPointTest", "test2.txt");
|
||||
|
||||
// In commit cd5c55fea4d58252bb38058dd3818da75aff6685 the CheckoutNewBranchFromStartingPointTest files were present
|
||||
this.ValidateGitCommand("checkout -b tests/functional/CheckoutNewBranchFromStartingPointTest cd5c55fea4d58252bb38058dd3818da75aff6685");
|
||||
this.FileShouldHaveContents("TestFile1 \r\n", "GitCommandsTests", "CheckoutNewBranchFromStartingPointTest", "test1.txt");
|
||||
this.FileShouldHaveContents("TestFile2 \r\n", "GitCommandsTests", "CheckoutNewBranchFromStartingPointTest", "test2.txt");
|
||||
|
||||
this.ValidateGitCommand("status");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CheckoutOrhpanBranchFromStartingPointTest()
|
||||
{
|
||||
// In commit 8df701986dea0a5e78b742d2eaf9348825b14d35 the CheckoutOrhpanBranchFromStartingPointTest files were not present
|
||||
this.ValidateGitCommand("checkout 8df701986dea0a5e78b742d2eaf9348825b14d35");
|
||||
this.ShouldNotExistOnDisk("GitCommandsTests", "CheckoutOrhpanBranchFromStartingPointTest", "test1.txt");
|
||||
this.ShouldNotExistOnDisk("GitCommandsTests", "CheckoutOrhpanBranchFromStartingPointTest", "test2.txt");
|
||||
|
||||
// In commit 15a9676c9192448820bd243807f6dab1bac66680 the CheckoutOrhpanBranchFromStartingPointTest files were present
|
||||
this.ValidateGitCommand("checkout --orphan tests/functional/CheckoutOrhpanBranchFromStartingPointTest 15a9676c9192448820bd243807f6dab1bac66680");
|
||||
this.FileShouldHaveContents("TestFile1 \r\n", "GitCommandsTests", "CheckoutOrhpanBranchFromStartingPointTest", "test1.txt");
|
||||
this.FileShouldHaveContents("TestFile2 \r\n", "GitCommandsTests", "CheckoutOrhpanBranchFromStartingPointTest", "test2.txt");
|
||||
|
||||
this.ValidateGitCommand("status");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void MoveFileFromDotGitFolderToWorkingDirectoryAndAddAndCheckout()
|
||||
{
|
||||
string testFileContents = "Test file contents for MoveFileFromDotGitFolderToWorkingDirectoryAndAddAndCheckout";
|
||||
string filename = "AddedBySource.txt";
|
||||
string dotGitFilePath = Path.Combine(".git", filename);
|
||||
string targetPath = Path.Combine("Test_ConflictTests", "AddedFiles", filename);
|
||||
|
||||
// In commit db95d631e379d366d26d899523f8136a77441914 Test_ConflictTests\AddedFiles\AddedBySource.txt does not exist
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.ConflictSourceBranch);
|
||||
this.ValidateGitCommand("checkout db95d631e379d366d26d899523f8136a77441914");
|
||||
|
||||
string newBranchName = "tests/functional/MoveFileFromDotGitFolderToWorkingDirectoryAndAddAndCheckout";
|
||||
this.ValidateGitCommand("checkout -b " + newBranchName);
|
||||
|
||||
this.ShouldNotExistOnDisk(targetPath);
|
||||
this.CreateFile(testFileContents, dotGitFilePath);
|
||||
this.FileShouldHaveContents(testFileContents, dotGitFilePath);
|
||||
|
||||
// Move file to working directory
|
||||
this.MoveFile(dotGitFilePath, targetPath);
|
||||
this.FileContentsShouldMatch(targetPath);
|
||||
|
||||
this.ValidateGitCommand("status");
|
||||
this.ValidateGitCommand("add .");
|
||||
this.RunGitCommand("commit -m \"Change for MoveFileFromDotGitFolderToWorkingDirectoryAndAddAndCheckout\"");
|
||||
|
||||
// In commit 51d15f7584e81d59d44c1511ce17d7c493903390 Test_ConflictTests\AddedFiles\AddedBySource.txt was added
|
||||
this.ValidateGitCommand("checkout 51d15f7584e81d59d44c1511ce17d7c493903390");
|
||||
this.FileContentsShouldMatch(targetPath);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CheckoutBranchNoCrashOnStatus()
|
||||
{
|
||||
this.ControlGitRepo.Fetch("FunctionalTests/20170331_git_crash");
|
||||
this.ValidateGitCommand("checkout FunctionalTests/20170331_git_crash");
|
||||
this.ValidateGitCommand("status");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CheckoutCommitWhereFileContentsChangeAfterRead()
|
||||
{
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.ConflictSourceBranch);
|
||||
|
||||
string fileName = "SameChange.txt";
|
||||
|
||||
// In commit db95d631e379d366d26d899523f8136a77441914 the initial files for the FunctionalTests/20170206_Conflict_Source branch were created
|
||||
this.ValidateGitCommand("checkout db95d631e379d366d26d899523f8136a77441914");
|
||||
this.FileContentsShouldMatch("Test_ConflictTests", "ModifiedFiles", fileName);
|
||||
|
||||
// A read should not add the file to the modified paths
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.FileSystem, fileName);
|
||||
|
||||
this.ValidateGitCommand("checkout FunctionalTests/20170206_Conflict_Source");
|
||||
this.FileContentsShouldMatch("Test_ConflictTests", "ModifiedFiles", fileName);
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.FileSystem, fileName);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CheckoutCommitWhereFileDeletedAfterRead()
|
||||
{
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.ConflictSourceBranch);
|
||||
|
||||
string fileName = "DeleteInSource.txt";
|
||||
string filePath = Path.Combine("Test_ConflictTests", "DeletedFiles", fileName);
|
||||
|
||||
// In commit db95d631e379d366d26d899523f8136a77441914 the initial files for the FunctionalTests/20170206_Conflict_Source branch were created
|
||||
this.ValidateGitCommand("checkout db95d631e379d366d26d899523f8136a77441914");
|
||||
this.FileContentsShouldMatch(filePath);
|
||||
|
||||
// A read should not add the file to the modified paths
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.FileSystem, fileName);
|
||||
|
||||
this.ValidateGitCommand("checkout FunctionalTests/20170206_Conflict_Source");
|
||||
this.ShouldNotExistOnDisk(filePath);
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.FileSystem, fileName);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CheckoutBranchAfterReadingFileAndVerifyContentsCorrect()
|
||||
{
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.ConflictSourceBranch);
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.ConflictTargetBranch);
|
||||
|
||||
this.ValidateGitCommand("checkout " + GitRepoTests.ConflictTargetBranch);
|
||||
this.FilesShouldMatchCheckoutOfTargetBranch();
|
||||
|
||||
this.ValidateGitCommand("checkout " + GitRepoTests.ConflictSourceBranch);
|
||||
this.FilesShouldMatchCheckoutOfSourceBranch();
|
||||
|
||||
// Verify modified paths contents
|
||||
GVFSHelpers.ModifiedPathsContentsShouldEqual(this.Enlistment, this.FileSystem, "A .gitattributes" + GVFSHelpers.ModifiedPathsNewLine);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CheckoutBranchAfterReadingAllFilesAndVerifyContentsCorrect()
|
||||
{
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.ConflictSourceBranch);
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.ConflictTargetBranch);
|
||||
|
||||
this.ValidateGitCommand("checkout " + GitRepoTests.ConflictTargetBranch);
|
||||
this.Enlistment.RepoRoot.ShouldBeADirectory(this.FileSystem)
|
||||
.WithDeepStructure(this.FileSystem, this.ControlGitRepo.RootPath, compareContent: true, withinPrefixes: this.pathPrefixes);
|
||||
|
||||
this.ValidateGitCommand("checkout " + GitRepoTests.ConflictSourceBranch);
|
||||
this.Enlistment.RepoRoot.ShouldBeADirectory(this.FileSystem)
|
||||
.WithDeepStructure(this.FileSystem, this.ControlGitRepo.RootPath, compareContent: true, withinPrefixes: this.pathPrefixes);
|
||||
|
||||
// Verify modified paths contents
|
||||
GVFSHelpers.ModifiedPathsContentsShouldEqual(this.Enlistment, this.FileSystem, "A .gitattributes" + GVFSHelpers.ModifiedPathsNewLine);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CheckoutBranchThatHasFolderShouldGetDeleted()
|
||||
{
|
||||
// this.ControlGitRepo.Commitish should not have the folder Test_ConflictTests\AddedFiles
|
||||
string testFolder = Path.Combine("Test_ConflictTests", "AddedFiles");
|
||||
this.ShouldNotExistOnDisk(testFolder);
|
||||
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.ConflictSourceBranch);
|
||||
this.ValidateGitCommand("checkout " + GitRepoTests.ConflictSourceBranch);
|
||||
string testFile = Path.Combine(testFolder, "AddedByBothDifferentContent.txt");
|
||||
this.FileContentsShouldMatch(testFile);
|
||||
|
||||
// Move back to this.ControlGitRepo.Commitish where testFolder and testFile are not in the repo
|
||||
this.ValidateGitCommand("checkout " + this.ControlGitRepo.Commitish);
|
||||
this.ShouldNotExistOnDisk(testFile);
|
||||
|
||||
string virtualFolder = Path.Combine(this.Enlistment.RepoRoot, testFolder);
|
||||
string controlFolder = Path.Combine(this.ControlGitRepo.RootPath, testFolder);
|
||||
controlFolder.ShouldNotExistOnDisk(this.FileSystem);
|
||||
virtualFolder.ShouldNotExistOnDisk(this.FileSystem);
|
||||
|
||||
// Move back to GitRepoTests.ConflictSourceBranch where testFolder and testFile are present
|
||||
this.ValidateGitCommand("checkout " + GitRepoTests.ConflictSourceBranch);
|
||||
this.FileContentsShouldMatch(testFile);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CheckoutBranchThatDoesNotHaveFolderShouldNotHaveFolder()
|
||||
{
|
||||
// this.ControlGitRepo.Commitish should not have the folder Test_ConflictTests\AddedFiles
|
||||
string testFolder = Path.Combine("Test_ConflictTests", "AddedFiles");
|
||||
this.ShouldNotExistOnDisk(testFolder);
|
||||
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.ConflictSourceBranch);
|
||||
this.ValidateGitCommand("checkout " + GitRepoTests.ConflictSourceBranch);
|
||||
|
||||
string testFile = Path.Combine(testFolder, "AddedByBothDifferentContent.txt");
|
||||
this.FileContentsShouldMatch(testFile);
|
||||
this.ValidateGitCommand("checkout " + this.ControlGitRepo.Commitish);
|
||||
this.ShouldNotExistOnDisk(testFile);
|
||||
|
||||
this.ValidateGitCommand("checkout -b tests/functional/DeleteEmptyFolderPlaceholderAndCheckoutBranchThatDoesNotHaveFolder" + this.ControlGitRepo.Commitish);
|
||||
|
||||
string virtualFolder = Path.Combine(this.Enlistment.RepoRoot, testFolder);
|
||||
string controlFolder = Path.Combine(this.ControlGitRepo.RootPath, testFolder);
|
||||
controlFolder.ShouldNotExistOnDisk(this.FileSystem);
|
||||
virtualFolder.ShouldNotExistOnDisk(this.FileSystem);
|
||||
|
||||
this.ValidateGitCommand("checkout " + this.ControlGitRepo.Commitish);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void EditFileReadFileAndCheckoutConflict()
|
||||
{
|
||||
// editFilePath was changed on ConflictTargetBranch
|
||||
string editFilePath = Path.Combine("Test_ConflictTests", "ModifiedFiles", "ChangeInTarget.txt");
|
||||
|
||||
// readFilePath has different contents on ConflictSourceBranch and ConflictTargetBranch
|
||||
string readFilePath = Path.Combine("Test_ConflictTests", "ModifiedFiles", "ChangeInSource.txt");
|
||||
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.ConflictSourceBranch);
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.ConflictTargetBranch);
|
||||
this.ValidateGitCommand("checkout " + GitRepoTests.ConflictSourceBranch);
|
||||
|
||||
this.EditFile("New content", editFilePath);
|
||||
this.FileContentsShouldMatch(readFilePath);
|
||||
string originalReadFileContents = this.Enlistment.GetVirtualPathTo(readFilePath).ShouldBeAFile(this.FileSystem).WithContents();
|
||||
|
||||
// This checkout will hit a conflict due to the changes in editFilePath
|
||||
this.ValidateGitCommand("checkout " + GitRepoTests.ConflictTargetBranch);
|
||||
this.FileContentsShouldMatch(readFilePath);
|
||||
this.FileContentsShouldMatch(editFilePath);
|
||||
|
||||
// The contents of originalReadFileContents should not have changed
|
||||
this.Enlistment.GetVirtualPathTo(readFilePath).ShouldBeAFile(this.FileSystem).WithContents(originalReadFileContents);
|
||||
|
||||
this.ValidateGitCommand("checkout -- " + editFilePath.Replace('\\', '/'));
|
||||
this.ValidateGitCommand("checkout " + GitRepoTests.ConflictTargetBranch);
|
||||
this.FileContentsShouldMatch(readFilePath);
|
||||
this.FileContentsShouldMatch(editFilePath);
|
||||
this.Enlistment.GetVirtualPathTo(readFilePath).ShouldBeAFile(this.FileSystem).WithContents().ShouldNotEqual(originalReadFileContents);
|
||||
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.FileSystem, Path.GetFileName(readFilePath));
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void MarkFileAsReadOnlyAndCheckoutCommitWhereFileIsDifferent()
|
||||
{
|
||||
string filePath = Path.Combine("Test_ConflictTests", "ModifiedFiles", "ConflictingChange.txt");
|
||||
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.ConflictSourceBranch);
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.ConflictTargetBranch);
|
||||
this.ValidateGitCommand("checkout " + GitRepoTests.ConflictSourceBranch);
|
||||
|
||||
this.SetFileAsReadOnly(filePath);
|
||||
|
||||
this.ValidateGitCommand("checkout " + GitRepoTests.ConflictTargetBranch);
|
||||
this.FileContentsShouldMatch(filePath);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void MarkFileAsReadOnlyAndCheckoutCommitWhereFileIsDeleted()
|
||||
{
|
||||
string filePath = Path.Combine("Test_ConflictTests", "AddedFiles", "AddedBySource.txt");
|
||||
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.ConflictSourceBranch);
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.ConflictTargetBranch);
|
||||
this.ValidateGitCommand("checkout " + GitRepoTests.ConflictSourceBranch);
|
||||
|
||||
this.SetFileAsReadOnly(filePath);
|
||||
|
||||
this.ValidateGitCommand("checkout " + GitRepoTests.ConflictTargetBranch);
|
||||
this.ShouldNotExistOnDisk(filePath);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void ModifyAndCheckoutFirstOfSeveralFilesWhoseNamesAppearBeforeDot()
|
||||
{
|
||||
// Commit cb2d05febf64e3b0df50bd8d3fe8f05c0e2caa47 has the files (a).txt and (z).txt
|
||||
// in the DeleteFileWithNameAheadOfDotAndSwitchCommits folder
|
||||
string originalContent = "Test contents for (a).txt";
|
||||
string newContent = "content to append";
|
||||
|
||||
this.ValidateGitCommand("checkout cb2d05febf64e3b0df50bd8d3fe8f05c0e2caa47");
|
||||
this.EditFile(newContent, "DeleteFileWithNameAheadOfDotAndSwitchCommits", "(a).txt");
|
||||
this.FileShouldHaveContents(originalContent + newContent, "DeleteFileWithNameAheadOfDotAndSwitchCommits", "(a).txt");
|
||||
this.ValidateGitCommand("status");
|
||||
this.ValidateGitCommand("checkout -- DeleteFileWithNameAheadOfDotAndSwitchCommits/(a).txt");
|
||||
this.ValidateGitCommand("status");
|
||||
this.FileShouldHaveContents(originalContent, "DeleteFileWithNameAheadOfDotAndSwitchCommits", "(a).txt");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void ResetMixedToCommitWithNewFileThenCheckoutNewBranchAndCheckoutCommitWithNewFile()
|
||||
{
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.ConflictSourceBranch);
|
||||
|
||||
// Commit db95d631e379d366d26d899523f8136a77441914 was prior to the additional of these
|
||||
// three files in commit 51d15f7584e81d59d44c1511ce17d7c493903390:
|
||||
// Test_ConflictTests/AddedFiles/AddedByBothDifferentContent.txt
|
||||
// Test_ConflictTests/AddedFiles/AddedByBothSameContent.txt
|
||||
// Test_ConflictTests/AddedFiles/AddedBySource.txt
|
||||
this.ValidateGitCommand("checkout db95d631e379d366d26d899523f8136a77441914");
|
||||
this.ValidateGitCommand("reset --mixed 51d15f7584e81d59d44c1511ce17d7c493903390");
|
||||
|
||||
// Use RunGitCommand rather than ValidateGitCommand as G4W optimizations for "checkout -b" mean that the
|
||||
// command will not report modified and deleted files
|
||||
this.RunGitCommand("checkout -b tests/functional/ResetMixedToCommitWithNewFileThenCheckoutNewBranchAndCheckoutCommitWithNewFile");
|
||||
this.ValidateGitCommand("checkout 51d15f7584e81d59d44c1511ce17d7c493903390");
|
||||
}
|
||||
|
||||
// ReadFileAfterTryingToReadFileAtCommitWhereFileDoesNotExist is meant to exercise the NegativePathCache and its
|
||||
// behavior when projections change
|
||||
[TestCase]
|
||||
public void ReadFileAfterTryingToReadFileAtCommitWhereFileDoesNotExist()
|
||||
{
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.ConflictSourceBranch);
|
||||
|
||||
// Commit db95d631e379d366d26d899523f8136a77441914 was prior to the additional of these
|
||||
// three files in commit 51d15f7584e81d59d44c1511ce17d7c493903390:
|
||||
// Test_ConflictTests/AddedFiles/AddedByBothDifferentContent.txt
|
||||
// Test_ConflictTests/AddedFiles/AddedByBothSameContent.txt
|
||||
// Test_ConflictTests/AddedFiles/AddedBySource.txt
|
||||
this.ValidateGitCommand("checkout db95d631e379d366d26d899523f8136a77441914");
|
||||
|
||||
// Files should not exist
|
||||
this.ShouldNotExistOnDisk("Test_ConflictTests", "AddedFiles", "AddedByBothDifferentContent.txt");
|
||||
this.ShouldNotExistOnDisk("Test_ConflictTests", "AddedFiles", "AddedByBothSameContent.txt");
|
||||
this.ShouldNotExistOnDisk("Test_ConflictTests", "AddedFiles", "AddedBySource.txt");
|
||||
|
||||
// Check a second time to exercise the ProjFS negative cache
|
||||
this.ShouldNotExistOnDisk("Test_ConflictTests", "AddedFiles", "AddedByBothDifferentContent.txt");
|
||||
this.ShouldNotExistOnDisk("Test_ConflictTests", "AddedFiles", "AddedByBothSameContent.txt");
|
||||
this.ShouldNotExistOnDisk("Test_ConflictTests", "AddedFiles", "AddedBySource.txt");
|
||||
|
||||
// Switch to commit where files should exist
|
||||
this.ValidateGitCommand("checkout 51d15f7584e81d59d44c1511ce17d7c493903390");
|
||||
|
||||
// Confirm files exist
|
||||
this.FileContentsShouldMatch("Test_ConflictTests", "AddedFiles", "AddedByBothDifferentContent.txt");
|
||||
this.FileContentsShouldMatch("Test_ConflictTests", "AddedFiles", "AddedByBothSameContent.txt");
|
||||
this.FileContentsShouldMatch("Test_ConflictTests", "AddedFiles", "AddedBySource.txt");
|
||||
|
||||
// Switch to commit where files should not exist
|
||||
this.ValidateGitCommand("checkout db95d631e379d366d26d899523f8136a77441914");
|
||||
|
||||
// Verify files do not not exist
|
||||
this.ShouldNotExistOnDisk("Test_ConflictTests", "AddedFiles", "AddedByBothDifferentContent.txt");
|
||||
this.ShouldNotExistOnDisk("Test_ConflictTests", "AddedFiles", "AddedByBothSameContent.txt");
|
||||
this.ShouldNotExistOnDisk("Test_ConflictTests", "AddedFiles", "AddedBySource.txt");
|
||||
|
||||
// Check a second time to exercise the ProjFS negative cache
|
||||
this.ShouldNotExistOnDisk("Test_ConflictTests", "AddedFiles", "AddedByBothDifferentContent.txt");
|
||||
this.ShouldNotExistOnDisk("Test_ConflictTests", "AddedFiles", "AddedByBothSameContent.txt");
|
||||
this.ShouldNotExistOnDisk("Test_ConflictTests", "AddedFiles", "AddedBySource.txt");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CheckoutBranchWithOpenHandleBlockingRepoMetdataUpdate()
|
||||
{
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.ConflictSourceBranch);
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.ConflictTargetBranch);
|
||||
|
||||
ManualResetEventSlim testReady = new ManualResetEventSlim(initialState: false);
|
||||
ManualResetEventSlim fileLocked = new ManualResetEventSlim(initialState: false);
|
||||
Task task = Task.Run(() =>
|
||||
{
|
||||
int attempts = 0;
|
||||
while (attempts < 100)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (FileStream stream = new FileStream(Path.Combine(this.Enlistment.DotGVFSRoot, "databases", "RepoMetadata.dat"), FileMode.Open, FileAccess.Read, FileShare.None))
|
||||
{
|
||||
fileLocked.Set();
|
||||
testReady.Set();
|
||||
Thread.Sleep(15000);
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
++attempts;
|
||||
Thread.Sleep(50);
|
||||
}
|
||||
}
|
||||
|
||||
testReady.Set();
|
||||
});
|
||||
|
||||
// Wait for task to acquire the handle
|
||||
testReady.Wait();
|
||||
fileLocked.IsSet.ShouldBeTrue("Failed to obtain exclusive file handle. Exclusive handle required to validate behavior");
|
||||
|
||||
try
|
||||
{
|
||||
this.ValidateGitCommand("checkout " + GitRepoTests.ConflictTargetBranch);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// If the test fails, we should wait for the Task to complete so that it does not keep a handle open
|
||||
task.Wait();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CheckoutBranchWithOpenHandleBlockingProjectionDeleteAndRepoMetdataUpdate()
|
||||
{
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.ConflictSourceBranch);
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.ConflictTargetBranch);
|
||||
|
||||
this.Enlistment.UnmountGVFS();
|
||||
string gitIndexPath = Path.Combine(this.Enlistment.RepoRoot, ".git", "index");
|
||||
CopyIndexAndRename(gitIndexPath);
|
||||
this.Enlistment.MountGVFS();
|
||||
|
||||
ManualResetEventSlim testReady = new ManualResetEventSlim(initialState: false);
|
||||
ManualResetEventSlim fileLocked = new ManualResetEventSlim(initialState: false);
|
||||
Task task = Task.Run(() =>
|
||||
{
|
||||
int attempts = 0;
|
||||
while (attempts < 100)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (FileStream projectionStream = new FileStream(Path.Combine(this.Enlistment.DotGVFSRoot, "GVFS_projection"), FileMode.Open, FileAccess.Read, FileShare.None))
|
||||
using (FileStream metadataStream = new FileStream(Path.Combine(this.Enlistment.DotGVFSRoot, "databases", "RepoMetadata.dat"), FileMode.Open, FileAccess.Read, FileShare.None))
|
||||
{
|
||||
fileLocked.Set();
|
||||
testReady.Set();
|
||||
Thread.Sleep(15000);
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
++attempts;
|
||||
Thread.Sleep(50);
|
||||
}
|
||||
}
|
||||
|
||||
testReady.Set();
|
||||
});
|
||||
|
||||
// Wait for task to acquire the handle
|
||||
testReady.Wait();
|
||||
fileLocked.IsSet.ShouldBeTrue("Failed to obtain exclusive file handle. Exclusive handle required to validate behavior");
|
||||
|
||||
try
|
||||
{
|
||||
this.ValidateGitCommand("checkout " + GitRepoTests.ConflictTargetBranch);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// If the test fails, we should wait for the Task to complete so that it does not keep a handle open
|
||||
task.Wait();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CheckoutBranchWithStaleRepoMetadataTmpFileOnDisk()
|
||||
{
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.ConflictSourceBranch);
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.ConflictTargetBranch);
|
||||
|
||||
this.FileSystem.WriteAllText(Path.Combine(this.Enlistment.DotGVFSRoot, "databases", "RepoMetadata.dat.tmp"), "Stale RepoMetadata.dat.tmp file");
|
||||
this.ValidateGitCommand("checkout " + GitRepoTests.ConflictTargetBranch);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CheckoutBranchWhileOutsideToolDoesNotAllowDeleteOfOpenRepoMetadata()
|
||||
{
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.ConflictSourceBranch);
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.ConflictTargetBranch);
|
||||
|
||||
ManualResetEventSlim testReady = new ManualResetEventSlim(initialState: false);
|
||||
ManualResetEventSlim fileLocked = new ManualResetEventSlim(initialState: false);
|
||||
Task task = Task.Run(() =>
|
||||
{
|
||||
int attempts = 0;
|
||||
while (attempts < 100)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (FileStream stream = new FileStream(Path.Combine(this.Enlistment.DotGVFSRoot, "databases", "RepoMetadata.dat"), FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
|
||||
{
|
||||
fileLocked.Set();
|
||||
testReady.Set();
|
||||
Thread.Sleep(15000);
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
++attempts;
|
||||
Thread.Sleep(50);
|
||||
}
|
||||
}
|
||||
|
||||
testReady.Set();
|
||||
});
|
||||
|
||||
// Wait for task to acquire the handle
|
||||
testReady.Wait();
|
||||
fileLocked.IsSet.ShouldBeTrue("Failed to obtain file handle. Handle required to validate behavior");
|
||||
|
||||
try
|
||||
{
|
||||
this.ValidateGitCommand("checkout " + GitRepoTests.ConflictTargetBranch);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// If the test fails, we should wait for the Task to complete so that it does not keep a handle open
|
||||
task.Wait();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// WindowsOnly because the test depends on Windows specific file sharing behavior
|
||||
[TestCase]
|
||||
[Category(Categories.WindowsOnly)]
|
||||
public void CheckoutBranchWhileOutsideToolHasExclusiveReadHandleOnDatabasesFolder()
|
||||
{
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.ConflictSourceBranch);
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.ConflictTargetBranch);
|
||||
|
||||
ManualResetEventSlim testReady = new ManualResetEventSlim(initialState: false);
|
||||
ManualResetEventSlim folderLocked = new ManualResetEventSlim(initialState: false);
|
||||
Task task = Task.Run(() =>
|
||||
{
|
||||
int attempts = 0;
|
||||
string databasesPath = Path.Combine(this.Enlistment.DotGVFSRoot, "databases");
|
||||
while (attempts < 100)
|
||||
{
|
||||
using (SafeFileHandle result = CreateFile(
|
||||
databasesPath,
|
||||
NativeFileAccess.GENERIC_READ,
|
||||
FileShare.Read,
|
||||
IntPtr.Zero,
|
||||
FileMode.Open,
|
||||
NativeFileAttributes.FILE_FLAG_BACKUP_SEMANTICS | NativeFileAttributes.FILE_FLAG_OPEN_REPARSE_POINT,
|
||||
IntPtr.Zero))
|
||||
{
|
||||
if (result.IsInvalid)
|
||||
{
|
||||
++attempts;
|
||||
Thread.Sleep(50);
|
||||
}
|
||||
else
|
||||
{
|
||||
folderLocked.Set();
|
||||
testReady.Set();
|
||||
Thread.Sleep(15000);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
testReady.Set();
|
||||
});
|
||||
|
||||
// Wait for task to acquire the handle
|
||||
testReady.Wait();
|
||||
folderLocked.IsSet.ShouldBeTrue("Failed to obtain exclusive file handle. Handle required to validate behavior");
|
||||
|
||||
try
|
||||
{
|
||||
this.ValidateGitCommand("checkout " + GitRepoTests.ConflictTargetBranch);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// If the test fails, we should wait for the Task to complete so that it does not keep a handle open
|
||||
task.Wait();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void ResetMixedTwiceThenCheckoutWithChanges()
|
||||
{
|
||||
this.ControlGitRepo.Fetch("FunctionalTests/20171219_MultipleFileEdits");
|
||||
|
||||
this.ValidateGitCommand("checkout c0ca0f00063cdc969954fa9cb92dd4abe5e095e0");
|
||||
this.ValidateGitCommand("checkout -b tests/functional/ResetMixedTwice");
|
||||
|
||||
// Between the original commit c0ca0f00063cdc969954fa9cb92dd4abe5e095e0 and the second reset
|
||||
// 3ed4178bcb85085c06a24a76d2989f2364a64589, several files are changed, but none are added or
|
||||
// removed. The middle commit 2af5f08d010eade3c73a582711a36f0def10d6bc includes a variety
|
||||
// of changes including a renamed folder and new and removed files. The final checkout is
|
||||
// expected to error on changed files only.
|
||||
this.ValidateGitCommand("reset --mixed 2af5f08d010eade3c73a582711a36f0def10d6bc");
|
||||
this.ValidateGitCommand("reset --mixed 3ed4178bcb85085c06a24a76d2989f2364a64589");
|
||||
|
||||
this.ValidateGitCommand("checkout " + this.ControlGitRepo.Commitish);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void ResetMixedTwiceThenCheckoutWithRemovedFiles()
|
||||
{
|
||||
this.ControlGitRepo.Fetch("FunctionalTests/20180102_MultipleFileDeletes");
|
||||
|
||||
this.ValidateGitCommand("checkout dee2cd6645752137e4e4eb311319bb95f533c2f1");
|
||||
this.ValidateGitCommand("checkout -b tests/functional/ResetMixedTwice");
|
||||
|
||||
// Between the original commit dee2cd6645752137e4e4eb311319bb95f533c2f1 and the second reset
|
||||
// 4275906774e9cc37a6875448cd3fcdc5b3ea2be3, several files are removed, but none are changed.
|
||||
// The middle commit c272d4846f2250edfb35fcac60b4b66bb17478fa includes a variety of changes
|
||||
// including a renamed folder as well as new, removed and changed files. The final checkout
|
||||
// is expected to error on untracked (new) files only.
|
||||
this.ValidateGitCommand("reset --mixed c272d4846f2250edfb35fcac60b4b66bb17478fa");
|
||||
this.ValidateGitCommand("reset --mixed 4275906774e9cc37a6875448cd3fcdc5b3ea2be3");
|
||||
|
||||
this.ValidateGitCommand("checkout " + this.ControlGitRepo.Commitish);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void DeleteFolderAndChangeBranchToFolderWithDifferentCase()
|
||||
{
|
||||
// 692765 - Recursive modified paths entries for folders should be case insensitive when
|
||||
// changing branches
|
||||
|
||||
string folderName = "GVFlt_MultiThreadTest";
|
||||
|
||||
// Confirm that no other test has caused "GVFlt_MultiThreadTest" to be added to the modified paths database
|
||||
GVFSHelpers.ModifiedPathsShouldNotContain(this.Enlistment, this.FileSystem, folderName);
|
||||
|
||||
this.FolderShouldHaveCaseMatchingName(folderName);
|
||||
this.DeleteFolder(folderName);
|
||||
|
||||
// 4141dc6023b853740795db41a06b278ebdee0192 is the commit prior to deleting GVFLT_MultiThreadTest
|
||||
// and re-adding it as as GVFlt_MultiThreadTest
|
||||
this.ValidateGitCommand("checkout 4141dc6023b853740795db41a06b278ebdee0192");
|
||||
this.FolderShouldHaveCaseMatchingName("GVFLT_MultiThreadTest");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void SuccessfullyChecksOutDirectoryToFileToDirectory()
|
||||
{
|
||||
// This test switches between two branches and verifies specific transitions occured
|
||||
this.ControlGitRepo.Fetch("FunctionalTests/20171103_DirectoryFileTransitionsPart1");
|
||||
this.ControlGitRepo.Fetch("FunctionalTests/20171103_DirectoryFileTransitionsPart2");
|
||||
this.ValidateGitCommand("checkout FunctionalTests/20171103_DirectoryFileTransitionsPart1");
|
||||
|
||||
// Delta of interest - Check initial state
|
||||
// renamed: foo.cpp\foo.cpp -> foo.cpp
|
||||
// where the top level "foo.cpp" is a folder with a file, then becomes just a file
|
||||
// note that folder\file names picked illustrate a real example
|
||||
this.FolderShouldExistAndHaveFile("foo.cpp", "foo.cpp");
|
||||
|
||||
// Delta of interest - Check initial state
|
||||
// renamed: a\a <-> b && b <-> a
|
||||
// where a\a contains "file contents one"
|
||||
// and b contains "file contents two"
|
||||
// This tests two types of renames crossing into each other
|
||||
this.FileShouldHaveContents("file contents one", "a", "a");
|
||||
this.FileShouldHaveContents("file contents two", "b");
|
||||
|
||||
// Delta of interest - Check initial state
|
||||
// renamed: c\c <-> d\c && d\d <-> c\d
|
||||
// where c\c contains "file contents c"
|
||||
// and d\d contains "file contents d"
|
||||
// This tests two types of renames crossing into each other
|
||||
this.FileShouldHaveContents("file contents c", "c", "c");
|
||||
this.FileShouldHaveContents("file contents d", "d", "d");
|
||||
|
||||
// Now switch to second branch, part2 and verify transitions
|
||||
this.ValidateGitCommand("checkout FunctionalTests/20171103_DirectoryFileTransitionsPart2");
|
||||
|
||||
// Delta of interest - Verify change
|
||||
// renamed: foo.cpp\foo.cpp -> foo.cpp
|
||||
this.FolderShouldExistAndHaveFile(string.Empty, "foo.cpp");
|
||||
|
||||
// Delta of interest - Verify change
|
||||
// renamed: a\a <-> b && b <-> a
|
||||
this.FileShouldHaveContents("file contents two", "a");
|
||||
this.FileShouldHaveContents("file contents one", "b");
|
||||
|
||||
// Delta of interest - Verify change
|
||||
// renamed: c\c <-> d\c && d\d <-> c\d
|
||||
this.FileShouldHaveContents("file contents d", "c", "d");
|
||||
this.FileShouldHaveContents("file contents c", "d", "c");
|
||||
this.ShouldNotExistOnDisk("c", "c");
|
||||
this.ShouldNotExistOnDisk("d", "d");
|
||||
|
||||
// And back again
|
||||
this.ValidateGitCommand("checkout FunctionalTests/20171103_DirectoryFileTransitionsPart1");
|
||||
|
||||
// Delta of interest - Final validation
|
||||
// renamed: foo.cpp\foo.cpp -> foo.cpp
|
||||
this.FolderShouldExistAndHaveFile("foo.cpp", "foo.cpp");
|
||||
|
||||
// Delta of interest - Final validation
|
||||
// renamed: a\a <-> b && b <-> a
|
||||
this.FileShouldHaveContents("file contents one", "a", "a");
|
||||
this.FileShouldHaveContents("file contents two", "b");
|
||||
|
||||
// Delta of interest - Final validation
|
||||
// renamed: c\c <-> d\c && d\d <-> c\d
|
||||
this.FileShouldHaveContents("file contents c", "c", "c");
|
||||
this.FileShouldHaveContents("file contents d", "d", "d");
|
||||
this.ShouldNotExistOnDisk("c", "d");
|
||||
this.ShouldNotExistOnDisk("d", "c");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void DeleteFileThenCheckout()
|
||||
{
|
||||
this.FolderShouldExistAndHaveFile("GitCommandsTests", "DeleteFileTests", "1", "#test");
|
||||
this.DeleteFile("GitCommandsTests", "DeleteFileTests", "1", "#test");
|
||||
this.FolderShouldExistAndBeEmpty("GitCommandsTests", "DeleteFileTests", "1");
|
||||
|
||||
// Commit cb2d05febf64e3b0df50bd8d3fe8f05c0e2caa47 is before
|
||||
// the files in GitCommandsTests\DeleteFileTests were added
|
||||
this.ValidateGitCommand("checkout cb2d05febf64e3b0df50bd8d3fe8f05c0e2caa47");
|
||||
|
||||
this.ShouldNotExistOnDisk("GitCommandsTests", "DeleteFileTests", "1");
|
||||
this.ShouldNotExistOnDisk("GitCommandsTests", "DeleteFileTests");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CheckoutEditCheckoutWithoutFolderThenCheckoutWithMultipleFiles()
|
||||
{
|
||||
// Edit the file to get the entry in the modified paths database
|
||||
this.EditFile("Changing the content of one file", "DeleteFileWithNameAheadOfDotAndSwitchCommits", "1");
|
||||
this.RunGitCommand("reset --hard -q HEAD");
|
||||
|
||||
// This commit should remove the DeleteFileWithNameAheadOfDotAndSwitchCommits folder
|
||||
this.ValidateGitCommand("checkout 9ba05ac6706d3952995d0a54703fc724ddde57cc");
|
||||
|
||||
this.ShouldNotExistOnDisk("DeleteFileWithNameAheadOfDotAndSwitchCommits");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
[Category(Categories.MacTODO.NeedsNewFolderCreateNotification)]
|
||||
public void CreateAFolderThenCheckoutBranchWithFolder()
|
||||
{
|
||||
this.FolderShouldExistAndHaveFile("DeleteFileWithNameAheadOfDotAndSwitchCommits", "1");
|
||||
|
||||
// This commit should remove the DeleteFileWithNameAheadOfDotAndSwitchCommits folder
|
||||
this.ValidateGitCommand("checkout 9ba05ac6706d3952995d0a54703fc724ddde57cc");
|
||||
this.ShouldNotExistOnDisk("DeleteFileWithNameAheadOfDotAndSwitchCommits");
|
||||
this.CreateFolder("DeleteFileWithNameAheadOfDotAndSwitchCommits");
|
||||
this.ValidateGitCommand("checkout " + this.ControlGitRepo.Commitish);
|
||||
this.FolderShouldExistAndHaveFile("DeleteFileWithNameAheadOfDotAndSwitchCommits", "1");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CheckoutBranchWithDirectoryNameSameAsFile()
|
||||
{
|
||||
this.SetupForFileDirectoryTest();
|
||||
this.ValidateFileDirectoryTest("checkout");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CheckoutBranchWithDirectoryNameSameAsFileEnumerate()
|
||||
{
|
||||
this.RunFileDirectoryEnumerateTest("checkout");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CheckoutBranchWithDirectoryNameSameAsFileWithRead()
|
||||
{
|
||||
this.RunFileDirectoryReadTest("checkout");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CheckoutBranchWithDirectoryNameSameAsFileWithWrite()
|
||||
{
|
||||
this.RunFileDirectoryWriteTest("checkout");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CheckoutBranchDirectoryWithOneFile()
|
||||
{
|
||||
this.SetupForFileDirectoryTest(commandBranch: GitRepoTests.DirectoryWithDifferentFileAfterBranch);
|
||||
this.ValidateFileDirectoryTest("checkout", commandBranch: GitRepoTests.DirectoryWithDifferentFileAfterBranch);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CheckoutBranchDirectoryWithOneFileEnumerate()
|
||||
{
|
||||
this.RunFileDirectoryEnumerateTest("checkout", commandBranch: GitRepoTests.DirectoryWithDifferentFileAfterBranch);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CheckoutBranchDirectoryWithOneFileRead()
|
||||
{
|
||||
this.RunFileDirectoryReadTest("checkout", commandBranch: GitRepoTests.DirectoryWithDifferentFileAfterBranch);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CheckoutBranchDirectoryWithOneFileWrite()
|
||||
{
|
||||
this.RunFileDirectoryWriteTest("checkout", commandBranch: GitRepoTests.DirectoryWithDifferentFileAfterBranch);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CheckoutBranchDirectoryWithOneDeepFileWrite()
|
||||
{
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.DeepDirectoryWithOneFile);
|
||||
this.ControlGitRepo.Fetch(GitRepoTests.DeepDirectoryWithOneDifferentFile);
|
||||
this.ValidateGitCommand($"checkout {GitRepoTests.DeepDirectoryWithOneFile}");
|
||||
this.FileShouldHaveContents(
|
||||
"TestFile1\n",
|
||||
"GitCommandsTests",
|
||||
"CheckoutBranchDirectoryWithOneDeepFile",
|
||||
"FolderDepth1",
|
||||
"FolderDepth2",
|
||||
"FolderDepth3",
|
||||
"File1.txt");
|
||||
|
||||
// Edit the file and commit the change so that git will
|
||||
// delete the file (and its parent directories) when
|
||||
// changing branches
|
||||
this.EditFile(
|
||||
"Change file",
|
||||
"GitCommandsTests",
|
||||
"CheckoutBranchDirectoryWithOneDeepFile",
|
||||
"FolderDepth1",
|
||||
"FolderDepth2",
|
||||
"FolderDepth3",
|
||||
"File1.txt");
|
||||
this.ValidateGitCommand("add --all");
|
||||
this.RunGitCommand("commit -m \"Some change\"");
|
||||
|
||||
this.ValidateGitCommand($"checkout {GitRepoTests.DeepDirectoryWithOneDifferentFile}");
|
||||
this.FileShouldHaveContents(
|
||||
"TestFile2\n",
|
||||
"GitCommandsTests",
|
||||
"CheckoutBranchDirectoryWithOneDeepFile",
|
||||
"FolderDepth1",
|
||||
"FolderDepth2",
|
||||
"FolderDepth3",
|
||||
"File2.txt");
|
||||
this.ShouldNotExistOnDisk(
|
||||
"GitCommandsTests",
|
||||
"CheckoutBranchDirectoryWithOneDeepFile",
|
||||
"FolderDepth1",
|
||||
"FolderDepth2",
|
||||
"FolderDepth3",
|
||||
"File1.txt");
|
||||
}
|
||||
|
||||
private static void CopyIndexAndRename(string indexPath)
|
||||
{
|
||||
string tempIndexPath = indexPath + ".lock";
|
||||
using (FileStream currentIndexStream = new FileStream(indexPath, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
using (FileStream tempIndexStream = new FileStream(tempIndexPath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.ReadWrite))
|
||||
{
|
||||
currentIndexStream.CopyTo(tempIndexStream);
|
||||
}
|
||||
|
||||
File.Delete(indexPath);
|
||||
File.Move(tempIndexPath, indexPath);
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
private static extern SafeFileHandle CreateFile(
|
||||
[In] string fileName,
|
||||
[MarshalAs(UnmanagedType.U4)] NativeFileAccess desiredAccess,
|
||||
FileShare shareMode,
|
||||
[In] IntPtr securityAttributes,
|
||||
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
|
||||
[MarshalAs(UnmanagedType.U4)] NativeFileAttributes flagsAndAttributes,
|
||||
[In] IntPtr templateFile);
|
||||
}
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
using GVFS.FunctionalTests.Properties;
|
||||
using GVFS.FunctionalTests.Should;
|
||||
using GVFS.FunctionalTests.Tools;
|
||||
using NUnit.Framework;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace GVFS.FunctionalTests.Tests.GitCommands
|
||||
{
|
||||
[TestFixtureSource(typeof(GitRepoTests), nameof(GitRepoTests.ValidateWorkingTree))]
|
||||
[Category(Categories.GitCommands)]
|
||||
public class CreatePlaceholderTests : GitRepoTests
|
||||
{
|
||||
private static readonly string FileToRead = Path.Combine("GVFS", "GVFS", "Program.cs");
|
||||
|
||||
public CreatePlaceholderTests(Settings.ValidateWorkingTreeMode validateWorkingTree)
|
||||
: base(enlistmentPerTest: true, validateWorkingTree: validateWorkingTree)
|
||||
{
|
||||
}
|
||||
|
||||
[TestCase("check-attr --stdin --all")]
|
||||
[TestCase("check-ignore --stdin")]
|
||||
[TestCase("check-mailmap --stdin")]
|
||||
[TestCase("diff-tree --stdin")]
|
||||
[TestCase("hash-object --stdin")]
|
||||
[TestCase("index-pack --stdin")]
|
||||
[TestCase("name-rev --stdin")]
|
||||
[TestCase("rev-list --stdin --quiet --all")]
|
||||
[TestCase("update-ref --stdin")]
|
||||
public void AllowsPlaceholderCreationWhileGitCommandIsRunning(string commandToRun)
|
||||
{
|
||||
this.CheckPlaceholderCreation(commandToRun, shouldAllow: true);
|
||||
}
|
||||
|
||||
[TestCase("checkout-index --stdin")]
|
||||
[TestCase("fetch-pack --stdin URL")]
|
||||
[TestCase("notes copy --stdin")]
|
||||
[TestCase("reset --stdin")]
|
||||
[TestCase("send-pack --stdin URL")]
|
||||
[TestCase("update-index --stdin")]
|
||||
[Category(Categories.WindowsOnly)] // Mac never blocks placeholder creation
|
||||
public void BlocksPlaceholderCreationWhileGitCommandIsRunning(string commandToRun)
|
||||
{
|
||||
this.CheckPlaceholderCreation(commandToRun, shouldAllow: false);
|
||||
}
|
||||
|
||||
private void CheckPlaceholderCreation(string command, bool shouldAllow)
|
||||
{
|
||||
string eofCharacter = "\x04";
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
eofCharacter = "\x1A";
|
||||
}
|
||||
|
||||
this.EditFile($"Some new content for {command}.", "Protocol.md");
|
||||
ManualResetEventSlim resetEvent = GitHelpers.RunGitCommandWithWaitAndStdIn(this.Enlistment, resetTimeout: 3000, command: $"{command}", stdinToQuit: eofCharacter, processId: out _);
|
||||
|
||||
if (shouldAllow)
|
||||
{
|
||||
this.FileContentsShouldMatch(FileToRead);
|
||||
}
|
||||
else
|
||||
{
|
||||
string virtualPath = Path.Combine(this.Enlistment.RepoRoot, FileToRead);
|
||||
string controlPath = Path.Combine(this.ControlGitRepo.RootPath, FileToRead);
|
||||
virtualPath.ShouldNotExistOnDisk(this.FileSystem);
|
||||
controlPath.ShouldBeAFile(this.FileSystem);
|
||||
}
|
||||
|
||||
this.ValidateGitCommand("--no-optional-locks status");
|
||||
resetEvent.Wait();
|
||||
this.RunGitCommand("reset --hard");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
using GVFS.FunctionalTests.Properties;
|
||||
using GVFS.FunctionalTests.Should;
|
||||
using GVFS.FunctionalTests.Tools;
|
||||
using NUnit.Framework;
|
||||
using System.IO;
|
||||
|
||||
namespace GVFS.FunctionalTests.Tests.GitCommands
|
||||
{
|
||||
[TestFixture]
|
||||
[Category(Categories.GitCommands)]
|
||||
public class HashObjectTests : GitRepoTests
|
||||
{
|
||||
public HashObjectTests()
|
||||
: base(enlistmentPerTest: false, validateWorkingTree: Settings.ValidateWorkingTreeMode.None)
|
||||
{
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CanReadFileAfterHashObject()
|
||||
{
|
||||
this.ValidateGitCommand("status");
|
||||
|
||||
// Validate that Scripts\RunUnitTests.bad is not on disk at all
|
||||
string filePath = Path.Combine("Scripts", "RunUnitTests.bat");
|
||||
|
||||
this.Enlistment.UnmountGVFS();
|
||||
this.Enlistment.GetVirtualPathTo(filePath).ShouldNotExistOnDisk(this.FileSystem);
|
||||
this.Enlistment.MountGVFS();
|
||||
|
||||
// TODO 1087312: Fix 'git hash-oject' so that it works for files that aren't on disk yet
|
||||
GitHelpers.InvokeGitAgainstGVFSRepo(
|
||||
this.Enlistment.RepoRoot,
|
||||
"hash-object " + GitHelpers.ConvertPathToGitFormat(filePath));
|
||||
|
||||
this.FileContentsShouldMatch(filePath);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
using GVFS.FunctionalTests.Properties;
|
||||
using GVFS.FunctionalTests.Should;
|
||||
using GVFS.FunctionalTests.Tools;
|
||||
using NUnit.Framework;
|
||||
using System.IO;
|
||||
|
||||
namespace GVFS.FunctionalTests.Tests.GitCommands
|
||||
{
|
||||
[TestFixture]
|
||||
public class RmTests : GitRepoTests
|
||||
{
|
||||
public RmTests()
|
||||
: base(enlistmentPerTest: false, validateWorkingTree: Settings.ValidateWorkingTreeMode.None)
|
||||
{
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CanReadFileAfterGitRmDryRun()
|
||||
{
|
||||
this.ValidateGitCommand("status");
|
||||
|
||||
// Validate that Scripts\RunUnitTests.bad is not on disk at all
|
||||
string filePath = Path.Combine("Scripts", "RunUnitTests.bat");
|
||||
|
||||
this.Enlistment.UnmountGVFS();
|
||||
this.Enlistment.GetVirtualPathTo(filePath).ShouldNotExistOnDisk(this.FileSystem);
|
||||
this.Enlistment.MountGVFS();
|
||||
|
||||
this.ValidateGitCommand("rm --dry-run " + GitHelpers.ConvertPathToGitFormat(filePath));
|
||||
this.FileContentsShouldMatch(filePath);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
using GVFS.Tests.Should;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace GVFS.FunctionalTests.Tools
|
||||
{
|
||||
public class ProjFSFilterInstaller
|
||||
{
|
||||
private const string GVFSServiceName = "GVFS.Service";
|
||||
private const string ProjFSServiceName = "prjflt";
|
||||
private const string OptionalFeatureName = "Client-ProjFS";
|
||||
private const string GVFSInstallPath = @"C:\Program Files\GVFS";
|
||||
private const string NativeProjFSLibInstallLocation = GVFSInstallPath + @"\ProjFS\ProjectedFSLib.dll";
|
||||
|
||||
private const string PrjfltInfName = "prjflt.inf";
|
||||
private const string PrjfltInfInstallFolder = GVFSInstallPath + @"\Filter";
|
||||
|
||||
private const string PrjfltSysName = "prjflt.sys";
|
||||
private const string System32DriversPath = @"C:\Windows\System32\drivers";
|
||||
|
||||
public static void ReplaceInboxProjFS()
|
||||
{
|
||||
if (IsInboxProjFSEnabled())
|
||||
{
|
||||
StopService(GVFSServiceName);
|
||||
StopService(ProjFSServiceName);
|
||||
DisableAndRemoveInboxProjFS();
|
||||
InstallProjFSViaINF();
|
||||
ValidateProjFSInstalled();
|
||||
StartService(ProjFSServiceName);
|
||||
StartService(GVFSServiceName);
|
||||
}
|
||||
else
|
||||
{
|
||||
ValidateProjFSInstalled();
|
||||
}
|
||||
}
|
||||
|
||||
private static ProcessResult CallPowershellCommand(string command)
|
||||
{
|
||||
return ProcessHelper.Run("powershell.exe", "-NonInteractive -NoProfile -Command \"& { " + command + " }\"");
|
||||
}
|
||||
|
||||
private static bool IsInboxProjFSEnabled()
|
||||
{
|
||||
const int ProjFSNotAnOptionalFeature = 2;
|
||||
const int ProjFSEnabled = 3;
|
||||
const int ProjFSDisabled = 4;
|
||||
|
||||
ProcessResult getOptionalFeatureResult = CallPowershellCommand(
|
||||
"$var=(Get-WindowsOptionalFeature -Online -FeatureName " + OptionalFeatureName + "); if($var -eq $null){exit " +
|
||||
ProjFSNotAnOptionalFeature + "}else{if($var.State -eq 'Enabled'){exit " + ProjFSEnabled + "}else{exit " + ProjFSDisabled + "}}");
|
||||
|
||||
return getOptionalFeatureResult.ExitCode == ProjFSEnabled;
|
||||
}
|
||||
|
||||
private static void StartService(string serviceName)
|
||||
{
|
||||
ProcessResult result = ProcessHelper.Run("sc.exe", $"start {serviceName}");
|
||||
Console.WriteLine($"sc start {serviceName} Output: {result.Output}");
|
||||
Console.WriteLine($"sc start {serviceName} Errors: {result.Errors}");
|
||||
result.ExitCode.ShouldEqual(0, $"Failed to start {serviceName}");
|
||||
}
|
||||
|
||||
private static void StopService(string serviceName)
|
||||
{
|
||||
ProcessResult result = ProcessHelper.Run("sc.exe", $"stop {serviceName}");
|
||||
|
||||
// 1060 -> The specified service does not exist as an installed service
|
||||
// 1062 -> The service has not been started
|
||||
bool stopSucceeded = result.ExitCode == 0 || result.ExitCode == 1060 || result.ExitCode == 1062;
|
||||
Console.WriteLine($"sc stop {serviceName} Output: {result.Output}");
|
||||
Console.WriteLine($"sc stop {serviceName} Errors: {result.Errors}");
|
||||
stopSucceeded.ShouldBeTrue($"Failed to stop {serviceName}");
|
||||
}
|
||||
|
||||
private static void DisableAndRemoveInboxProjFS()
|
||||
{
|
||||
ProcessResult disableFeatureResult = CallPowershellCommand("Disable-WindowsOptionalFeature -Online -FeatureName " + OptionalFeatureName + " -Remove");
|
||||
Console.WriteLine($"Disable ProjfS Output: {disableFeatureResult.Output}");
|
||||
Console.WriteLine($"Disable ProjfS Errors: {disableFeatureResult.Errors}");
|
||||
disableFeatureResult.ExitCode.ShouldEqual(0, "Error when disabling ProjFS");
|
||||
}
|
||||
|
||||
private static void InstallProjFSViaINF()
|
||||
{
|
||||
File.Exists(NativeProjFSLibInstallLocation).ShouldBeTrue($"{NativeProjFSLibInstallLocation} missing");
|
||||
File.Copy(NativeProjFSLibInstallLocation, GVFSInstallPath + @"\ProjectedFSLib.dll", overwrite: true);
|
||||
|
||||
string prjfltInfInstallLocation = Path.Combine(PrjfltInfInstallFolder, PrjfltInfName);
|
||||
File.Exists(prjfltInfInstallLocation).ShouldBeTrue($"{prjfltInfInstallLocation} missing");
|
||||
ProcessResult result = ProcessHelper.Run("RUNDLL32.EXE", $"SETUPAPI.DLL,InstallHinfSection DefaultInstall 128 {prjfltInfInstallLocation}");
|
||||
result.ExitCode.ShouldEqual(0, "Failed to install ProjFS via INF");
|
||||
}
|
||||
|
||||
private static void ValidateProjFSInstalled()
|
||||
{
|
||||
string installPathPrjflt = Path.Combine(PrjfltInfInstallFolder, PrjfltSysName);
|
||||
string system32Prjflt = Path.Combine(System32DriversPath, PrjfltSysName);
|
||||
ProcessResult result = ProcessHelper.Run("fc.exe", $"/b \"{installPathPrjflt}\" \"{system32Prjflt}\"");
|
||||
result.ExitCode.ShouldEqual(0, $"fc failed to validate prjflt.sys");
|
||||
result.Output.ShouldContain("no differences encountered");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<Import Project="..\GVFS.Build\GVFS.cs.props" />
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{1118B427-7063-422F-83B9-5023C8EC5A7A}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>GVFS.GVFlt</RootNamespace>
|
||||
<AssemblyName>GVFS.GVFlt</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="GVFltCallbacks.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Analyzer Include="..\..\..\packages\StyleCop.Analyzers.1.0.2\analyzers\dotnet\cs\StyleCop.Analyzers.CodeFixes.dll" />
|
||||
<Analyzer Include="..\..\..\packages\StyleCop.Analyzers.1.0.2\analyzers\dotnet\cs\StyleCop.Analyzers.dll" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -1,34 +0,0 @@
|
|||
using Newtonsoft.Json;
|
||||
using System;
|
||||
|
||||
namespace GVFS.GVFlt
|
||||
{
|
||||
public class GVFltCallbacks
|
||||
{
|
||||
/// <summary>
|
||||
/// This struct must remain here for DiskLayout9to10Upgrade_BackgroundAndPlaceholderListToFileBased
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This struct should only be used by the upgrader, it has been replaced by GVFS.Virtualization.Background.FileSystemTask
|
||||
/// </remarks>
|
||||
[Serializable]
|
||||
public struct BackgroundGitUpdate
|
||||
{
|
||||
// This enum must be present or the BinarySerializer will always deserialze Operation as 0
|
||||
public enum OperationType
|
||||
{
|
||||
Invalid = 0,
|
||||
}
|
||||
|
||||
public OperationType Operation { get; set; }
|
||||
public string VirtualPath { get; set; }
|
||||
public string OldVirtualPath { get; set; }
|
||||
|
||||
// Used by the logging in the upgrader
|
||||
public override string ToString()
|
||||
{
|
||||
return JsonConvert.SerializeObject(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("GVFS.GVFlt")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("GVFS.GVFlt")]
|
||||
[assembly: AssemblyCopyright("Copyright © Microsoft 2019")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("1118b427-7063-422f-83b9-5023c8ec5a7a")]
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче