public void GetContent_CachesRemoteSourcesInFiles() { // Arrange // Arbitrary permalink const string url = "https://raw.githubusercontent.com/JeringTech/Markdig.Extensions.FlexiBlocks/6998b1c27821d8393ad39beb54f782515c39d98b/test/FlexiBlocks.Tests/exampleInclude.md"; var dummyUri = new Uri(url); IContentRetrieverService testSubject = _serviceProvider.GetRequiredService <IContentRetrieverService>(); ReadOnlyCollection <string> expectedResult = testSubject.GetContent(dummyUri, _fixture.TempDirectory); ((IDisposable)_serviceProvider).Dispose(); // Avoid retrieving from in-memory cache Mock <IHttpClientService> mockHttpClientService = _mockRepository.Create <IHttpClientService>(); var services = new ServiceCollection(); services.AddFlexiBlocks(); services.AddSingleton(mockHttpClientService.Object); _serviceProvider = services.BuildServiceProvider(); testSubject = _serviceProvider.GetRequiredService <IContentRetrieverService>(); // Act ReadOnlyCollection <string> result = testSubject.GetContent(dummyUri, _fixture.TempDirectory); // Assert Assert.Equal(expectedResult, result); mockHttpClientService.Verify(h => h.GetAsync(It.IsAny <Uri>(), HttpCompletionOption.ResponseHeadersRead, default), Times.Never); }
public void GetContent_CachesSourcesInMemory() { // Arrange const string dummySource = "this\nis\na\ndummy\nsource"; var dummyUri = new Uri(_dummyFile); using (FileStream fileStream = File.Open(_dummyFile, FileMode.Create, FileAccess.Write, FileShare.None)) { byte[] bytes = Encoding.UTF8.GetBytes(dummySource); fileStream.Write(bytes, 0, bytes.Length); } IContentRetrieverService testSubject = _serviceProvider.GetRequiredService <IContentRetrieverService>(); // Act var threads = new List <Thread>(); var results = new ConcurrentBag <ReadOnlyCollection <string> >(); for (int i = 0; i < 3; i++) { var thread = new Thread(() => results.Add(testSubject.GetContent(dummyUri))); thread.Start(); threads.Add(thread); } foreach (Thread thread in threads) { thread.Join(); } // Assert ReadOnlyCollection <string>[] resultsArr = results.ToArray(); Assert.Equal(3, resultsArr.Length); Assert.Equal(dummySource.Split('\n'), resultsArr[0]); Assert.Same(resultsArr[0], resultsArr[1]); // Result should get cached after first call Assert.Same(resultsArr[1], resultsArr[2]); // The same relation is transitive, so we do not need to check if the item at index 0 is the same as the item at index 2 }
internal virtual void ProcessFlexiIncludeBlock(FlexiIncludeBlock flexiIncludeBlock, ProxyJsonBlock proxyJsonBlock, BlockProcessor blockProcessor) { // If the FlexiIncludeBlock's type is markdown, we need to ensure we don't have a cycle of includes. bool isMarkdown = flexiIncludeBlock.Type == FlexiIncludeType.Markdown; Stack <FlexiIncludeBlock> closingFlexiIncludeBlocks = null; if (isMarkdown) { closingFlexiIncludeBlocks = GetOrCreateClosingFlexiIncludeBlocks(blockProcessor); CheckForCycle(closingFlexiIncludeBlocks, flexiIncludeBlock); closingFlexiIncludeBlocks.Push(flexiIncludeBlock); } // Retrieve source ReadOnlyCollection <string> content = _contentRetrieverService.GetContent(flexiIncludeBlock.Source, flexiIncludeBlock.CacheDirectory); // Parent of new blocks ContainerBlock parentOfNewBlocks = proxyJsonBlock.Parent; parentOfNewBlocks.Remove(proxyJsonBlock); try { // Convert content into blocks and add them to parentOfNewBlocks ProcessContent(blockProcessor, flexiIncludeBlock, parentOfNewBlocks, content); } catch (Exception exception) { // FlexiIncludeBlocks can reference sources other than the entry markdown document. If an exception is thrown while processing content from such sources, // we must provide context. Specifically, we must specify which source and where within the source the unrecoverable situation was encountered. throw new BlockException(flexiIncludeBlock, string.Format(Strings.BlockException_FlexiIncludeBlockFactory_ExceptionOccurredWhileProcessingContent, flexiIncludeBlock.Source.AbsoluteUri), exception); } // Remove FlexiIncludeBlock from stack used to check for cycles if (isMarkdown) { closingFlexiIncludeBlocks.Pop(); } // Add to trees if FlexiIncludeBlock is a root block TryAddToFlexiIncludeBlockTrees(flexiIncludeBlock, blockProcessor); }