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:
Derrick Stolee 2019-08-07 10:45:29 -04:00
Родитель 1d9b7ae509
Коммит d748084d85
455 изменённых файлов: 136 добавлений и 59930 удалений

Просмотреть файл

@ -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

3
.gitignore поставляемый
Просмотреть файл

@ -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
Просмотреть файл

@ -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")]

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше