diff --git a/.github/workflows/p4vfs-verify.yml b/.github/workflows/p4vfs-verify.yml index 597ff45..476306f 100644 --- a/.github/workflows/p4vfs-verify.yml +++ b/.github/workflows/p4vfs-verify.yml @@ -28,7 +28,7 @@ jobs: runs-on: ${{matrix.os}} strategy: matrix: - os: [windows-latest] + os: [windows-2022] configuration: [DebugDev, ReleaseDev] steps: diff --git a/external/OpenSSL/OpenSSL.Module.cs b/external/OpenSSL/OpenSSL.Module.cs index 482c690..d30a5e5 100644 --- a/external/OpenSSL/OpenSSL.Module.cs +++ b/external/OpenSSL/OpenSSL.Module.cs @@ -12,7 +12,7 @@ namespace Microsoft.P4VFS.External { public class OpensslModule : Module { - private const string OPENSSL_VERSION = "3.3.3"; + private const string OPENSSL_VERSION = "3.0.18"; private const string PERL_PACKAGE_NAME = "StrawberryPerl"; private const string PERL_VERSION = "5.28.0.1"; @@ -75,7 +75,7 @@ private void BuildOpensslLibrary(string opensslArchiveFolder, string opensslTarg $"@ECHO ON", $"CALL \"{vcvarsScriptPath}\"", $"CD /D \"{opensslArchiveFolder}\"", - $"\"{perlExe}\" Configure VC-WIN64A no-asm no-apps no-tests \"--prefix={opensslConfigurationFolder}\" \"--openssldir={opensslConfigurationFolder}-ssl\" --{configuration}", + $"\"{perlExe}\" Configure VC-WIN64A no-asm no-tests \"--prefix={opensslConfigurationFolder}\" \"--openssldir={opensslConfigurationFolder}-ssl\" --{configuration}", $"IF %ERRORLEVEL% NEQ 0 EXIT /B 1", $"nmake clean", $"IF %ERRORLEVEL% NEQ 0 EXIT /B 1", diff --git a/external/P4API/P4API.Module.cs b/external/P4API/P4API.Module.cs index 71799be..9c9df93 100644 --- a/external/P4API/P4API.Module.cs +++ b/external/P4API/P4API.Module.cs @@ -12,7 +12,7 @@ namespace Microsoft.P4VFS.External { public class P4apiModule : Module { - private const string P4API_VERSION = "r24.1"; + private const string P4API_VERSION = "r25.2"; private const string P4API_VISUAL_STUDIO_EDITION = "2022"; public override string Name diff --git a/source/P4VFS.Console/P4VFS.Notes.txt b/source/P4VFS.Console/P4VFS.Notes.txt index 0b968ce..2219383 100644 --- a/source/P4VFS.Console/P4VFS.Notes.txt +++ b/source/P4VFS.Console/P4VFS.Notes.txt @@ -1,5 +1,28 @@ Microsoft P4VFS Release Notes +Version [1.29.4.0] +* Updating to latest P4API 25.2 +* Migrating to OpenSSL 3.0 LTS as recommended by P4API 25.2 with latest 3.0.18 +* Addition to default ExcludedProcessNames of MpDlpService.exe which is common Windows + Defender application +* Optimization to use C++ transparent comparators on std::map to enable + heterogeneous lookup from either c_str or string +* Addition of default property values to SettingManager. It is now used to test + against combinations of default and overridden settings values +* Fixing bug where p4vfs.exe common option '-v' would not actually enable verbose logging + for the command +* Addition of experimental configuration setting MaxDiff2StatFileCount for specifying + the maximum number of files for when fstat commands should be used instead of diff2 + for determining the have & head filetype of files to sync. Using fstat can be + faster than diff2 in the rare case where a very few number files are updated under a + very large file spec. For that reason, we'll keep this feature disabled by default indefinitly. + A MaxDiff2StatFileCount of zero (default) will use legacy behavior to only use + diff2. A MaxDiff2StatFileCount of less than zero will always use fstat. A + MaxDiff2StatFileCount of greater than zero will use fstat if the number of modifications + are less than the value. +* Addition of a new verbose mode "Preview Time" profile time to the sync command which + indicates the time spend gathering virtual sync file modifications to be applied. + Version [1.29.3.0] * Perforce SSO login from URL now uses impersonated p4vfs login command and ShellExecute instead of using cmd.exe. This fixes rare case lingering cmd.exe processes from failed diff --git a/source/P4VFS.Console/Source/Program.cs b/source/P4VFS.Console/Source/Program.cs index d0e13e1..9eb5da1 100644 --- a/source/P4VFS.Console/Source/Program.cs +++ b/source/P4VFS.Console/Source/Program.cs @@ -264,6 +264,8 @@ public static int Main(string[] args) try { VirtualFileSystemLog.Intitialize(); + VirtualFileSystemLog.Info("P4VFS version {0}", VirtualFileSystem.CurrentVersion); + SettingManager.RemoteLogging = SettingManager.ConsoleRemoteLogging; SettingManager.ImmediateLogging = SettingManager.ConsoleImmediateLogging; _P4Directory = Environment.CurrentDirectory; @@ -317,7 +319,6 @@ public static int Main(string[] args) } } - VirtualFileSystemLog.Info("P4VFS version {0}", VirtualFileSystem.CurrentVersion); if (argIndex >= args.Length) { argIndex = 0; diff --git a/source/P4VFS.Core/Include/DepotClient.h b/source/P4VFS.Core/Include/DepotClient.h index 91c8cc3..b3f68c5 100644 --- a/source/P4VFS.Core/Include/DepotClient.h +++ b/source/P4VFS.Core/Include/DepotClient.h @@ -114,7 +114,7 @@ namespace P4 { template Result FDepotClient::Run(const DepotCommand& cmd) { - Result result = std::make_shared(); + Result result = MakeResult(); Run(cmd, *result.get()); return result; } diff --git a/source/P4VFS.Core/Include/DepotOperations.h b/source/P4VFS.Core/Include/DepotOperations.h index e8d0e8b..d28004d 100644 --- a/source/P4VFS.Core/Include/DepotOperations.h +++ b/source/P4VFS.Core/Include/DepotOperations.h @@ -147,6 +147,22 @@ namespace P4 { CreateFileSpecFlags::Enum flags = CreateFileSpecFlags::None ); + static DepotStringArray + DepotOperations::CreateFileSpecs( + DepotClient& depotClient, + const DepotSyncActionInfoArray& fileModifications, + const DepotRevision& revision, + CreateFileSpecFlags::Enum flags + ); + + static DepotResultDiff2 + DepotOperations::Diff2Stat( + DepotClient& depotClient, + DepotSyncFlags::Enum syncFlags, + const DepotStringArray& fileSpecs, + const DepotSyncActionInfoArray& fileModifications + ); + static DepotResultDiff2 Diff2( DepotClient& depotClient, @@ -175,6 +191,11 @@ namespace P4 { SizesFlags::Enum flags = SizesFlags::None ); + static bool + IsFullDepotFileSpec( + const DepotString& fileSpec + ); + static bool IsFileTypeAlwaysResident( const DepotString& syncResident, diff --git a/source/P4VFS.Core/Include/DepotResult.h b/source/P4VFS.Core/Include/DepotResult.h index ca52e80..ddee104 100644 --- a/source/P4VFS.Core/Include/DepotResult.h +++ b/source/P4VFS.Core/Include/DepotResult.h @@ -29,13 +29,13 @@ namespace P4 { struct FDepotResultTag { - bool ContainsKey(const DepotString& tagKey) const; - void RemoveKey(const DepotString& tagKey); - void SetValue(const DepotString& tagKey, const DepotString& tagValue); + bool ContainsKey(const char* tagKey) const; + void RemoveKey(const char* tagKey); + void SetValue(const char* tagKey, const DepotString& tagValue); - bool TryGetValue(const DepotString& tagKey, DepotString& value) const; - const DepotString& GetValue(const DepotString& tagKey) const; - const DepotString* GetValuePtr(const DepotString& tagKey) const; + bool TryGetValue(const char* tagKey, DepotString& value) const; + const DepotString& GetValue(const char* tagKey) const; + const DepotString* GetValuePtr(const char* tagKey) const; int32_t GetValueInt32(const char* tagKey, int32_t defaultValue = 0) const; int64_t GetValueInt64(const char* tagKey, int64_t defaultValue = 0) const; @@ -79,7 +79,12 @@ namespace P4 { P4VFS_CORE_API const Array& TagList() const; P4VFS_CORE_API const Array& TextList() const; - const DepotString& GetTagValue(const DepotString& tagKey) const; + const DepotString& GetTagValue(const char* tagKey) const; + + void Append(const FDepotResult& src); + void Append(const Array& srcTagList); + void Append(const Array& srcTextList); + void Append(const DepotResultTag& srcTag); protected: friend class DepotClientCommand; @@ -95,12 +100,12 @@ namespace P4 { return m_Tag.get() ? *m_Tag : Empty; } - bool ContainsTagKey(const DepotString& tagKey) const + bool ContainsTagKey(const char* tagKey) const { return Tag().ContainsKey(tagKey); } - void RemoveTagKey(const DepotString& tagKey) + void RemoveTagKey(const char* tagKey) { if (m_Tag.get()) { @@ -108,7 +113,7 @@ namespace P4 { } } - void SetTagValue(const DepotString& tagKey, const DepotString& tagValue) + void SetTagValue(const char* tagKey, const DepotString& tagValue) { if (m_Tag.get() == nullptr) { @@ -117,7 +122,7 @@ namespace P4 { m_Tag->SetValue(tagKey, tagValue); } - const DepotString& GetTagValue(const DepotString& tagKey) const + const DepotString& GetTagValue(const char* tagKey) const { return Tag().GetValue(tagKey); } @@ -162,6 +167,20 @@ namespace P4 { return FDepotResultNode::Create(index < m_TagList.size() ? m_TagList[index] : nullptr); } }; + + template + Result MakeResult() + { + return std::make_shared(); + } + + template + Result MakeErrorResult(const DepotString& errorText) + { + Result result = MakeResult(); + result->SetError(errorText.c_str()); + return result; + } }}} #pragma managed(pop) diff --git a/source/P4VFS.Core/Include/FileCore.h b/source/P4VFS.Core/Include/FileCore.h index b868cec..0f515ce 100644 --- a/source/P4VFS.Core/Include/FileCore.h +++ b/source/P4VFS.Core/Include/FileCore.h @@ -288,36 +288,42 @@ namespace FileCore { { template bool operator()(const TL& a, const TR& b) const { return StringInfo::Strcmp(StringInfo::CStr(a), StringInfo::CStr(b)) < 0; } + using is_transparent = int; }; struct LessInsensitive { template bool operator()(const TL& a, const TR& b) const { return StringInfo::Stricmp(StringInfo::CStr(a), StringInfo::CStr(b)) < 0; } + using is_transparent = int; }; struct Greater { template bool operator()(const TL& a, const TR& b) const { return StringInfo::Strcmp(StringInfo::CStr(a), StringInfo::CStr(b)) > 0; } + using is_transparent = int; }; struct GreaterInsensitive { template bool operator()(const TL& a, const TR& b) const { return StringInfo::Stricmp(StringInfo::CStr(a), StringInfo::CStr(b)) > 0; } + using is_transparent = int; }; struct Equal { template bool operator()(const TL& a, const TR& b) const { return StringInfo::Strcmp(StringInfo::CStr(a), StringInfo::CStr(b)) == 0; } + using is_transparent = int; }; struct EqualInsensitive { template bool operator()(const TL& a, const TR& b) const { return StringInfo::Stricmp(StringInfo::CStr(a), StringInfo::CStr(b)) == 0; } + using is_transparent = int; }; class WtoA @@ -377,6 +383,7 @@ namespace FileCore { }; #define LITERAL(CharType, str) ::Microsoft::P4VFS::FileCore::StringInfo::Traits::Type::Literal(str, L##str) + #define LITERAL_STRING(StringType, str) LITERAL(typename std::decay::type::value_type, str) }; struct P4VFS_CORE_API FileInfo @@ -603,6 +610,12 @@ namespace FileCore { return std::any_of(elements.begin(), elements.end(), predicate); } + template + static bool All(const ArrayType& elements, Predicate predicate) + { + return std::all_of(elements.begin(), elements.end(), predicate); + } + template static void RemoveIf(ArrayType& elements, Predicate predicate) { @@ -739,18 +752,20 @@ namespace FileCore { do { if (((v) == (ns::ev)) || ((ns::ev) != 0 && ((v) & (ns::ev)))) \ { \ if (s.empty() == false) \ - s += "|"; \ - s += #ev; \ + { \ + s += LITERAL_STRING(s, "|"); \ + } \ + s += LITERAL_STRING(s, #ev); \ } } while(0) - #define P4VFS_ENUM_TO_STRING_RETURN(v, ns, ev) \ + #define P4VFS_ENUM_TO_STRING_RETURN(rt, v, ns, ev) \ do { if ((v) == (ns::ev)) \ { \ - return #ev; \ + return LITERAL(rt::value_type, #ev); \ } } while (0) #define P4VFS_STRING_TO_ENUM_RETURN(v, ns, ev) \ - do { if (Microsoft::P4VFS::FileCore::StringInfo::Stricmp(#ev, Microsoft::P4VFS::FileCore::StringInfo::CStr(v)) == 0) \ + do { if (Microsoft::P4VFS::FileCore::StringInfo::Stricmp(LITERAL_STRING(v, #ev), Microsoft::P4VFS::FileCore::StringInfo::CStr(v)) == 0) \ { \ return ns::ev; \ } } while (0) diff --git a/source/P4VFS.Core/Include/LogDevice.h b/source/P4VFS.Core/Include/LogDevice.h index ce76868..f8d3b86 100644 --- a/source/P4VFS.Core/Include/LogDevice.h +++ b/source/P4VFS.Core/Include/LogDevice.h @@ -21,8 +21,8 @@ namespace FileCore { Error }; - P4VFS_CORE_API static AString ToString(Enum value); - P4VFS_CORE_API static LogChannel::Enum FromString(const AString& value); + P4VFS_CORE_API static String ToString(Enum value); + P4VFS_CORE_API static LogChannel::Enum FromString(const String& value); }; struct LogElement diff --git a/source/P4VFS.Core/Include/SettingManager.h b/source/P4VFS.Core/Include/SettingManager.h index f32b14a..fa2beee 100644 --- a/source/P4VFS.Core/Include/SettingManager.h +++ b/source/P4VFS.Core/Include/SettingManager.h @@ -24,13 +24,14 @@ namespace FileCore { _N( bool, SyncDefaultQuiet, false ) \ _N( String, SyncResidentPattern, L"" ) \ _N( bool, Unattended, false ) \ - _N( String, Verbosity, CSTR_ATOW(FileCore::LogChannel::ToString(FileCore::LogChannel::Info)) ) \ - _N( String, ExcludedProcessNames, L"MsSense.exe;MsMpEng.exe;SenseCE.exe;SenseIR.exe;SearchProtocolHost.exe" ) \ + _N( String, Verbosity, FileCore::LogChannel::ToString(FileCore::LogChannel::Info).c_str() ) \ + _N( String, ExcludedProcessNames, L"MsSense.exe;MsMpEng.exe;SenseCE.exe;SenseIR.exe;SearchProtocolHost.exe;MpDlpService.exe" ) \ _N( int32_t, CreateFileRetryCount, 8 ) \ _N( int32_t, CreateFileRetryWaitMs, 250 ) \ _N( int32_t, PoolDefaultNumberOfThreads, 8 ) \ _N( int32_t, GarbageCollectPeriodMs, 5*60*1000 ) \ _N( int32_t, DepotClientCacheIdleTimeoutMs, 5*60*1000 ) \ + _N( int32_t, MaxDiff2StatFileCount, 0 ) \ class SettingManager; @@ -82,7 +83,7 @@ namespace FileCore { P4VFS_CORE_API void SetInt32(int32_t value); P4VFS_CORE_API void SetString(const String& value); - template T Get(const T& defaultValue) const = 0; + template T Get(const T& defaultValue) const; template <> bool Get(const bool& defaultValue) const { return GetBool(defaultValue); } template <> int32_t Get(const int32_t& defaultValue) const { return GetInt32(defaultValue); } template <> String Get(const String& defaultValue) const { return GetString(defaultValue); } @@ -154,6 +155,13 @@ namespace FileCore { SETTING_MANAGER_PROPERTIES(SETTING_MANAGER_DECLARE_PROP) #undef SETTING_MANAGER_DECLARE_PROP + struct Default + { + #define SETTING_MANAGER_DECLARE_PROP(type, name, value) static type name(); + SETTING_MANAGER_PROPERTIES(SETTING_MANAGER_DECLARE_PROP) + #undef SETTING_MANAGER_DECLARE_PROP + }; + private: PropertyMap m_PropertMap; HANDLE m_PropertMapMutex; diff --git a/source/P4VFS.Core/Source/DepotOperations.cpp b/source/P4VFS.Core/Source/DepotOperations.cpp index d4c3507..f37580e 100644 --- a/source/P4VFS.Core/Source/DepotOperations.cpp +++ b/source/P4VFS.Core/Source/DepotOperations.cpp @@ -90,13 +90,16 @@ DepotOperations::SyncVirtual( } // Retrieve a list of files to be added, deleted, and updated + DepotStopwatch previewTime(DepotStopwatch::Init::Start); DepotSyncActionInfoArray modifications = SyncCommand(depotClient, syncOptions.m_Files, revision, primarySyncFlags | DepotSyncFlags::Quiet); if (modifications.get() == nullptr) { return std::make_shared(DepotSyncStatus::Error); } + previewTime.Stop(); depotClient->Log(LogChannel::Info, StringInfo::Format("%I64u Modification message%s to act on.", uint64_t(modifications->size()), modifications->size() ? "s" : "")); + ThreadPool::ForEach::Execute( modifications->data(), modifications->size(), @@ -260,6 +263,7 @@ DepotOperations::SyncVirtual( depotClient->Log(LogChannel::Verbose, StringInfo::Format("Flush Time: %s", ToDisplayStringMilliseconds(flushTime).c_str())); depotClient->Log(LogChannel::Verbose, StringInfo::Format("Placeholder Time: %s", ToDisplayStringMilliseconds(placeholderTime).c_str())); depotClient->Log(LogChannel::Verbose, StringInfo::Format("Sync Time: %s", ToDisplayStringMilliseconds(syncTime).c_str())); + depotClient->Log(LogChannel::Verbose, StringInfo::Format("Preview Time: %s", ToDisplayStringMilliseconds(previewTime.TotalMilliseconds()).c_str())); return std::make_shared(status, resultModifications); } @@ -833,64 +837,6 @@ DepotOperations::SyncCommand( return nullptr; } - typedef HashSet DepotFileHashSet; - typedef Map DepotFileClientSizeMapType; - - DepotFileHashSet writeableHeadDepotFiles; - DepotFileHashSet writeableHaveDepotFiles; - DepotFileHashSet symlinkDepotFiles; - DepotFileHashSet diffDepotFiles; - DepotFileClientSizeMapType depotFileClientSizeMap; - - if ((syncFlags & (DepotSyncFlags::Preview | DepotSyncFlags::Flush)) != 0 && (syncFlags & DepotSyncFlags::IgnoreOutput) == 0) - { - DepotRevision haveRevision = (syncFlags & DepotSyncFlags::Force) ? FDepotRevision::New() : FDepotRevision::New(); - for (const DepotString& fileSpec : fileSpecs) - { - const DepotString haveFileSpec = CreateFileSpec(fileSpec, haveRevision, CreateFileSpecFlags::OverrideRevison); - if (haveFileSpec.empty()) - { - reportError(StringInfo::Format("Invalid haveFileSpec for fileSpec='%s' rev='%s'", fileSpec.c_str(), FDepotRevision::ToString(haveRevision).c_str())); - return nullptr; - } - - const DepotString& headFileSpec = fileSpec; - if (headFileSpec.empty()) - { - reportError(StringInfo::Format("Invalid headFileSpec for fileSpec='%s' rev='%s'", fileSpec.c_str(), FDepotRevision::ToString(revision).c_str())); - return nullptr; - } - - DepotResultDiff2 diff2 = Diff2(depotClient, haveFileSpec, headFileSpec); - for (size_t diff2NodeIndex = 0; diff2NodeIndex < diff2->NodeCount(); ++diff2NodeIndex) - { - FDepotResultDiff2Node node = diff2->Node(diff2NodeIndex); - diffDepotFiles.insert(node.DepotFile()); - diffDepotFiles.insert(node.DepotFile2()); - - if (DepotInfo::IsWritableFileType(node.Type())) - writeableHaveDepotFiles.insert(node.DepotFile()); - if (DepotInfo::IsWritableFileType(node.Type2())) - writeableHeadDepotFiles.insert(node.DepotFile2()); - - if (DepotInfo::IsSymlinkFileType(node.Type())) - symlinkDepotFiles.insert(node.DepotFile()); - if (DepotInfo::IsSymlinkFileType(node.Type2())) - symlinkDepotFiles.insert(node.DepotFile2()); - } - - if ((syncFlags & DepotSyncFlags::ClientSize) != 0 && depotClient->GetServerApiLevel() >= DepotProtocol::SERVER_SIZES_C) - { - DepotResultSizes sizes = Sizes(depotClient, headFileSpec, SizesFlags::ClientSize); - for (size_t sizesNodeIndex = 0; sizesNodeIndex < sizes->NodeCount(); ++sizesNodeIndex) - { - FDepotResultSizesNode node = sizes->Node(sizesNodeIndex); - depotFileClientSizeMap[node.DepotFile()] = node; - } - } - } - } - DepotClientLogCallback onClientLogCallback = std::make_shared([log](LogChannel::Enum channel, const char* severity, const char* text) -> void { if (log != nullptr) @@ -972,6 +918,55 @@ DepotOperations::SyncCommand( } } + typedef HashSet DepotFileHashSet; + typedef Map DepotFileClientSizeMapType; + + DepotFileHashSet writeableHeadDepotFiles; + DepotFileHashSet writeableHaveDepotFiles; + DepotFileHashSet symlinkDepotFiles; + DepotFileHashSet diffDepotFiles; + DepotFileClientSizeMapType depotFileClientSizeMap; + + if (syncFlags & (DepotSyncFlags::Preview | DepotSyncFlags::Flush)) + { + DepotResultDiff2 diff2 = Diff2Stat(depotClient, syncFlags, fileSpecs, modifications); + if (diff2.get() == nullptr || diff2->HasError()) + { + reportError(StringInfo::Format("Failed get filetype differences. %s", diff2.get() ? diff2->GetError().c_str() : "Invalid result")); + return nullptr; + } + + for (size_t diff2NodeIndex = 0; diff2NodeIndex < diff2->NodeCount(); ++diff2NodeIndex) + { + FDepotResultDiff2Node node = diff2->Node(diff2NodeIndex); + diffDepotFiles.insert(node.DepotFile()); + diffDepotFiles.insert(node.DepotFile2()); + + if (DepotInfo::IsWritableFileType(node.Type())) + writeableHaveDepotFiles.insert(node.DepotFile()); + if (DepotInfo::IsWritableFileType(node.Type2())) + writeableHeadDepotFiles.insert(node.DepotFile2()); + + if (DepotInfo::IsSymlinkFileType(node.Type())) + symlinkDepotFiles.insert(node.DepotFile()); + if (DepotInfo::IsSymlinkFileType(node.Type2())) + symlinkDepotFiles.insert(node.DepotFile2()); + } + + for (const DepotString& fileSpec : fileSpecs) + { + if ((syncFlags & DepotSyncFlags::ClientSize) != 0 && depotClient->GetServerApiLevel() >= DepotProtocol::SERVER_SIZES_C) + { + DepotResultSizes sizes = Sizes(depotClient, fileSpec, SizesFlags::ClientSize); + for (size_t sizesNodeIndex = 0; sizesNodeIndex < sizes->NodeCount(); ++sizesNodeIndex) + { + FDepotResultSizesNode node = sizes->Node(sizesNodeIndex); + depotFileClientSizeMap[node.DepotFile()] = node; + } + } + } + } + typedef Map OpenedHeadDepotFilesType; OpenedHeadDepotFilesType openedHeadDepotFiles; @@ -1159,6 +1154,125 @@ DepotOperations::CreateFileSpecs( return fileSpecs; } +DepotStringArray +DepotOperations::CreateFileSpecs( + DepotClient& depotClient, + const DepotSyncActionInfoArray& fileModifications, + const DepotRevision& revision, + CreateFileSpecFlags::Enum flags + ) +{ + DepotStringArray fileSpecs; + if (fileModifications.get()) + { + fileSpecs.reserve(fileModifications->size()); + for (const DepotSyncActionInfo& modification : *fileModifications) + { + if (modification.get()) + { + const DepotRevision& modificationRevision = flags & CreateFileSpecFlags::OverrideRevison ? revision : modification->m_Revision; + DepotString fileSpec = CreateFileSpec(modification->m_DepotFile, modificationRevision, CreateFileSpecFlags::None); + if (fileSpec.empty() == false) + { + fileSpecs.push_back(fileSpec); + } + } + } + } + return fileSpecs; +} + +DepotResultDiff2 +DepotOperations::Diff2Stat( + DepotClient& depotClient, + DepotSyncFlags::Enum syncFlags, + const DepotStringArray& fileSpecs, + const DepotSyncActionInfoArray& fileModifications + ) +{ + DepotResultDiff2 diff2stat = MakeResult(); + const DepotRevision haveRevision = (syncFlags & DepotSyncFlags::Force) ? FDepotRevision::New() : FDepotRevision::New(); + bool useFStat = false; + + if (fileModifications.get() && fileModifications->size() > 0) + { + const int32_t maxDiff2FileCount = SettingManager::StaticInstance().MaxDiff2StatFileCount.GetValue(); + if (maxDiff2FileCount < 0 || (maxDiff2FileCount > 0 && int32_t(fileModifications->size()) >= maxDiff2FileCount)) + { + useFStat = Algo::All(*fileModifications, [](const DepotSyncActionInfo& modification) -> bool { return IsFullDepotFileSpec(modification->m_DepotFile); }); + } + } + + if (useFStat) + { + DepotStringArray haveFileSpecs = CreateFileSpecs(depotClient, fileModifications, haveRevision, CreateFileSpecFlags::OverrideRevison); + DepotResultFStat haveFStat = FStat(depotClient, haveFileSpecs, "", FDepotResultFStatField::DepotFile | FDepotResultFStatField::HeadType | FDepotResultFStatField::HeadRev); + + DepotStringArray headFileSpecs = CreateFileSpecs(depotClient, fileModifications, nullptr, CreateFileSpecFlags::None); + DepotResultFStat headFStat = FStat(depotClient, headFileSpecs, "", FDepotResultFStatField::DepotFile | FDepotResultFStatField::HeadType | FDepotResultFStatField::HeadRev); + + typedef Map Diff2NodeMap; + Diff2NodeMap nodeMap; + + auto addNodes = [&nodeMap](const DepotResultFStat& fstat, const char* depotFileName, const char* revName, const char* typeName) -> void + { + for (size_t fstatNodeIndex = 0; fstatNodeIndex < fstat->NodeCount(); ++fstatNodeIndex) + { + const FDepotResultFStatNode& node = fstat->Node(fstatNodeIndex); + const DepotString& depotFile = node.DepotFile(); + DepotResultTag tag = nullptr; + + Diff2NodeMap::iterator tagIt = nodeMap.find(depotFile); + if (tagIt == nodeMap.end()) + { + tag = std::make_shared(); + nodeMap.insert(Diff2NodeMap::value_type(depotFile, tag)); + } + else + { + tag = tagIt->second; + } + + tag->SetValue(depotFileName, depotFile); + tag->SetValue(revName, node.GetTagValue(FDepotResultFStatField::Name::HeadRev)); + tag->SetValue(typeName, node.GetTagValue(FDepotResultFStatField::Name::HeadType)); + } + }; + + addNodes(haveFStat, FDepotResultDiff2Field::Name::DepotFile, FDepotResultDiff2Field::Name::Rev, FDepotResultDiff2Field::Name::Type); + addNodes(headFStat, FDepotResultDiff2Field::Name::DepotFile2, FDepotResultDiff2Field::Name::Rev2, FDepotResultDiff2Field::Name::Type2); + + for (Diff2NodeMap::value_type& node : nodeMap) + { + diff2stat->Append(node.second); + } + } + else + { + for (const DepotString& fileSpec : fileSpecs) + { + const DepotString haveFileSpec = CreateFileSpec(fileSpec, haveRevision, CreateFileSpecFlags::OverrideRevison); + if (haveFileSpec.empty()) + { + return MakeErrorResult(StringInfo::Format("Invalid haveFileSpec for fileSpec='%s' rev='%s'", fileSpec.c_str(), FDepotRevision::ToString(haveRevision).c_str())); + } + + const DepotString& headFileSpec = fileSpec; + if (headFileSpec.empty()) + { + return MakeErrorResult(StringInfo::Format("Invalid headFileSpec for fileSpec='%s'", fileSpec.c_str())); + } + + DepotResultDiff2 diff2 = Diff2(depotClient, haveFileSpec, headFileSpec); + if (diff2.get()) + { + diff2stat->Append(diff2->TagList()); + } + } + } + return diff2stat; +} + DepotResultDiff2 DepotOperations::Diff2( DepotClient& depotClient, @@ -1220,6 +1334,21 @@ DepotOperations::FStat( return depotClient->Run("fstat", fstatArgs); } +bool +DepotOperations::IsFullDepotFileSpec( + const DepotString& fileSpec + ) +{ + if (StringInfo::StartsWith(fileSpec.c_str(), "//")) + { + const char* path = fileSpec.c_str() + 2; + return StringInfo::IsNullOrWhiteSpace(path) == false && + StringInfo::Contains(path, '*') == false && + StringInfo::Contains(path, "...") == false; + } + return false; +} + bool DepotOperations::IsFileTypeAlwaysResident( const DepotString& syncResident, diff --git a/source/P4VFS.Core/Source/DepotResult.cpp b/source/P4VFS.Core/Source/DepotResult.cpp index 39fe4a5..80f7cb4 100644 --- a/source/P4VFS.Core/Source/DepotResult.cpp +++ b/source/P4VFS.Core/Source/DepotResult.cpp @@ -14,25 +14,25 @@ FDepotResultText::FDepotResultText(DepotResultChannel channel, const DepotString { } -bool FDepotResultTag::ContainsKey(const DepotString& tagKey) const +bool FDepotResultTag::ContainsKey(const char* tagKey) const { return GetValuePtr(tagKey) != nullptr; } -void FDepotResultTag::RemoveKey(const DepotString& tagKey) +void FDepotResultTag::RemoveKey(const char* tagKey) { m_Fields.erase(tagKey); } -void FDepotResultTag::SetValue(const DepotString& tagKey, const DepotString& tagValue) +void FDepotResultTag::SetValue(const char* tagKey, const DepotString& tagValue) { - if (tagKey.empty() == false) + if (StringInfo::IsNullOrEmpty(tagKey) == false) { m_Fields[tagKey] = tagValue; } } -bool FDepotResultTag::TryGetValue(const DepotString& tagKey, DepotString& tagValue) const +bool FDepotResultTag::TryGetValue(const char* tagKey, DepotString& tagValue) const { if (const DepotString* valuePtr = GetValuePtr(tagKey)) { @@ -42,15 +42,15 @@ bool FDepotResultTag::TryGetValue(const DepotString& tagKey, DepotString& tagVal return false; } -const DepotString& FDepotResultTag::GetValue(const DepotString& tagKey) const +const DepotString& FDepotResultTag::GetValue(const char* tagKey) const { const DepotString* valuePtr = GetValuePtr(tagKey); return valuePtr ? *valuePtr : StringInfo::EmptyA(); } -const DepotString* FDepotResultTag::GetValuePtr(const DepotString& tagKey) const +const DepotString* FDepotResultTag::GetValuePtr(const char* tagKey) const { - if (tagKey.empty() == false) + if (StringInfo::IsNullOrEmpty(tagKey) == false) { DepotResultFields::const_iterator v = m_Fields.find(tagKey); if (v != m_Fields.end()) @@ -172,9 +172,9 @@ const Array& FDepotResult::TextList() const return m_TextList; } -const DepotString& FDepotResult::GetTagValue(const DepotString& tagKey) const +const DepotString& FDepotResult::GetTagValue(const char* tagKey) const { - if (tagKey.empty() == false) + if (StringInfo::IsNullOrEmpty(tagKey) == false) { for (const DepotResultTag& tag : m_TagList) { @@ -185,4 +185,25 @@ const DepotString& FDepotResult::GetTagValue(const DepotString& tagKey) const return StringInfo::EmptyA(); } +void FDepotResult::Append(const FDepotResult& src) +{ + Append(src.m_TagList); + Append(src.m_TextList); +} + +void FDepotResult::Append(const Array& srcTagList) +{ + Algo::Append(m_TagList, srcTagList); +} + +void FDepotResult::Append(const Array& srcTextList) +{ + Algo::Append(m_TextList, srcTextList); +} + +void FDepotResult::Append(const DepotResultTag& srcTag) +{ + m_TagList.push_back(srcTag); +} + }}} diff --git a/source/P4VFS.Core/Source/DepotSyncAction.cpp b/source/P4VFS.Core/Source/DepotSyncAction.cpp index d05dbe9..cbb6e5f 100644 --- a/source/P4VFS.Core/Source/DepotSyncAction.cpp +++ b/source/P4VFS.Core/Source/DepotSyncAction.cpp @@ -24,8 +24,8 @@ DepotString DepotSyncFlags::ToString(DepotSyncFlags::Enum value) DepotString DepotFlushType::ToString(DepotFlushType::Enum value) { - P4VFS_ENUM_TO_STRING_RETURN(value, DepotFlushType, Single); - P4VFS_ENUM_TO_STRING_RETURN(value, DepotFlushType, Atomic); + P4VFS_ENUM_TO_STRING_RETURN(DepotString, value, DepotFlushType, Single); + P4VFS_ENUM_TO_STRING_RETURN(DepotString, value, DepotFlushType, Atomic); return DepotString(); } @@ -74,21 +74,21 @@ bool DepotSyncActionType::IsLocalChanged(DepotSyncActionType::Enum value) DepotString DepotSyncActionType::ToString(DepotSyncActionType::Enum value) { - P4VFS_ENUM_TO_STRING_RETURN(value, DepotSyncActionType, None); - P4VFS_ENUM_TO_STRING_RETURN(value, DepotSyncActionType, Added); - P4VFS_ENUM_TO_STRING_RETURN(value, DepotSyncActionType, Deleted); - P4VFS_ENUM_TO_STRING_RETURN(value, DepotSyncActionType, Updated); - P4VFS_ENUM_TO_STRING_RETURN(value, DepotSyncActionType, Refreshed); - P4VFS_ENUM_TO_STRING_RETURN(value, DepotSyncActionType, Replaced); - P4VFS_ENUM_TO_STRING_RETURN(value, DepotSyncActionType, UpToDate); - P4VFS_ENUM_TO_STRING_RETURN(value, DepotSyncActionType, NoFilesFound); - P4VFS_ENUM_TO_STRING_RETURN(value, DepotSyncActionType, NoFileAtRevision); - P4VFS_ENUM_TO_STRING_RETURN(value, DepotSyncActionType, InvalidPattern); - P4VFS_ENUM_TO_STRING_RETURN(value, DepotSyncActionType, NotInClientView); - P4VFS_ENUM_TO_STRING_RETURN(value, DepotSyncActionType, OpenedNotChanged); - P4VFS_ENUM_TO_STRING_RETURN(value, DepotSyncActionType, CantClobber); - P4VFS_ENUM_TO_STRING_RETURN(value, DepotSyncActionType, NeedsResolve); - P4VFS_ENUM_TO_STRING_RETURN(value, DepotSyncActionType, GenericError); + P4VFS_ENUM_TO_STRING_RETURN(DepotString, value, DepotSyncActionType, None); + P4VFS_ENUM_TO_STRING_RETURN(DepotString, value, DepotSyncActionType, Added); + P4VFS_ENUM_TO_STRING_RETURN(DepotString, value, DepotSyncActionType, Deleted); + P4VFS_ENUM_TO_STRING_RETURN(DepotString, value, DepotSyncActionType, Updated); + P4VFS_ENUM_TO_STRING_RETURN(DepotString, value, DepotSyncActionType, Refreshed); + P4VFS_ENUM_TO_STRING_RETURN(DepotString, value, DepotSyncActionType, Replaced); + P4VFS_ENUM_TO_STRING_RETURN(DepotString, value, DepotSyncActionType, UpToDate); + P4VFS_ENUM_TO_STRING_RETURN(DepotString, value, DepotSyncActionType, NoFilesFound); + P4VFS_ENUM_TO_STRING_RETURN(DepotString, value, DepotSyncActionType, NoFileAtRevision); + P4VFS_ENUM_TO_STRING_RETURN(DepotString, value, DepotSyncActionType, InvalidPattern); + P4VFS_ENUM_TO_STRING_RETURN(DepotString, value, DepotSyncActionType, NotInClientView); + P4VFS_ENUM_TO_STRING_RETURN(DepotString, value, DepotSyncActionType, OpenedNotChanged); + P4VFS_ENUM_TO_STRING_RETURN(DepotString, value, DepotSyncActionType, CantClobber); + P4VFS_ENUM_TO_STRING_RETURN(DepotString, value, DepotSyncActionType, NeedsResolve); + P4VFS_ENUM_TO_STRING_RETURN(DepotString, value, DepotSyncActionType, GenericError); return DepotString(); } diff --git a/source/P4VFS.Core/Source/DepotSyncOptions.cpp b/source/P4VFS.Core/Source/DepotSyncOptions.cpp index a79f07b..b5a4d54 100644 --- a/source/P4VFS.Core/Source/DepotSyncOptions.cpp +++ b/source/P4VFS.Core/Source/DepotSyncOptions.cpp @@ -9,8 +9,8 @@ namespace P4 { DepotString DepotSyncMethod::ToString(DepotSyncMethod::Enum value) { - P4VFS_ENUM_TO_STRING_RETURN(value, DepotSyncMethod, Regular); - P4VFS_ENUM_TO_STRING_RETURN(value, DepotSyncMethod, Virtual); + P4VFS_ENUM_TO_STRING_RETURN(DepotString, value, DepotSyncMethod, Regular); + P4VFS_ENUM_TO_STRING_RETURN(DepotString, value, DepotSyncMethod, Virtual); return DepotString(); } diff --git a/source/P4VFS.Core/Source/FileSystem.cpp b/source/P4VFS.Core/Source/FileSystem.cpp index d3783d6..ac563ff 100644 --- a/source/P4VFS.Core/Source/FileSystem.cpp +++ b/source/P4VFS.Core/Source/FileSystem.cpp @@ -336,9 +336,9 @@ FilePopulateMethod::ToString( FilePopulateMethod::Enum value ) { - P4VFS_ENUM_TO_STRING_RETURN(value, FilePopulateMethod, Copy); - P4VFS_ENUM_TO_STRING_RETURN(value, FilePopulateMethod, Move); - P4VFS_ENUM_TO_STRING_RETURN(value, FilePopulateMethod, Stream); + P4VFS_ENUM_TO_STRING_RETURN(AString, value, FilePopulateMethod, Copy); + P4VFS_ENUM_TO_STRING_RETURN(AString, value, FilePopulateMethod, Move); + P4VFS_ENUM_TO_STRING_RETURN(AString, value, FilePopulateMethod, Stream); return AString(); } diff --git a/source/P4VFS.Core/Source/LogDevice.cpp b/source/P4VFS.Core/Source/LogDevice.cpp index ed321da..a1d8d40 100644 --- a/source/P4VFS.Core/Source/LogDevice.cpp +++ b/source/P4VFS.Core/Source/LogDevice.cpp @@ -11,17 +11,17 @@ namespace Microsoft { namespace P4VFS { namespace FileCore { -AString LogChannel::ToString(Enum value) +String LogChannel::ToString(Enum value) { - P4VFS_ENUM_TO_STRING_RETURN(value, LogChannel, Verbose); - P4VFS_ENUM_TO_STRING_RETURN(value, LogChannel, Debug); - P4VFS_ENUM_TO_STRING_RETURN(value, LogChannel, Info); - P4VFS_ENUM_TO_STRING_RETURN(value, LogChannel, Warning); - P4VFS_ENUM_TO_STRING_RETURN(value, LogChannel, Error); - return AString(); + P4VFS_ENUM_TO_STRING_RETURN(String, value, LogChannel, Verbose); + P4VFS_ENUM_TO_STRING_RETURN(String, value, LogChannel, Debug); + P4VFS_ENUM_TO_STRING_RETURN(String, value, LogChannel, Info); + P4VFS_ENUM_TO_STRING_RETURN(String, value, LogChannel, Warning); + P4VFS_ENUM_TO_STRING_RETURN(String, value, LogChannel, Error); + return String(); } -LogChannel::Enum LogChannel::FromString(const AString& value) +LogChannel::Enum LogChannel::FromString(const String& value) { P4VFS_STRING_TO_ENUM_RETURN(value, LogChannel, Verbose); P4VFS_STRING_TO_ENUM_RETURN(value, LogChannel, Debug); @@ -129,7 +129,7 @@ void LogDeviceFile::WriteInternal(time_t time, LogChannel::Enum channel, const S { String line = StringInfo::Format(L"-%s::<%s> - %s\n", CSTR_ATOW(P4::DepotDateTime(time).ToDisplayString()), - CSTR_ATOW(LogChannel::ToString(channel)), + LogChannel::ToString(channel).c_str(), StringInfo::TrimRight(text.c_str(), L"\n").c_str()); if (m_RemoteFilePath.empty() == false && SettingManager::StaticInstance().RemoteLogging.GetValue()) @@ -440,7 +440,7 @@ void LogSystem::WriteLogElement(const LogElement& element) bool LogSystem::IsChannelEnabled(LogChannel::Enum channel) { - return channel >= LogChannel::FromString(StringInfo::ToAnsi(SettingManager::StaticInstance().Verbosity.GetValue())); + return channel >= LogChannel::FromString(SettingManager::StaticInstance().Verbosity.GetValue()); } DWORD LogSystem::WriteThreadEntry(void* data) diff --git a/source/P4VFS.Core/Source/SettingManager.cpp b/source/P4VFS.Core/Source/SettingManager.cpp index 993f022..f060e21 100644 --- a/source/P4VFS.Core/Source/SettingManager.cpp +++ b/source/P4VFS.Core/Source/SettingManager.cpp @@ -227,5 +227,9 @@ bool SettingManager::GetProperties(PropertyMap& propertyMap) const return true; } +#define SETTING_MANAGER_DECLARE_PROP(type, name, value) type SettingManager::Default::name() { return value; } +SETTING_MANAGER_PROPERTIES(SETTING_MANAGER_DECLARE_PROP) +#undef SETTING_MANAGER_DECLARE_PROP + }}} diff --git a/source/P4VFS.CoreInterop/Include/SettingManagerInterop.h b/source/P4VFS.CoreInterop/Include/SettingManagerInterop.h index 97bf5da..5d7956b 100644 --- a/source/P4VFS.CoreInterop/Include/SettingManagerInterop.h +++ b/source/P4VFS.CoreInterop/Include/SettingManagerInterop.h @@ -102,6 +102,14 @@ namespace SettingTraits { typedef System::Boolean ManagedType; + static ManagedType + ToManagedType( + bool value + ) + { + return value; + } + static ManagedType ToManagedType( SettingNode^ n, @@ -124,6 +132,14 @@ namespace SettingTraits { typedef System::Int32 ManagedType; + static ManagedType + ToManagedType( + int32_t value + ) + { + return value; + } + static ManagedType ToManagedType( SettingNode^ n, @@ -146,6 +162,14 @@ namespace SettingTraits { typedef System::String^ ManagedType; + static ManagedType + ToManagedType( + const wchar_t* value + ) + { + return Marshal::FromNativeWide(value); + } + static ManagedType ToManagedType( SettingNode^ n, @@ -209,6 +233,22 @@ public ref class SettingManager abstract sealed }; SETTING_MANAGER_PROPERTIES(SETTING_MANAGER_DECLARE_PROP) #undef SETTING_MANAGER_DECLARE_PROP + + ref class Default abstract sealed + { + public: + + #define SETTING_MANAGER_DECLARE_PROP(type, name, value) \ + static property SettingTraits::Value::ManagedType name \ + { \ + SettingTraits::Value::ManagedType get() \ + { \ + return SettingTraits::Value::ToManagedType(value); \ + } \ + }; + SETTING_MANAGER_PROPERTIES(SETTING_MANAGER_DECLARE_PROP) + #undef SETTING_MANAGER_DECLARE_PROP + }; }; }}} diff --git a/source/P4VFS.Driver/Include/DriverVersion.h b/source/P4VFS.Driver/Include/DriverVersion.h index 657a651..91f0f03 100644 --- a/source/P4VFS.Driver/Include/DriverVersion.h +++ b/source/P4VFS.Driver/Include/DriverVersion.h @@ -4,7 +4,7 @@ #define P4VFS_VER_MAJOR 1 // Increment this number almost never #define P4VFS_VER_MINOR 29 // Increment this number whenever the driver changes -#define P4VFS_VER_BUILD 3 // Increment this number when a major user mode change has been made +#define P4VFS_VER_BUILD 4 // Increment this number when a major user mode change has been made #define P4VFS_VER_REVISION 0 // Increment this number when we rebuild with any change #define P4VFS_VER_STRINGIZE_EX(v) L#v diff --git a/source/P4VFS.UnitTest/Source/UnitTestBase.cs b/source/P4VFS.UnitTest/Source/UnitTestBase.cs index 922c33d..8f7cb35 100644 --- a/source/P4VFS.UnitTest/Source/UnitTestBase.cs +++ b/source/P4VFS.UnitTest/Source/UnitTestBase.cs @@ -233,21 +233,6 @@ public static void WaitForAttachedDebugger() System.Threading.Thread.Sleep(100); } - public string ExcludedProcessNames - { - get - { - return String.Join(";", new[] - { - "MsSense.exe", - "MsMpEng.exe", - "SenseCE.exe", - "SenseIR.exe", - "SearchProtocolHost.exe", - }); - } - } - public void WorkspaceReset(string port, string client, string user) { WorkspaceReset(new DepotConfig{ Port=port??_P4Port, Client=client??_P4Client, User=user??_P4User }); @@ -278,9 +263,13 @@ public void WorkspaceReset(DepotConfig config = null) AssertRetry(() => VirtualFileSystem.IsDriverLoaded(), message:"IsDriverLoaded"); AssertRetry(() => VirtualFileSystem.IsDriverReady(), message:"IsDriverReady"); AssertRetry(() => VirtualFileSystem.IsVirtualFileSystemAvailable(), message:"IsVirtualFileSystemAvailable"); - ServiceSettings.Reset(); - InteractiveOverridePasswd = null; + + if (ServiceSettingScope.IsOverriding == false) + { + ServiceSettings.Reset(); + } + InteractiveOverridePasswd = null; if (String.IsNullOrEmpty(config.Passwd)) { config.Passwd = UnitTestServer.GetUserP4Passwd(config.User); @@ -342,7 +331,7 @@ public void WorkspaceReset(DepotConfig config = null) Extensions.SocketModel.SocketModelClient service = new Extensions.SocketModel.SocketModelClient(); Assert(service.GarbageCollect()); - Assert(service.GetServiceSetting(nameof(SettingManager.ExcludedProcessNames)).ToString() == ExcludedProcessNames); + Assert(service.GetServiceSetting(nameof(SettingManager.ExcludedProcessNames)).ToString() == SettingManager.Default.ExcludedProcessNames); } public void ServiceRestart() @@ -647,32 +636,44 @@ public IEnumerable EnumerateCommonPrimaryDepotSyncFlags(bool ver foreach (DepotSyncFlags primarySyncFlags in new[]{ DepotSyncFlags.Normal, DepotSyncFlags.Quiet, DepotSyncFlags.IgnoreOutput }) { if (verbose) + { VirtualFileSystemLog.Info("EnumerateCommonPrimaryDepotSyncFlags: {0}", primarySyncFlags); + } yield return primarySyncFlags; } } - public IEnumerable EnumerateCommonServicePopulateSettings(bool verbose = true) + public IEnumerable EnumerateCommonServiceSettings(bool verbose = true) { - foreach (FilePopulateMethod populateMethod in new[]{ FilePopulateMethod.Copy, FilePopulateMethod.Stream }) + ServiceSettingsScope[] items = ServiceSettingsScope.Combined(Enumerable.Empty>() + .Concat( + new[]{ FilePopulateMethod.Copy, FilePopulateMethod.Stream } + .Select(v => new KeyValuePair(nameof(SettingManager.PopulateMethod), v.ToString()))) + .Concat( + new[]{ -1, 0, 10, SettingManager.Default.MaxDiff2StatFileCount } + .Select(v => new KeyValuePair(nameof(SettingManager.MaxDiff2StatFileCount), v.ToString())))); + + foreach (ServiceSettingsScope item in items) { - ServiceSettingsScope item = new ServiceSettingsScope(); - item.ServiceSettings.Add(new ServiceSettingScope("PopulateMethod", populateMethod.ToString())); if (verbose) + { VirtualFileSystemLog.Info("EnumerateCommonServicePopulateSettings: {0}", item); + } yield return item; } } public IEnumerable EnumerateCommonServiceSyncSettings(bool verbose = true) { - foreach (ServiceSettingsScope item in EnumerateCommonServicePopulateSettings(false)) + foreach (ServiceSettingsScope item in EnumerateCommonServiceSettings(false)) { foreach (DepotSyncFlags primarySyncFlags in EnumerateCommonPrimaryDepotSyncFlags(false)) { item.SyncFlags = primarySyncFlags; if (verbose) + { VirtualFileSystemLog.Info("EnumerateCommonServiceSyncSettings: {0}", item); + } yield return item; } } @@ -686,7 +687,9 @@ public IEnumerable EnumerateCommonServiceSyncLineEndSettin { item.LineEnd = lineEnd; if (verbose) + { VirtualFileSystemLog.Info("EnumerateCommonServiceSyncLineEndSettings: {0}", item); + } yield return item; } } @@ -812,41 +815,27 @@ public struct FileDifferenceSummary public uint LinesTotal { get { return LinesAdded + LinesDeleted + LinesChanged; } } } - public class LocalSettingScope : IDisposable + public class DisposableAction : IDisposable { - public string Name { get; private set; } - public string Value { get; private set; } - public SettingNode PreviousValue { get; private set; } + private Action m_Action; - public LocalSettingScope(string name, string value) + public DisposableAction(Action action) { - Name = name; - UnitTestBase.Assert(String.IsNullOrEmpty(Name) == false); - Value = value; - UnitTestBase.Assert(Value != null); - - PreviousValue = ServiceSettings.GetProperty(Name); - UnitTestBase.Assert(PreviousValue.ToString() != null); - UnitTestBase.Assert(ServiceSettings.SetProperty(SettingNode.FromString(Value), Name)); - UnitTestBase.Assert(ServiceSettings.GetProperty(Name).ToString() == Value); + m_Action = action; } public void Dispose() { - UnitTestBase.Assert(ServiceSettings.SetProperty(PreviousValue, Name)); - } - - public override string ToString() - { - return String.Format("{0}={1}", Name, Value); + UnitTestBase.Assert(m_Action != null); + m_Action.Invoke(); + m_Action = null; } } - public class ServiceSettingScope : IDisposable + public class ServiceSettingScope { public string Name { get; private set; } public string Value { get; private set; } - public SettingNode PreviousValue { get; private set; } public ServiceSettingScope(string name, string value) { @@ -854,22 +843,39 @@ public ServiceSettingScope(string name, string value) UnitTestBase.Assert(String.IsNullOrEmpty(Name) == false); Value = value; UnitTestBase.Assert(Value != null); - - Extensions.SocketModel.SocketModelClient serviceClient = new Extensions.SocketModel.SocketModelClient(); - PreviousValue = serviceClient.GetServiceSetting(Name); - UnitTestBase.Assert(PreviousValue.ToString() != null); - UnitTestBase.Assert(serviceClient.SetServiceSetting(Name, SettingNode.FromString(Value))); - UnitTestBase.Assert(serviceClient.GetServiceSetting(Name).ToString() == Value); } public ServiceSettingScope(string name) : this(name, ServiceSettings.GetProperty(name).ToString()) { } - public void Dispose() + public DisposableAction CreateDisposable() { + UnitTestBase.Assert(System.Threading.Interlocked.Increment(ref m_Depth) > 0); + Extensions.SocketModel.SocketModelClient serviceClient = new Extensions.SocketModel.SocketModelClient(); - UnitTestBase.Assert(serviceClient.SetServiceSetting(Name, PreviousValue)); + SettingNode PreviousServiceValue = serviceClient.GetServiceSetting(Name); + UnitTestBase.Assert(PreviousServiceValue.ToString() != null); + UnitTestBase.Assert(serviceClient.SetServiceSetting(Name, SettingNode.FromString(Value))); + UnitTestBase.Assert(serviceClient.GetServiceSetting(Name).ToString() == Value); + + SettingNode PreviousClientValue = ServiceSettings.GetProperty(Name); + UnitTestBase.Assert(PreviousClientValue.ToString() != null); + UnitTestBase.Assert(ServiceSettings.SetProperty(SettingNode.FromString(Value), Name)); + UnitTestBase.Assert(ServiceSettings.GetProperty(Name).ToString() == Value); + + return new DisposableAction(() => + { + UnitTestBase.Assert(serviceClient.GetServiceSetting(Name).ToString() == Value, $"Service setting {Name} expected to be overriden to {Value}"); + UnitTestBase.Assert(serviceClient.SetServiceSetting(Name, PreviousServiceValue)); + UnitTestBase.Assert(serviceClient.GetServiceSetting(Name).ToString() == PreviousServiceValue.ToString()); + + UnitTestBase.Assert(ServiceSettings.GetProperty(Name).ToString() == Value, $"Client setting {Name} expected to be overriden to {Value}"); + UnitTestBase.Assert(ServiceSettings.SetProperty(PreviousClientValue, Name)); + UnitTestBase.Assert(ServiceSettings.GetProperty(Name).ToString() == PreviousClientValue.ToString()); + + UnitTestBase.Assert(System.Threading.Interlocked.Decrement(ref m_Depth) >= 0); + }); } public override string ToString() @@ -877,40 +883,68 @@ public override string ToString() return String.Format("{0}={1}", Name, Value); } - public void ApplyGlobal() + private static volatile int m_Depth; + public static bool IsOverriding { - UnitTestBase.Assert(ServiceSettings.SetProperty(new SettingNode(Value), Name)); - UnitTestBase.Assert(ServiceSettings.GetPropertyJson(Name).ToString() == ServiceSettings.SettingNodeToJson(new SettingNode(Value)).ToString()); + get { return m_Depth > 0; } } } - public class ServiceSettingsScope : IDisposable + public class ServiceSettingsScope { public List ServiceSettings = new List(); public DepotSyncFlags? SyncFlags; public string LineEnd; - public void Dispose() + public static ServiceSettingsScope[] Combined(IEnumerable> keyValues) { - foreach (ServiceSettingScope setting in ServiceSettings) - setting.Dispose(); + KeyValuePair[] groups = keyValues + .GroupBy(selector => selector.Key) + .Select(grouping => new KeyValuePair(grouping.Key, grouping.Select(kvp => kvp.Value).Distinct().ToArray())) + .ToArray(); + + return CreateCombinations(groups, 0).ToArray(); + } + + private static IEnumerable CreateCombinations(KeyValuePair[] groups, int index) + { + if (index >= groups.Length) + { + yield return new ServiceSettingsScope(); + yield break; + } + + foreach (string value in groups[index].Value) + { + foreach (ServiceSettingsScope combined in CreateCombinations(groups, index + 1)) + { + combined.ServiceSettings.Add(new ServiceSettingScope(groups[index].Key, value)); + yield return combined; + } + } + } + + public DisposableAction CreateDisposable() + { + List actions = ServiceSettings + .Select(scope => scope.CreateDisposable()) + .ToList(); + + return new DisposableAction(() => + { + actions.ForEach(action => action.Dispose()); + }); } public override string ToString() { - string result = String.Format("ServiceSettings=[{0}]", String.Join(",", ServiceSettings)); + string result = String.Format("ServiceSettings={{{0}}}", String.Join(", ", ServiceSettings)); if (SyncFlags.HasValue) - result += String.Format(", SyncFlags=[{0}]", SyncFlags.Value); + result += String.Format(", SyncFlags={0}", SyncFlags.Value); if (LineEnd != null) result += String.Format(", LineEnd={0}", LineEnd); return result; } - - public void ApplyGlobal() - { - foreach (ServiceSettingScope setting in ServiceSettings) - setting.ApplyGlobal(); - } } public class DepotTunableScope : IDisposable diff --git a/source/P4VFS.UnitTest/Source/UnitTestCommon.cs b/source/P4VFS.UnitTest/Source/UnitTestCommon.cs index 5c90ba3..381acbf 100644 --- a/source/P4VFS.UnitTest/Source/UnitTestCommon.cs +++ b/source/P4VFS.UnitTest/Source/UnitTestCommon.cs @@ -25,7 +25,7 @@ public void BasicWorkspaceTest() { foreach (ServiceSettingsScope settings in EnumerateCommonServiceSyncSettings()) { - using (settings) { + using (settings.CreateDisposable()) { using (DepotClient depotClient = new DepotClient()) { DepotSyncFlags syncFlags = settings.SyncFlags.Value; @@ -92,7 +92,7 @@ public void MakeFileResidentTest() { foreach (ServiceSettingsScope settings in EnumerateCommonServiceSyncSettings()) { - using (settings) { + using (settings.CreateDisposable()) { using (DepotClient depotClient = new DepotClient()) { DepotSyncFlags syncFlags = settings.SyncFlags.Value; @@ -148,7 +148,7 @@ public void MakeDirectoryResidentTest() { foreach (ServiceSettingsScope settings in EnumerateCommonServiceSyncSettings()) { - using (settings) { + using (settings.CreateDisposable()) { using (DepotClient depotClient = new DepotClient()) { DepotSyncFlags syncFlags = settings.SyncFlags.Value; @@ -191,7 +191,7 @@ public void WritableClientAndFileTest() { foreach (ServiceSettingsScope settings in EnumerateCommonServiceSyncLineEndSettings()) { - using (settings) { + using (settings.CreateDisposable()) { using (DepotClient depotClient = new DepotClient()) { DepotSyncFlags syncFlags = settings.SyncFlags.Value; @@ -269,7 +269,7 @@ public void OpenedFileSyncTest() { foreach (ServiceSettingsScope settings in EnumerateCommonServiceSyncSettings()) { - using (settings) { + using (settings.CreateDisposable()) { using (DepotClient depotClient = new DepotClient()) { DepotSyncFlags syncFlags = settings.SyncFlags.Value; @@ -539,7 +539,7 @@ public void MakeResidentRaceTest() { foreach (ServiceSettingsScope settings in EnumerateCommonServiceSyncSettings()) { - using (settings) { + using (settings.CreateDisposable()) { using (DepotClient depotClient = new DepotClient()) { WorkspaceReset(); @@ -872,7 +872,7 @@ public void DepotClientSymlinkSyncTest() { foreach (ServiceSettingsScope settings in EnumerateCommonServiceSyncLineEndSettings().Where(s => s.SyncFlags == DepotSyncFlags.Normal)) { - using (settings) { + using (settings.CreateDisposable()) { using (DepotClient depotClient = new DepotClient()) { WorkspaceReset(); @@ -1040,6 +1040,12 @@ public void DepotServerConfigTest() ServiceSettings.Reset(); AssertCurrentServiceSettings(); + // Simple test of expected values of SettingManager.Default + Assert(SettingManager.ExcludedProcessNames == SettingManager.Default.ExcludedProcessNames); + Assert(SettingManager.ExcludedProcessNames == "MsSense.exe;MsMpEng.exe;SenseCE.exe;SenseIR.exe;SearchProtocolHost.exe;MpDlpService.exe"); + Assert(SettingManager.PopulateMethod == SettingManager.Default.PopulateMethod); + Assert(SettingManager.PopulateMethod == "Stream"); + // Test DepotServerConfig serialization DepotServerConfig config0 = new DepotServerConfig{ Servers = new[]{ new DepotServerConfigEntry{ Pattern = "MyHost*", Address = "127.0.0.1:1666" }}}; SettingNode configNode = config0.ToNode(); @@ -1077,16 +1083,15 @@ public void DepotServerConfigTest() Assert(DepotOperations.ResolveDepotServerName("CTO-P4MAIN") == "CTO-P4MAIN"); // Test the service DepotServerConfig pattern matching of the host name resolving - foreach (ServiceSettingsScope settings in EnumerateCommonServicePopulateSettings()) + foreach (ServiceSettingsScope settings in EnumerateCommonServiceSettings()) { - using (settings) { + using (settings.CreateDisposable()) { using (DepotClient depotClient = new DepotClient()) { WorkspaceReset(); Assert(depotClient.Connect(_P4Port, _P4Client, _P4User)); string clientRoot = GetClientRoot(depotClient); - settings.ApplyGlobal(); SettingManagerExtensions.DepotServerConfig = depotServerConfig; Assert(JsonConvert.SerializeObject(SettingManagerExtensions.DepotServerConfig) == JsonConvert.SerializeObject(depotServerConfig)); AssertCurrentServiceSettings(); @@ -1206,9 +1211,9 @@ public void DepotServerConfigTest() [TestMethod, Priority(21), TestRemote] public void FileCopyTest() { - foreach (ServiceSettingsScope settings in EnumerateCommonServicePopulateSettings()) + foreach (ServiceSettingsScope settings in EnumerateCommonServiceSettings()) { - using (settings) { + using (settings.CreateDisposable()) { using (DepotClient depotClient = new DepotClient()) { WorkspaceReset(); @@ -1303,9 +1308,9 @@ public void ServiceSettingNodeTest() [TestMethod, Priority(23), TestRemote] public void SparseUnderflowCopyTest() { - foreach (ServiceSettingsScope settings in EnumerateCommonServicePopulateSettings()) + foreach (ServiceSettingsScope settings in EnumerateCommonServiceSettings()) { - using (settings) { + using (settings.CreateDisposable()) { using (DepotClient depotClient = new DepotClient()) { WorkspaceReset(); @@ -1676,7 +1681,7 @@ public void LongPathSyncTest() { foreach (ServiceSettingsScope settings in EnumerateCommonServiceSyncSettings()) { - using (settings) { + using (settings.CreateDisposable()) { using (DepotClient depotClient = new DepotClient()) { DepotSyncFlags syncFlags = settings.SyncFlags.Value; @@ -1876,8 +1881,8 @@ public void DepotClientCacheIdleTimeoutTest() Assert(GetServiceIdleConnectionCount() == 1); // Write a temporary PublicSettingsFilePath settings file with very short GC timeout - using (new LocalSettingScope(nameof(SettingManager.GarbageCollectPeriodMs), "100")) { - using (new LocalSettingScope(nameof(SettingManager.DepotClientCacheIdleTimeoutMs), "5000")) { + using (new ServiceSettingScope(nameof(SettingManager.GarbageCollectPeriodMs), "100").CreateDisposable()) { + using (new ServiceSettingScope(nameof(SettingManager.DepotClientCacheIdleTimeoutMs), "5000").CreateDisposable()) { using (new DepotTempFile(VirtualFileSystem.PublicSettingsFilePath)) { Assert(ServiceSettings.SaveToFile(VirtualFileSystem.PublicSettingsFilePath)); @@ -2006,7 +2011,7 @@ public void NonAsciiFilePathTest() foreach (ServiceSettingsScope settings in EnumerateCommonServiceSyncSettings()) { - using (settings) { + using (settings.CreateDisposable()) { using (DepotClient depotClient = new DepotClient()) { DepotSyncFlags syncFlags = settings.SyncFlags.Value; @@ -2035,7 +2040,7 @@ public void SyncClientSizeTest() settings.SyncFlags = settings.SyncFlags.Value | DepotSyncFlags.ClientSize; VirtualFileSystemLog.Info("EnumerateCommonServiceSyncLineEndSettings: {0}", settings); - using (settings) { + using (settings.CreateDisposable()) { using (DepotClient depotClient = new DepotClient()) { WorkspaceReset(); diff --git a/source/P4VFS.UnitTest/Source/UnitTestUtilities.cs b/source/P4VFS.UnitTest/Source/UnitTestUtilities.cs index ed2101c..4b02a31 100644 --- a/source/P4VFS.UnitTest/Source/UnitTestUtilities.cs +++ b/source/P4VFS.UnitTest/Source/UnitTestUtilities.cs @@ -20,9 +20,9 @@ public class UnitTestUtilities : UnitTestBase [TestMethod, Priority(0)] public void FileAttributeTest() { - foreach (ServiceSettingsScope settings in EnumerateCommonServicePopulateSettings()) + foreach (ServiceSettingsScope settings in EnumerateCommonServiceSettings()) { - using (settings) { + using (settings.CreateDisposable()) { WorkspaceReset(); string clientRoot = GetClientRoot(); diff --git a/source/P4VFS.UnitTest/Source/UnitTestWorkflow.cs b/source/P4VFS.UnitTest/Source/UnitTestWorkflow.cs index f3c70b7..ede692e 100644 --- a/source/P4VFS.UnitTest/Source/UnitTestWorkflow.cs +++ b/source/P4VFS.UnitTest/Source/UnitTestWorkflow.cs @@ -21,9 +21,9 @@ public class UnitTestWorkflow : UnitTestBase [TestMethod, Priority(0)] public void ReconciledTest() { - foreach (ServiceSettingsScope settings in EnumerateCommonServicePopulateSettings()) + foreach (ServiceSettingsScope settings in EnumerateCommonServiceSettings()) { - using (settings) { + using (settings.CreateDisposable()) { WorkspaceReset(); string[] fileSpecs = new string[]{ @@ -52,9 +52,9 @@ public void ReconciledTest() [TestMethod, Priority(1)] public void CurrentWorkingDirectoryTest() { - foreach (ServiceSettingsScope settings in EnumerateCommonServicePopulateSettings()) + foreach (ServiceSettingsScope settings in EnumerateCommonServiceSettings()) { - using (settings) { + using (settings.CreateDisposable()) { foreach (string syncOption in EnumerateCommonConsoleSyncOptions()) { WorkspaceReset(); @@ -165,9 +165,9 @@ public void ServiceInstallUninstallTest() [TestMethod, Priority(5), TestRemote] public void ResidentCommandTest() { - foreach (ServiceSettingsScope settings in EnumerateCommonServicePopulateSettings()) + foreach (ServiceSettingsScope settings in EnumerateCommonServiceSettings()) { - using (settings) { + using (settings.CreateDisposable()) { WorkspaceReset(); string clientRoot = GetClientRoot(); diff --git a/source/P4VFS.props b/source/P4VFS.props index 661097d..791de78 100644 --- a/source/P4VFS.props +++ b/source/P4VFS.props @@ -65,7 +65,7 @@ $(P4VFSBuildDir)/$(P4VFSUnitTestName) - 2024.1 + 2025.2 $(P4VFSConfiguration) $(P4VFSExternalDir)/P4API/$(PerforceApiVersion) $(PerforceApiDir)/include @@ -73,7 +73,7 @@ libclient.lib;libp4api.lib;libp4script.lib;libp4script_c.lib;libp4script_curl.lib;libp4script_sqlite.lib;librpc.lib;libsupp.lib - 3.3.3 + 3.0.18 $(P4VFSConfiguration) $(P4VFSExternalDir)/OpenSSL/$(OpenSSLApiVersion) $(OpenSSLApiDir)/include