public void Dispose() { LocalResourceUtils.DeleteDirectoryTree( Path.Combine( SettingsUtility.GetPluginsCacheFolder(), CachingUtility.RemoveInvalidFileNameChars(CachingUtility.ComputeHash(_pluginFilePath))), new List <string>()); PluginManager.Dispose(); _testDirectory.Dispose(); _reader.Verify(); _pluginDiscoverer.Verify(); foreach (var expectation in _expectations) { _connection.Verify(x => x.SendRequestAndReceiveResponseAsync <GetOperationClaimsRequest, GetOperationClaimsResponse>( It.Is <MessageMethod>(m => m == MessageMethod.GetOperationClaims), It.Is <GetOperationClaimsRequest>( g => g.PackageSourceRepository == expectation.SourceRepository.PackageSource.Source), It.IsAny <CancellationToken>()), Times.Once()); var expectedSetCredentialsRequestCalls = expectation.OperationClaims.Any() ? Times.Once() : Times.Never(); _connection.Verify(x => x.SendRequestAndReceiveResponseAsync <SetCredentialsRequest, SetCredentialsResponse>( It.Is <MessageMethod>(m => m == MessageMethod.SetCredentials), It.Is <SetCredentialsRequest>( g => g.PackageSourceRepository == expectation.SourceRepository.PackageSource.Source), It.IsAny <CancellationToken>()), expectedSetCredentialsRequestCalls); } _plugin.Verify(); _factory.Verify(); }
/// <summary> /// Loads and processes the contet from the generated file if it exists. /// Even after this method is invoked, the operation claims might be null. /// </summary> public void LoadFromFile() { Stream content = null; try { content = CachingUtility.ReadCacheFile(MaxAge, CacheFileName); if (content != null) { ProcessContent(content); } } finally { content?.Dispose(); } }
public void Dispose() { LocalResourceUtils.DeleteDirectoryTree( Path.Combine( SettingsUtility.GetPluginsCacheFolder(), CachingUtility.RemoveInvalidFileNameChars(CachingUtility.ComputeHash(_pluginFilePath))), new List <string>()); PluginManager.Dispose(); _reader.Verify(); _pluginDiscoverer.Verify(); if (_expectations.PluginLaunched) { _connection.Verify(x => x.SendRequestAndReceiveResponseAsync <GetOperationClaimsRequest, GetOperationClaimsResponse>( It.Is <MessageMethod>(m => m == MessageMethod.GetOperationClaims), It.Is <GetOperationClaimsRequest>( g => g.PackageSourceRepository == null), // The source repository should be null in the context of credential plugins It.IsAny <CancellationToken>()), Times.Once()); if (_expectations.Success) { _connection.Verify(x => x.SendRequestAndReceiveResponseAsync <GetAuthenticationCredentialsRequest, GetAuthenticationCredentialsResponse>( It.Is <MessageMethod>(m => m == MessageMethod.GetAuthenticationCredentials), It.IsAny <GetAuthenticationCredentialsRequest>(), It.IsAny <CancellationToken>()), Times.Once()); } if (_expectations.ProxyUsername != null && _expectations.ProxyPassword != null) { _connection.Verify(x => x.SendRequestAndReceiveResponseAsync <SetCredentialsRequest, SetCredentialsResponse>( It.Is <MessageMethod>(m => m == MessageMethod.SetCredentials), It.Is <SetCredentialsRequest>(e => e.PackageSourceRepository.Equals(_expectations.Uri.AbsolutePath) && e.Password == null && e.Username == null && e.ProxyPassword.Equals(_expectations.ProxyPassword) && e.ProxyUsername.Equals(_expectations.ProxyUsername)), It.IsAny <CancellationToken>()), Times.Once()); } } _connection.Verify(); _plugin.Verify(); _factory.Verify(); _testDirectory.Dispose(); }
/// <summary> /// Updates the cache file with the current value in the operation claims if the operationn claims is not null. /// </summary> /// <returns>Task</returns> public async Task UpdateCacheFileAsync() { if (OperationClaims != null) { // Make sure the cache file directory is created before writing a file to it. DirectoryUtility.CreateSharedDirectory(RootFolder); // The update of a cached file is divided into two steps: // 1) Delete the old file. // 2) Create a new file with the same name. using (var fileStream = new FileStream( NewCacheFileName, FileMode.Create, FileAccess.ReadWrite, FileShare.None, CachingUtility.BufferSize, useAsync: true)) { var json = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(OperationClaims, Formatting.Indented)); await fileStream.WriteAsync(json, 0, json.Length).ConfigureAwait(false); } if (File.Exists(CacheFileName)) { // Process B can perform deletion on an opened file if the file is opened by process A // with FileShare.Delete flag. However, the file won't be actually deleted until A close it. // This special feature can cause race condition, so we never delete an opened file. if (!CachingUtility.IsFileAlreadyOpen(CacheFileName)) { File.Delete(CacheFileName); } } // If the destination file doesn't exist, we can safely perform moving operation. // Otherwise, moving operation will fail. if (!File.Exists(CacheFileName)) { File.Move( NewCacheFileName, CacheFileName); } } }
public async Task PluginCacheEntry_DoesNotDeleteAnOpenedFile() { var list = new List <OperationClaim>() { OperationClaim.Authentication }; using (var testDirectory = TestDirectory.Create()) { var entry = new PluginCacheEntry(testDirectory.Path, "a", "b"); entry.LoadFromFile(); entry.OperationClaims = list; await entry.UpdateCacheFileAsync(); var CacheFileName = Path.Combine( Path.Combine(testDirectory.Path, CachingUtility.RemoveInvalidFileNameChars(CachingUtility.ComputeHash("a", false))), CachingUtility.RemoveInvalidFileNameChars(CachingUtility.ComputeHash("b", false)) + ".dat"); Assert.True(File.Exists(CacheFileName)); using (var fileStream = new FileStream( CacheFileName, FileMode.Open, FileAccess.ReadWrite, FileShare.None, CachingUtility.BufferSize, useAsync: true)) { list.Add(OperationClaim.DownloadPackage); entry.OperationClaims = list; await entry.UpdateCacheFileAsync(); // this should not update } entry.LoadFromFile(); Assert.True(EqualityUtility.SequenceEqualWithNullCheck(entry.OperationClaims, new List <OperationClaim>() { OperationClaim.Authentication })); } }
/// <summary> /// Create a plugin cache entry. /// </summary> /// <param name="rootCacheFolder">The root cache folder, normally /localappdata/nuget/plugins-cache</param> /// <param name="pluginFilePath">The full plugin file path, which will be used to create a key for the folder created in the root folder itself </param> /// <param name="requestKey">A unique request key for the operation claims. Ideally the packageSourceRepository value of the PluginRequestKey. Example https://protected.package.feed/index.json, or Source-Agnostic</param> public PluginCacheEntry(string rootCacheFolder, string pluginFilePath, string requestKey) { RootFolder = Path.Combine(rootCacheFolder, CachingUtility.RemoveInvalidFileNameChars(CachingUtility.ComputeHash(pluginFilePath, addIdentifiableCharacters: false))); CacheFileName = Path.Combine(RootFolder, CachingUtility.RemoveInvalidFileNameChars(CachingUtility.ComputeHash(requestKey, addIdentifiableCharacters: false)) + ".dat"); NewCacheFileName = CacheFileName + "-new"; }