From af14969b540fe5ff16ede8e6812a0d05a24dab5e Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Fri, 17 Jan 2014 16:15:21 -0800 Subject: [PATCH] Added tests project. --- Razor.sln | 21 + .../Properties/AssemblyInfo.cs | 2 + .../CSharpRazorCodeLanguageTest.cs | 55 + .../CodeCompileUnitExtensions.cs | 24 + .../Editor/RazorEditorParserTest.cs | 193 ++ .../Framework/BlockExtensions.cs | 29 + .../Framework/BlockTypes.cs | 235 ++ .../Framework/CodeParserTestBase.cs | 76 + .../Framework/CsHtmlCodeParserTestBase.cs | 30 + .../Framework/CsHtmlMarkupParserTestBase.cs | 30 + .../Framework/ErrorCollector.cs | 57 + .../Framework/MarkupParserTestBase.cs | 21 + .../Framework/ParserTestBase.cs | 426 ++++ .../Framework/RawTextSymbol.cs | 74 + .../Framework/TestSpanBuilder.cs | 408 ++++ .../Framework/VBHtmlCodeParserTestBase.cs | 30 + .../Framework/VBHtmlMarkupParserTestBase.cs | 30 + .../Generator/CSharpRazorCodeGeneratorTest.cs | 417 ++++ .../Generator/GeneratedCodeMappingTest.cs | 65 + .../Generator/PaddingTest.cs | 236 ++ .../Generator/RazorCodeGeneratorTest.cs | 245 ++ .../Generator/TabTest.cs | 13 + .../Generator/TestSpan.cs | 59 + .../Generator/VBRazorCodeGeneratorTest.cs | 281 +++ .../Microsoft.AspNet.Razor.Test.csproj | 468 ++++ .../Parser/BlockTest.cs | 124 + .../Parser/CSharp/CSharpAutoCompleteTest.cs | 175 ++ .../Parser/CSharp/CSharpBlockTest.cs | 733 ++++++ .../Parser/CSharp/CSharpDirectivesTest.cs | 163 ++ .../Parser/CSharp/CSharpErrorTest.cs | 624 +++++ .../CSharp/CSharpExplicitExpressionTest.cs | 142 ++ .../Parser/CSharp/CSharpHelperTest.cs | 347 +++ .../CSharp/CSharpImplicitExpressionTest.cs | 204 ++ .../CSharp/CSharpLayoutDirectiveTest.cs | 109 + .../CSharp/CSharpNestedStatementsTest.cs | 102 + .../Parser/CSharp/CSharpRazorCommentsTest.cs | 174 ++ .../Parser/CSharp/CSharpReservedWordsTest.cs | 44 + .../Parser/CSharp/CSharpSectionTest.cs | 405 ++++ .../Parser/CSharp/CSharpSpecialBlockTest.cs | 198 ++ .../Parser/CSharp/CSharpStatementTest.cs | 210 ++ .../Parser/CSharp/CSharpTemplateTest.cs | 261 +++ .../Parser/CSharp/CSharpToMarkupSwitchTest.cs | 494 ++++ .../Parser/CSharp/CSharpVerbatimBlockTest.cs | 121 + .../CSharp/CSharpWhitespaceHandlingTest.cs | 33 + .../Parser/CSharp/CsHtmlDocumentTest.cs | 289 +++ .../Parser/CallbackParserListenerTest.cs | 165 ++ .../Parser/Html/HtmlAttributeTest.cs | 303 +++ .../Parser/Html/HtmlBlockTest.cs | 393 ++++ .../Parser/Html/HtmlDocumentTest.cs | 239 ++ .../Parser/Html/HtmlErrorTest.cs | 90 + .../Parser/Html/HtmlParserTestUtils.cs | 40 + .../Parser/Html/HtmlTagsTest.cs | 152 ++ .../Parser/Html/HtmlToCodeSwitchTest.cs | 353 +++ .../Parser/Html/HtmlUrlAttributeTest.cs | 273 +++ .../Parser/ParserContextTest.cs | 242 ++ .../Parser/ParserVisitorExtensionsTest.cs | 83 + .../CSharpPartialParsingTest.cs | 638 ++++++ .../PartialParsing/PartialParsingTestBase.cs | 114 + .../PartialParsing/VBPartialParsingTest.cs | 375 +++ .../Parser/RazorParserTest.cs | 136 ++ .../Parser/VB/VBAutoCompleteTest.cs | 156 ++ .../Parser/VB/VBBlockTest.cs | 367 +++ .../Parser/VB/VBContinueStatementTest.cs | 59 + .../Parser/VB/VBDirectiveTest.cs | 128 ++ .../Parser/VB/VBErrorTest.cs | 174 ++ .../Parser/VB/VBExitStatementTest.cs | 97 + .../Parser/VB/VBExplicitExpressionTest.cs | 22 + .../Parser/VB/VBExpressionTest.cs | 111 + .../Parser/VB/VBExpressionsInCodeTest.cs | 121 + .../Parser/VB/VBHelperTest.cs | 324 +++ .../Parser/VB/VBHtmlDocumentTest.cs | 251 +++ .../Parser/VB/VBImplicitExpressionTest.cs | 79 + .../Parser/VB/VBLayoutDirectiveTest.cs | 87 + .../Parser/VB/VBNestedStatementsTest.cs | 170 ++ .../Parser/VB/VBRazorCommentsTest.cs | 175 ++ .../Parser/VB/VBReservedWordsTest.cs | 31 + .../Parser/VB/VBSectionTest.cs | 277 +++ .../Parser/VB/VBSpecialKeywordsTest.cs | 172 ++ .../Parser/VB/VBStatementTest.cs | 221 ++ .../Parser/VB/VBTemplateTest.cs | 214 ++ .../Parser/VB/VBToMarkupSwitchTest.cs | 105 + .../Parser/WhitespaceRewriterTest.cs | 51 + .../RazorCodeLanguageTest.cs | 49 + .../RazorDirectiveAttributeTest.cs | 81 + .../RazorEngineHostTest.cs | 187 ++ .../RazorTemplateEngineTest.cs | 198 ++ .../StringTextBuffer.cs | 52 + .../CodeGenerator/CS/Output/Blocks.cs | 194 ++ .../CodeGenerator/CS/Output/CodeBlock.cs | 31 + .../CodeGenerator/CS/Output/CodeBlockAtEOF.cs | 27 + .../CodeGenerator/CS/Output/Comments.cs | 38 + .../CS/Output/ConditionalAttributes.cs | 197 ++ .../CS/Output/DesignTime.Tabs.cs | 110 + .../CodeGenerator/CS/Output/DesignTime.cs | 110 + .../CodeGenerator/CS/Output/EmptyCodeBlock.cs | 27 + .../CS/Output/EmptyExplicitExpression.cs | 29 + .../CS/Output/EmptyImplicitExpression.cs | 29 + .../EmptyImplicitExpressionInCode.Tabs.cs | 43 + .../Output/EmptyImplicitExpressionInCode.cs | 43 + .../CS/Output/ExplicitExpression.cs | 30 + .../CS/Output/ExplicitExpressionAtEOF.cs | 29 + .../CS/Output/ExpressionsInCode.cs | 89 + .../Output/FunctionsBlock.DesignTime.Tabs.cs | 46 + .../CS/Output/FunctionsBlock.DesignTime.cs | 46 + .../CodeGenerator/CS/Output/FunctionsBlock.cs | 49 + .../FunctionsBlockMinimal.DesignTime.Tabs.cs | 30 + .../CS/Output/FunctionsBlock_Tabs.cs | 49 + .../CS/Output/Helpers.Instance.cs | 106 + .../CodeGenerator/CS/Output/Helpers.cs | 106 + .../CS/Output/HelpersMissingCloseParen.cs | 66 + .../CS/Output/HelpersMissingName.cs | 21 + .../CS/Output/HelpersMissingOpenBrace.cs | 72 + .../CS/Output/HelpersMissingOpenParen.cs | 72 + .../CS/Output/HiddenSpansInCode.cs | 35 + .../CS/Output/HtmlCommentWithQuote_Double.cs | 30 + .../CS/Output/HtmlCommentWithQuote_Single.cs | 30 + .../CS/Output/ImplicitExpression.cs | 45 + .../CS/Output/ImplicitExpressionAtEOF.cs | 29 + .../CS/Output/Imports.DesignTime.cs | 53 + .../CodeGenerator/CS/Output/Imports.cs | 58 + .../CS/Output/Inherits.Designtime.cs | 40 + .../CS/Output/Inherits.Runtime.cs | 30 + .../CodeGenerator/CS/Output/InlineBlocks.cs | 87 + .../CodeGenerator/CS/Output/Instrumented.cs | 359 +++ .../CS/Output/LayoutDirective.cs | 22 + .../CS/Output/MarkupInCodeBlock.cs | 49 + .../CS/Output/NestedCodeBlocks.cs | 42 + .../CodeGenerator/CS/Output/NestedHelpers.cs | 111 + .../CodeGenerator/CS/Output/NoLinePragmas.cs | 102 + .../CS/Output/OpenedIf.DesignTime.Tabs.cs | 41 + .../CS/Output/OpenedIf.DesignTime.cs | 41 + .../CodeGenerator/CS/Output/ParserError.cs | 31 + .../CS/Output/RazorComments.DesignTime.cs | 72 + .../CodeGenerator/CS/Output/RazorComments.cs | 83 + .../CodeGenerator/CS/Output/ResolveUrl.cs | 230 ++ .../CodeGenerator/CS/Output/Sections.cs | 45 + .../SimpleUnspacedIf.DesignTime.Tabs.cs | 36 + .../CodeGenerator/CS/Output/Templates.cs | 180 ++ .../Output/UnfinishedExpressionInCode.Tabs.cs | 43 + .../CS/Output/UnfinishedExpressionInCode.cs | 43 + .../CodeGenerator/CS/Source/Blocks.cshtml | 37 + .../CodeGenerator/CS/Source/CodeBlock.cshtml | 5 + .../CS/Source/CodeBlockAtEOF.cshtml | 1 + .../CS/Source/ConditionalAttributes.cshtml | 15 + .../CodeGenerator/CS/Source/DesignTime.cshtml | 21 + .../CS/Source/EmptyCodeBlock.cshtml | 3 + .../CS/Source/EmptyExplicitExpression.cshtml | 3 + .../CS/Source/EmptyImplicitExpression.cshtml | 3 + .../EmptyImplicitExpressionInCode.cshtml | 3 + .../CS/Source/ExplicitExpression.cshtml | 1 + .../CS/Source/ExplicitExpressionAtEOF.cshtml | 3 + .../CS/Source/ExpressionsInCode.cshtml | 16 + .../CS/Source/FunctionsBlock.cshtml | 12 + .../CS/Source/FunctionsBlockMinimal.cshtml | 7 + .../CS/Source/FunctionsBlock_Tabs.cshtml | 12 + .../CodeGenerator/CS/Source/Helpers.cshtml | 11 + .../CS/Source/HelpersMissingCloseParen.cshtml | 7 + .../CS/Source/HelpersMissingName.cshtml | 1 + .../CS/Source/HelpersMissingOpenBrace.cshtml | 7 + .../CS/Source/HelpersMissingOpenParen.cshtml | 7 + .../CS/Source/HiddenSpansInCode.cshtml | 3 + .../Source/HtmlCommentWithQuote_Double.cshtml | 2 + .../Source/HtmlCommentWithQuote_Single.cshtml | 2 + .../CS/Source/ImplicitExpression.cshtml | 3 + .../CS/Source/ImplicitExpressionAtEOF.cshtml | 3 + .../CodeGenerator/CS/Source/Imports.cshtml | 6 + .../CodeGenerator/CS/Source/Inherits.cshtml | 3 + .../CS/Source/InlineBlocks.cshtml | 3 + .../CS/Source/Instrumented.cshtml | 42 + .../CS/Source/LayoutDirective.cshtml | 1 + .../CS/Source/MarkupInCodeBlock.cshtml | 5 + .../CS/Source/NestedCodeBlocks.cshtml | 4 + .../CS/Source/NestedHelpers.cshtml | 10 + .../CS/Source/NoLinePragmas.cshtml | 38 + .../CodeGenerator/CS/Source/OpenedIf.cshtml | 5 + .../CS/Source/ParserError.cshtml | 5 + .../CS/Source/RazorComments.cshtml | 15 + .../CodeGenerator/CS/Source/ResolveUrl.cshtml | 20 + .../CodeGenerator/CS/Source/Sections.cshtml | 13 + .../CS/Source/SimpleUnspacedIf.cshtml | 4 + .../CodeGenerator/CS/Source/Templates.cshtml | 35 + .../Source/UnfinishedExpressionInCode.cshtml | 3 + .../CodeGenerator/VB/Output/Blocks.vb | 255 +++ .../CodeGenerator/VB/Output/CodeBlock.vb | 31 + .../CodeGenerator/VB/Output/CodeBlockAtEOF.vb | 29 + .../CodeGenerator/VB/Output/Comments.vb | 51 + .../VB/Output/ConditionalAttributes.vb | 187 ++ .../CodeGenerator/VB/Output/DesignTime.vb | 101 + .../VB/Output/EmptyExplicitExpression.vb | 31 + .../VB/Output/EmptyImplicitExpression.vb | 31 + .../Output/EmptyImplicitExpressionInCode.vb | 43 + .../CodeGenerator/VB/Output/EmptySection.vb | 30 + .../VB/Output/ExplicitExpression.vb | 30 + .../VB/Output/ExplicitExpressionAtEOF.vb | 31 + .../VB/Output/ExpressionsInCode.vb | 86 + .../Output/FunctionsBlock.DesignTime.Tabs.vb | 47 + .../VB/Output/FunctionsBlock.DesignTime.vb | 47 + .../CodeGenerator/VB/Output/FunctionsBlock.vb | 50 + .../VB/Output/Helpers.Instance.vb | 99 + .../CodeGenerator/VB/Output/Helpers.vb | 99 + .../VB/Output/HelpersMissingCloseParen.vb | 66 + .../VB/Output/HelpersMissingName.vb | 24 + .../VB/Output/HelpersMissingOpenParen.vb | 72 + .../VB/Output/ImplicitExpression.vb | 56 + .../VB/Output/ImplicitExpressionAtEOF.vb | 31 + .../VB/Output/Imports.DesignTime.vb | 41 + .../CodeGenerator/VB/Output/Imports.vb | 46 + .../VB/Output/Inherits.Designtime.vb | 35 + .../VB/Output/Inherits.Runtime.vb | 25 + .../CodeGenerator/VB/Output/Instrumented.vb | 511 +++++ .../VB/Output/LayoutDirective.vb | 25 + .../VB/Output/MarkupInCodeBlock.vb | 51 + .../VB/Output/NestedCodeBlocks.vb | 42 + .../CodeGenerator/VB/Output/NestedHelpers.vb | 103 + .../CodeGenerator/VB/Output/NoLinePragmas.vb | 143 ++ .../CodeGenerator/VB/Output/Options.vb | 28 + .../CodeGenerator/VB/Output/ParserError.vb | 31 + .../VB/Output/RazorComments.DesignTime.vb | 59 + .../CodeGenerator/VB/Output/RazorComments.vb | 72 + .../CodeGenerator/VB/Output/ResolveUrl.vb | 183 ++ .../CodeGenerator/VB/Output/Sections.vb | 47 + .../CodeGenerator/VB/Output/Templates.vb | 169 ++ .../VB/Output/UnfinishedExpressionInCode.vb | 43 + .../CodeGenerator/VB/Source/Blocks.vbhtml | 46 + .../CodeGenerator/VB/Source/CodeBlock.vbhtml | 3 + .../VB/Source/CodeBlockAtEOF.vbhtml | 3 + .../VB/Source/ConditionalAttributes.vbhtml | 16 + .../CodeGenerator/VB/Source/DesignTime.vbhtml | 21 + .../VB/Source/EmptyExplicitExpression.vbhtml | 3 + .../VB/Source/EmptyImplicitExpression.vbhtml | 3 + .../EmptyImplicitExpressionInCode.vbhtml | 3 + .../VB/Source/EmptySection.vbhtml | 1 + .../VB/Source/ExplicitExpression.vbhtml | 1 + .../VB/Source/ExplicitExpressionAtEOF.vbhtml | 3 + .../VB/Source/ExpressionsInCode.vbhtml | 16 + .../VB/Source/FunctionsBlock.vbhtml | 12 + .../CodeGenerator/VB/Source/Helpers.vbhtml | 11 + .../VB/Source/HelpersMissingCloseParen.vbhtml | 8 + .../VB/Source/HelpersMissingName.vbhtml | 1 + .../VB/Source/HelpersMissingOpenParen.vbhtml | 8 + .../VB/Source/ImplicitExpression.vbhtml | 5 + .../VB/Source/ImplicitExpressionAtEOF.vbhtml | 3 + .../CodeGenerator/VB/Source/Imports.vbhtml | 6 + .../CodeGenerator/VB/Source/Inherits.vbhtml | 1 + .../VB/Source/Instrumented.vbhtml | 53 + .../VB/Source/LayoutDirective.vbhtml | 1 + .../VB/Source/MarkupInCodeBlock.vbhtml | 5 + .../VB/Source/NestedCodeBlocks.vbhtml | 4 + .../VB/Source/NestedHelpers.vbhtml | 10 + .../VB/Source/NoLinePragmas.vbhtml | 45 + .../CodeGenerator/VB/Source/Options.vbhtml | 4 + .../VB/Source/ParserError.vbhtml | 3 + .../VB/Source/RazorComments.vbhtml | 12 + .../CodeGenerator/VB/Source/ResolveUrl.vbhtml | 20 + .../CodeGenerator/VB/Source/Sections.vbhtml | 13 + .../CodeGenerator/VB/Source/Templates.vbhtml | 36 + .../Source/UnfinishedExpressionInCode.vbhtml | 3 + .../TestFiles/DesignTime/Simple.cshtml | 16 + .../TestFiles/DesignTime/Simple.txt | 60 + .../TestFiles/nested-1000.html | 2002 +++++++++++++++++ .../Text/BufferingTextReaderTest.cs | 267 +++ .../Text/LineTrackingStringBufferTest.cs | 27 + .../Text/LookaheadTextReaderTestBase.cs | 255 +++ .../Text/SourceLocationTest.cs | 22 + .../Text/SourceLocationTrackerTest.cs | 196 ++ .../Text/TextBufferReaderTest.cs | 231 ++ .../Text/TextChangeTest.cs | 322 +++ .../Text/TextReaderExtensionsTest.cs | 186 ++ .../Tokenizer/CSharpTokenizerCommentTest.cs | 89 + .../CSharpTokenizerIdentifierTest.cs | 169 ++ .../Tokenizer/CSharpTokenizerLiteralTest.cs | 284 +++ .../Tokenizer/CSharpTokenizerOperatorsTest.cs | 296 +++ .../Tokenizer/CSharpTokenizerTest.cs | 103 + .../Tokenizer/CSharpTokenizerTestBase.cs | 29 + .../Tokenizer/HtmlTokenizerTest.cs | 167 ++ .../Tokenizer/HtmlTokenizerTestBase.cs | 29 + .../Tokenizer/TokenizerLookaheadTest.cs | 41 + .../Tokenizer/TokenizerTestBase.cs | 77 + .../Tokenizer/VBTokenizerCommentTest.cs | 93 + .../Tokenizer/VBTokenizerIdentifierTest.cs | 267 +++ .../Tokenizer/VBTokenizerLiteralTest.cs | 182 ++ .../Tokenizer/VBTokenizerOperatorsTest.cs | 154 ++ .../Tokenizer/VBTokenizerTest.cs | 95 + .../Tokenizer/VBTokenizerTestBase.cs | 29 + .../Utils/DisposableActionTest.cs | 30 + .../Utils/EnumerableUtils.cs | 20 + .../Utils/MiscAssert.cs | 34 + .../Utils/MiscUtils.cs | 36 + .../Utils/SpanAssert.cs | 59 + .../VBRazorCodeLanguageTest.cs | 55 + .../packages.config | 6 + test/Microsoft.TestCommon/Assert.cs | 30 + test/Microsoft.TestCommon/CultureReplacer.cs | 72 + test/Microsoft.TestCommon/DataAttribute.cs | 11 + .../DictionaryEqualityComparer.cs | 49 + .../EnumHelperTestBase.cs | 101 + .../ExceptionAssertions.cs | 521 +++++ test/Microsoft.TestCommon/FactAttribute.cs | 60 + test/Microsoft.TestCommon/ForceGCAttribute.cs | 17 + .../InlineDataAttribute.cs | 15 + .../MatrixTheoryDataSet.cs | 25 + test/Microsoft.TestCommon/MemberHelper.cs | 383 ++++ .../Microsoft.TestCommon.csproj | 104 + .../DataSets/CommonUnitTestDataSets.cs | 40 + .../TestCommon/DataSets/RefTypeTestData.cs | 75 + .../Microsoft/TestCommon/DataSets/TestData.cs | 463 ++++ .../TestCommon/DataSets/TestDataHolder.cs | 49 + .../TestCommon/DataSets/TestDataVariations.cs | 115 + .../TestCommon/DataSets/ValueTypeTestData.cs | 39 + .../Microsoft/TestCommon/GenericTypeAssert.cs | 490 ++++ .../Microsoft/TestCommon/HttpAssert.cs | 256 +++ .../Microsoft/TestCommon/MediaTypeAssert.cs | 54 + .../MediaTypeHeaderValueComparer.cs | 90 + .../TestCommon/ParsedMediaTypeHeaderValue.cs | 111 + .../Microsoft/TestCommon/RegexReplacement.cs | 40 + .../TestCommon/RuntimeEnvironment.cs | 33 + .../Microsoft/TestCommon/SerializerAssert.cs | 152 ++ .../Microsoft/TestCommon/StreamAssert.cs | 96 + .../Microsoft/TestCommon/TaskAssert.cs | 100 + .../TestCommon/TestDataSetAttribute.cs | 203 ++ .../Microsoft/TestCommon/TimeoutConstant.cs | 22 + .../Microsoft/TestCommon/TypeAssert.cs | 163 ++ .../Microsoft/TestCommon/Types/ByteEnum.cs | 11 + .../Microsoft/TestCommon/Types/FlagsEnum.cs | 14 + .../TestCommon/Types/INameAndIdContainer.cs | 14 + .../TestCommon/Types/ISerializableType.cs | 75 + .../Microsoft/TestCommon/Types/LongEnum.cs | 12 + .../Microsoft/TestCommon/Types/SByteEnum.cs | 11 + .../Microsoft/TestCommon/Types/ShortEnum.cs | 11 + .../Microsoft/TestCommon/Types/SimpleEnum.cs | 12 + .../Microsoft/TestCommon/Types/UIntEnum.cs | 11 + .../Microsoft/TestCommon/Types/UShortEnum.cs | 11 + .../Microsoft/TestCommon/XmlAssert.cs | 78 + test/Microsoft.TestCommon/Platform.cs | 36 + test/Microsoft.TestCommon/PlatformInfo.cs | 33 + test/Microsoft.TestCommon/PortReserver.cs | 135 ++ .../PreAppStartTestHelper.cs | 27 + .../PreserveSyncContextAttribute.cs | 25 + .../PropertyDataAttribute.cs | 15 + test/Microsoft.TestCommon/ReflectionAssert.cs | 221 ++ .../ReplaceCultureAttribute.cs | 55 + .../RestoreThreadPrincipalAttribute.cs | 30 + .../StringAssertException.cs | 117 + test/Microsoft.TestCommon/StringAssertions.cs | 135 ++ test/Microsoft.TestCommon/TaskExtensions.cs | 35 + test/Microsoft.TestCommon/TestFile.cs | 77 + test/Microsoft.TestCommon/TestHelper.cs | 31 + test/Microsoft.TestCommon/TheoryAttribute.cs | 57 + test/Microsoft.TestCommon/TheoryDataSet.cs | 105 + .../ThreadPoolSyncContext.cs | 33 + test/Microsoft.TestCommon/TraitAttribute.cs | 15 + .../Microsoft.TestCommon/VersionTestHelper.cs | 20 + test/Microsoft.TestCommon/WebUtils.cs | 89 + test/Microsoft.TestCommon/packages.config | 5 + 354 files changed, 35115 insertions(+) create mode 100644 test/Microsoft.AspNet.Razor.Test/CSharpRazorCodeLanguageTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/CodeCompileUnitExtensions.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Editor/RazorEditorParserTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Framework/BlockExtensions.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Framework/BlockTypes.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Framework/CodeParserTestBase.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Framework/CsHtmlCodeParserTestBase.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Framework/CsHtmlMarkupParserTestBase.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Framework/ErrorCollector.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Framework/MarkupParserTestBase.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Framework/ParserTestBase.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Framework/RawTextSymbol.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Framework/TestSpanBuilder.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Framework/VBHtmlCodeParserTestBase.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Framework/VBHtmlMarkupParserTestBase.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Generator/CSharpRazorCodeGeneratorTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Generator/GeneratedCodeMappingTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Generator/PaddingTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Generator/RazorCodeGeneratorTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Generator/TabTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Generator/TestSpan.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Generator/VBRazorCodeGeneratorTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Microsoft.AspNet.Razor.Test.csproj create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/BlockTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpAutoCompleteTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpBlockTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpDirectivesTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpErrorTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpExplicitExpressionTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpHelperTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpImplicitExpressionTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpLayoutDirectiveTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpNestedStatementsTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpRazorCommentsTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpReservedWordsTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpSectionTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpSpecialBlockTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpStatementTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpTemplateTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpToMarkupSwitchTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpVerbatimBlockTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpWhitespaceHandlingTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CsHtmlDocumentTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/CallbackParserListenerTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlAttributeTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlBlockTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlDocumentTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlErrorTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlParserTestUtils.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlTagsTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlToCodeSwitchTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlUrlAttributeTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/ParserContextTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/ParserVisitorExtensionsTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/PartialParsing/CSharpPartialParsingTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/PartialParsing/PartialParsingTestBase.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/PartialParsing/VBPartialParsingTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/RazorParserTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/VB/VBAutoCompleteTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/VB/VBBlockTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/VB/VBContinueStatementTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/VB/VBDirectiveTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/VB/VBErrorTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/VB/VBExitStatementTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/VB/VBExplicitExpressionTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/VB/VBExpressionTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/VB/VBExpressionsInCodeTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/VB/VBHelperTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/VB/VBHtmlDocumentTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/VB/VBImplicitExpressionTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/VB/VBLayoutDirectiveTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/VB/VBNestedStatementsTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/VB/VBRazorCommentsTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/VB/VBReservedWordsTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/VB/VBSectionTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/VB/VBSpecialKeywordsTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/VB/VBStatementTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/VB/VBTemplateTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/VB/VBToMarkupSwitchTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Parser/WhitespaceRewriterTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/RazorCodeLanguageTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/RazorDirectiveAttributeTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/RazorEngineHostTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/RazorTemplateEngineTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/StringTextBuffer.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Blocks.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/CodeBlock.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/CodeBlockAtEOF.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Comments.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ConditionalAttributes.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/DesignTime.Tabs.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/DesignTime.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/EmptyCodeBlock.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/EmptyExplicitExpression.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/EmptyImplicitExpression.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/EmptyImplicitExpressionInCode.Tabs.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/EmptyImplicitExpressionInCode.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ExplicitExpression.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ExplicitExpressionAtEOF.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ExpressionsInCode.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/FunctionsBlock.DesignTime.Tabs.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/FunctionsBlock.DesignTime.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/FunctionsBlock.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/FunctionsBlockMinimal.DesignTime.Tabs.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/FunctionsBlock_Tabs.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Helpers.Instance.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Helpers.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HelpersMissingCloseParen.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HelpersMissingName.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HelpersMissingOpenBrace.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HelpersMissingOpenParen.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HiddenSpansInCode.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HtmlCommentWithQuote_Double.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HtmlCommentWithQuote_Single.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ImplicitExpression.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ImplicitExpressionAtEOF.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Imports.DesignTime.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Imports.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Inherits.Designtime.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Inherits.Runtime.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/InlineBlocks.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Instrumented.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/LayoutDirective.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/MarkupInCodeBlock.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/NestedCodeBlocks.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/NestedHelpers.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/NoLinePragmas.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/OpenedIf.DesignTime.Tabs.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/OpenedIf.DesignTime.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ParserError.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/RazorComments.DesignTime.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/RazorComments.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ResolveUrl.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Sections.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/SimpleUnspacedIf.DesignTime.Tabs.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Templates.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/UnfinishedExpressionInCode.Tabs.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/UnfinishedExpressionInCode.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Blocks.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/CodeBlock.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/CodeBlockAtEOF.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ConditionalAttributes.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/DesignTime.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/EmptyCodeBlock.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/EmptyExplicitExpression.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/EmptyImplicitExpression.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/EmptyImplicitExpressionInCode.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ExplicitExpression.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ExplicitExpressionAtEOF.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ExpressionsInCode.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/FunctionsBlock.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/FunctionsBlockMinimal.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/FunctionsBlock_Tabs.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Helpers.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HelpersMissingCloseParen.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HelpersMissingName.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HelpersMissingOpenBrace.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HelpersMissingOpenParen.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HiddenSpansInCode.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HtmlCommentWithQuote_Double.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HtmlCommentWithQuote_Single.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ImplicitExpression.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ImplicitExpressionAtEOF.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Imports.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Inherits.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/InlineBlocks.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Instrumented.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/LayoutDirective.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/MarkupInCodeBlock.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/NestedCodeBlocks.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/NestedHelpers.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/NoLinePragmas.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/OpenedIf.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ParserError.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/RazorComments.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ResolveUrl.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Sections.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/SimpleUnspacedIf.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Templates.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/UnfinishedExpressionInCode.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Blocks.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/CodeBlock.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/CodeBlockAtEOF.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Comments.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ConditionalAttributes.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/DesignTime.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/EmptyExplicitExpression.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/EmptyImplicitExpression.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/EmptyImplicitExpressionInCode.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/EmptySection.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ExplicitExpression.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ExplicitExpressionAtEOF.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ExpressionsInCode.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/FunctionsBlock.DesignTime.Tabs.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/FunctionsBlock.DesignTime.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/FunctionsBlock.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Helpers.Instance.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Helpers.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/HelpersMissingCloseParen.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/HelpersMissingName.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/HelpersMissingOpenParen.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ImplicitExpression.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ImplicitExpressionAtEOF.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Imports.DesignTime.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Imports.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Inherits.Designtime.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Inherits.Runtime.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Instrumented.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/LayoutDirective.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/MarkupInCodeBlock.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/NestedCodeBlocks.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/NestedHelpers.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/NoLinePragmas.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Options.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ParserError.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/RazorComments.DesignTime.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/RazorComments.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ResolveUrl.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Sections.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Templates.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/UnfinishedExpressionInCode.vb create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Blocks.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/CodeBlock.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/CodeBlockAtEOF.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ConditionalAttributes.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/DesignTime.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/EmptyExplicitExpression.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/EmptyImplicitExpression.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/EmptyImplicitExpressionInCode.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/EmptySection.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ExplicitExpression.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ExplicitExpressionAtEOF.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ExpressionsInCode.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/FunctionsBlock.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Helpers.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/HelpersMissingCloseParen.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/HelpersMissingName.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/HelpersMissingOpenParen.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ImplicitExpression.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ImplicitExpressionAtEOF.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Imports.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Inherits.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Instrumented.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/LayoutDirective.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/MarkupInCodeBlock.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/NestedCodeBlocks.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/NestedHelpers.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/NoLinePragmas.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Options.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ParserError.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/RazorComments.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ResolveUrl.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Sections.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Templates.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/UnfinishedExpressionInCode.vbhtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/DesignTime/Simple.cshtml create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/DesignTime/Simple.txt create mode 100644 test/Microsoft.AspNet.Razor.Test/TestFiles/nested-1000.html create mode 100644 test/Microsoft.AspNet.Razor.Test/Text/BufferingTextReaderTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Text/LineTrackingStringBufferTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Text/LookaheadTextReaderTestBase.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Text/SourceLocationTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Text/SourceLocationTrackerTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Text/TextBufferReaderTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Text/TextChangeTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Text/TextReaderExtensionsTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Tokenizer/CSharpTokenizerCommentTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Tokenizer/CSharpTokenizerIdentifierTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Tokenizer/CSharpTokenizerLiteralTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Tokenizer/CSharpTokenizerOperatorsTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Tokenizer/CSharpTokenizerTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Tokenizer/CSharpTokenizerTestBase.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Tokenizer/HtmlTokenizerTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Tokenizer/HtmlTokenizerTestBase.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Tokenizer/TokenizerLookaheadTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Tokenizer/TokenizerTestBase.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Tokenizer/VBTokenizerCommentTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Tokenizer/VBTokenizerIdentifierTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Tokenizer/VBTokenizerLiteralTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Tokenizer/VBTokenizerOperatorsTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Tokenizer/VBTokenizerTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Tokenizer/VBTokenizerTestBase.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Utils/DisposableActionTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Utils/EnumerableUtils.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Utils/MiscAssert.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Utils/MiscUtils.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/Utils/SpanAssert.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/VBRazorCodeLanguageTest.cs create mode 100644 test/Microsoft.AspNet.Razor.Test/packages.config create mode 100644 test/Microsoft.TestCommon/Assert.cs create mode 100644 test/Microsoft.TestCommon/CultureReplacer.cs create mode 100644 test/Microsoft.TestCommon/DataAttribute.cs create mode 100644 test/Microsoft.TestCommon/DictionaryEqualityComparer.cs create mode 100644 test/Microsoft.TestCommon/EnumHelperTestBase.cs create mode 100644 test/Microsoft.TestCommon/ExceptionAssertions.cs create mode 100644 test/Microsoft.TestCommon/FactAttribute.cs create mode 100644 test/Microsoft.TestCommon/ForceGCAttribute.cs create mode 100644 test/Microsoft.TestCommon/InlineDataAttribute.cs create mode 100644 test/Microsoft.TestCommon/MatrixTheoryDataSet.cs create mode 100644 test/Microsoft.TestCommon/MemberHelper.cs create mode 100644 test/Microsoft.TestCommon/Microsoft.TestCommon.csproj create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/CommonUnitTestDataSets.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/RefTypeTestData.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/TestData.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/TestDataHolder.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/TestDataVariations.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/ValueTypeTestData.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/GenericTypeAssert.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/HttpAssert.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/MediaTypeAssert.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/MediaTypeHeaderValueComparer.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/ParsedMediaTypeHeaderValue.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/RegexReplacement.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/RuntimeEnvironment.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/SerializerAssert.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/StreamAssert.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/TaskAssert.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/TestDataSetAttribute.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/TimeoutConstant.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/TypeAssert.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/Types/ByteEnum.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/Types/FlagsEnum.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/Types/INameAndIdContainer.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/Types/ISerializableType.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/Types/LongEnum.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/Types/SByteEnum.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/Types/ShortEnum.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/Types/SimpleEnum.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/Types/UIntEnum.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/Types/UShortEnum.cs create mode 100644 test/Microsoft.TestCommon/Microsoft/TestCommon/XmlAssert.cs create mode 100644 test/Microsoft.TestCommon/Platform.cs create mode 100644 test/Microsoft.TestCommon/PlatformInfo.cs create mode 100644 test/Microsoft.TestCommon/PortReserver.cs create mode 100644 test/Microsoft.TestCommon/PreAppStartTestHelper.cs create mode 100644 test/Microsoft.TestCommon/PreserveSyncContextAttribute.cs create mode 100644 test/Microsoft.TestCommon/PropertyDataAttribute.cs create mode 100644 test/Microsoft.TestCommon/ReflectionAssert.cs create mode 100644 test/Microsoft.TestCommon/ReplaceCultureAttribute.cs create mode 100644 test/Microsoft.TestCommon/RestoreThreadPrincipalAttribute.cs create mode 100644 test/Microsoft.TestCommon/StringAssertException.cs create mode 100644 test/Microsoft.TestCommon/StringAssertions.cs create mode 100644 test/Microsoft.TestCommon/TaskExtensions.cs create mode 100644 test/Microsoft.TestCommon/TestFile.cs create mode 100644 test/Microsoft.TestCommon/TestHelper.cs create mode 100644 test/Microsoft.TestCommon/TheoryAttribute.cs create mode 100644 test/Microsoft.TestCommon/TheoryDataSet.cs create mode 100644 test/Microsoft.TestCommon/ThreadPoolSyncContext.cs create mode 100644 test/Microsoft.TestCommon/TraitAttribute.cs create mode 100644 test/Microsoft.TestCommon/VersionTestHelper.cs create mode 100644 test/Microsoft.TestCommon/WebUtils.cs create mode 100644 test/Microsoft.TestCommon/packages.config diff --git a/Razor.sln b/Razor.sln index 08285df812..148df2a733 100644 --- a/Razor.sln +++ b/Razor.sln @@ -5,6 +5,14 @@ VisualStudioVersion = 12.0.21005.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.Razor", "src\Microsoft.AspNet.Razor\Microsoft.AspNet.Razor.csproj", "{E75D8296-3BA6-4E67-AFEB-90FF77460B15}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3C0D6505-79B3-49D0-B4C3-176F0F1836ED}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{92463391-81BE-462B-AC3C-78C6C760741F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.Razor.Test", "test\Microsoft.AspNet.Razor.Test\Microsoft.AspNet.Razor.Test.csproj", "{0BB62A1D-E6B5-49FA-9E3C-6AF679A66DFE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.TestCommon", "test\Microsoft.TestCommon\Microsoft.TestCommon.csproj", "{FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,8 +23,21 @@ Global {E75D8296-3BA6-4E67-AFEB-90FF77460B15}.Debug|Any CPU.Build.0 = Debug|Any CPU {E75D8296-3BA6-4E67-AFEB-90FF77460B15}.Release|Any CPU.ActiveCfg = Release|Any CPU {E75D8296-3BA6-4E67-AFEB-90FF77460B15}.Release|Any CPU.Build.0 = Release|Any CPU + {0BB62A1D-E6B5-49FA-9E3C-6AF679A66DFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0BB62A1D-E6B5-49FA-9E3C-6AF679A66DFE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0BB62A1D-E6B5-49FA-9E3C-6AF679A66DFE}.Release|Any CPU.ActiveCfg = Debug|Any CPU + {0BB62A1D-E6B5-49FA-9E3C-6AF679A66DFE}.Release|Any CPU.Build.0 = Debug|Any CPU + {FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0}.Release|Any CPU.ActiveCfg = Debug|Any CPU + {FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0}.Release|Any CPU.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {E75D8296-3BA6-4E67-AFEB-90FF77460B15} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED} + {0BB62A1D-E6B5-49FA-9E3C-6AF679A66DFE} = {92463391-81BE-462B-AC3C-78C6C760741F} + {FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0} = {92463391-81BE-462B-AC3C-78C6C760741F} + EndGlobalSection EndGlobal diff --git a/src/Microsoft.AspNet.Razor/Properties/AssemblyInfo.cs b/src/Microsoft.AspNet.Razor/Properties/AssemblyInfo.cs index 86e5f08fd0..55359a5a0e 100644 --- a/src/Microsoft.AspNet.Razor/Properties/AssemblyInfo.cs +++ b/src/Microsoft.AspNet.Razor/Properties/AssemblyInfo.cs @@ -37,3 +37,5 @@ using System.Runtime.InteropServices; // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] + +[assembly: InternalsVisibleTo("Microsoft.AspNet.Razor.Test")] \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/CSharpRazorCodeLanguageTest.cs b/test/Microsoft.AspNet.Razor.Test/CSharpRazorCodeLanguageTest.cs new file mode 100644 index 0000000000..48ef1f2cca --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/CSharpRazorCodeLanguageTest.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.CSharp; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test +{ + public class CSharpRazorCodeLanguageTest + { + [Fact] + public void CreateCodeParserReturnsNewCSharpCodeParser() + { + // Arrange + RazorCodeLanguage service = new CSharpRazorCodeLanguage(); + + // Act + ParserBase parser = service.CreateCodeParser(); + + // Assert + Assert.NotNull(parser); + Assert.IsType(parser); + } + + [Fact] + public void CreateCodeGeneratorParserListenerReturnsNewCSharpCodeGeneratorParserListener() + { + // Arrange + RazorCodeLanguage service = new CSharpRazorCodeLanguage(); + + // Act + RazorEngineHost host = new RazorEngineHost(service); + RazorCodeGenerator generator = service.CreateCodeGenerator("Foo", "Bar", "Baz", host); + + // Assert + Assert.NotNull(generator); + Assert.IsType(generator); + Assert.Equal("Foo", generator.ClassName); + Assert.Equal("Bar", generator.RootNamespaceName); + Assert.Equal("Baz", generator.SourceFileName); + Assert.Same(host, generator.Host); + } + + [Fact] + public void CodeDomProviderTypeReturnsVBCodeProvider() + { + // Arrange + RazorCodeLanguage service = new CSharpRazorCodeLanguage(); + + // Assert + Assert.Equal(typeof(CSharpCodeProvider), service.CodeDomProviderType); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/CodeCompileUnitExtensions.cs b/test/Microsoft.AspNet.Razor.Test/CodeCompileUnitExtensions.cs new file mode 100644 index 0000000000..c5cd9f6661 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/CodeCompileUnitExtensions.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.CodeDom; +using System.CodeDom.Compiler; +using System.IO; +using System.Text; + +namespace Microsoft.AspNet.Razor.Test +{ + internal static class CodeCompileUnitExtensions + { + public static string GenerateCode(this CodeCompileUnit ccu) where T : CodeDomProvider, new() + { + StringBuilder output = new StringBuilder(); + using (StringWriter writer = new StringWriter(output)) + { + T provider = new T(); + provider.GenerateCodeFromCompileUnit(ccu, writer, new CodeGeneratorOptions() { IndentString = " " }); + } + + return output.ToString(); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Editor/RazorEditorParserTest.cs b/test/Microsoft.AspNet.Razor.Test/Editor/RazorEditorParserTest.cs new file mode 100644 index 0000000000..b31a1c267a --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Editor/RazorEditorParserTest.cs @@ -0,0 +1,193 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.Threading; +using Microsoft.AspNet.Razor.Editor; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Test.Utils; +using Microsoft.AspNet.Razor.Text; +using System.Web.WebPages.TestUtils; +using Microsoft.CSharp; +using Microsoft.TestCommon; +using Moq; +using System; + +namespace Microsoft.AspNet.Razor.Test.Editor +{ + public class RazorEditorParserTest + { + private static readonly TestFile SimpleCSHTMLDocument = TestFile.Create("DesignTime.Simple.cshtml"); + private static readonly TestFile SimpleCSHTMLDocumentGenerated = TestFile.Create("DesignTime.Simple.txt"); + private const string TestLinePragmaFileName = "C:\\This\\Path\\Is\\Just\\For\\Line\\Pragmas.cshtml"; + + [Fact] + public void ConstructorRequiresNonNullHost() + { + Assert.ThrowsArgumentNull(() => new RazorEditorParser(null, TestLinePragmaFileName), + "host"); + } + + [Fact] + public void ConstructorRequiresNonNullPhysicalPath() + { + Assert.ThrowsArgumentNullOrEmptyString(() => new RazorEditorParser(CreateHost(), null), + "sourceFileName"); + } + + [Fact] + public void ConstructorRequiresNonEmptyPhysicalPath() + { + Assert.ThrowsArgumentNullOrEmptyString(() => new RazorEditorParser(CreateHost(), String.Empty), + "sourceFileName"); + } + + [Fact] + public void TreesAreDifferentReturnsTrueIfTreeStructureIsDifferent() + { + var factory = SpanFactory.CreateCsHtml(); + Block original = new MarkupBlock( + factory.Markup("

"), + new ExpressionBlock( + factory.CodeTransition()), + factory.Markup("

")); + Block modified = new MarkupBlock( + factory.Markup("

"), + new ExpressionBlock( + factory.CodeTransition("@"), + factory.Code("f") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: false)), + factory.Markup("

")); + ITextBuffer oldBuffer = new StringTextBuffer("

@

"); + ITextBuffer newBuffer = new StringTextBuffer("

@f

"); + Assert.True(BackgroundParser.TreesAreDifferent( + original, modified, new[] { + new TextChange(position: 4, oldLength: 0, oldBuffer: oldBuffer, newLength: 1, newBuffer: newBuffer) + })); + } + + [Fact] + public void TreesAreDifferentReturnsFalseIfTreeStructureIsSame() + { + var factory = SpanFactory.CreateCsHtml(); + Block original = new MarkupBlock( + factory.Markup("

"), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("f") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: false)), + factory.Markup("

")); + factory.Reset(); + Block modified = new MarkupBlock( + factory.Markup("

"), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("foo") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: false)), + factory.Markup("

")); + original.LinkNodes(); + modified.LinkNodes(); + ITextBuffer oldBuffer = new StringTextBuffer("

@f

"); + ITextBuffer newBuffer = new StringTextBuffer("

@foo

"); + Assert.False(BackgroundParser.TreesAreDifferent( + original, modified, new[] { + new TextChange(position: 5, oldLength: 0, oldBuffer: oldBuffer, newLength: 2, newBuffer: newBuffer) + })); + } + + [Fact] + public void CheckForStructureChangesRequiresNonNullBufferInChange() + { + TextChange change = new TextChange(); + Assert.ThrowsArgument( + () => new RazorEditorParser( + CreateHost(), + "C:\\Foo.cshtml").CheckForStructureChanges(change), + "change", + String.Format(RazorResources.Structure_Member_CannotBeNull, "Buffer", "TextChange")); + } + + private static RazorEngineHost CreateHost() + { + return new RazorEngineHost(new CSharpRazorCodeLanguage()) { DesignTimeMode = true }; + } + + [Fact] + [ReplaceCulture] + public void CheckForStructureChangesStartsReparseAndFiresDocumentParseCompletedEventIfNoAdditionalChangesQueued() + { + // Arrange + using (RazorEditorParser parser = CreateClientParser()) + { + StringTextBuffer input = new StringTextBuffer(SimpleCSHTMLDocument.ReadAllText()); + + DocumentParseCompleteEventArgs capturedArgs = null; + ManualResetEventSlim parseComplete = new ManualResetEventSlim(false); + + parser.DocumentParseComplete += (sender, args) => + { + capturedArgs = args; + parseComplete.Set(); + }; + + // Act + parser.CheckForStructureChanges(new TextChange(0, 0, new StringTextBuffer(String.Empty), input.Length, input)); + + // Assert + MiscUtils.DoWithTimeoutIfNotDebugging(parseComplete.Wait); + + string generatedCode = capturedArgs.GeneratorResults.GeneratedCode.GenerateCode(); + + Assert.Equal( + SimpleCSHTMLDocumentGenerated.ReadAllText(), + MiscUtils.StripRuntimeVersion(generatedCode)); + } + } + + [Fact] + public void CheckForStructureChangesStartsFullReparseIfChangeOverlapsMultipleSpans() + { + // Arrange + RazorEditorParser parser = new RazorEditorParser(CreateHost(), TestLinePragmaFileName); + ITextBuffer original = new StringTextBuffer("Foo @bar Baz"); + ITextBuffer changed = new StringTextBuffer("Foo @bap Daz"); + TextChange change = new TextChange(7, 3, original, 3, changed); + + ManualResetEventSlim parseComplete = new ManualResetEventSlim(); + int parseCount = 0; + parser.DocumentParseComplete += (sender, args) => + { + Interlocked.Increment(ref parseCount); + parseComplete.Set(); + }; + + Assert.Equal(PartialParseResult.Rejected, parser.CheckForStructureChanges(new TextChange(0, 0, new StringTextBuffer(String.Empty), 12, original))); + MiscUtils.DoWithTimeoutIfNotDebugging(parseComplete.Wait); // Wait for the parse to finish + parseComplete.Reset(); + + // Act + PartialParseResult result = parser.CheckForStructureChanges(change); + + // Assert + Assert.Equal(PartialParseResult.Rejected, result); + MiscUtils.DoWithTimeoutIfNotDebugging(parseComplete.Wait); + Assert.Equal(2, parseCount); + } + + private TextChange CreateDummyChange() + { + return new TextChange(0, 0, new StringTextBuffer(String.Empty), 3, new StringTextBuffer("foo")); + } + + private static Mock CreateMockParser() + { + return new Mock(CreateHost(), TestLinePragmaFileName) { CallBase = true }; + } + + private static RazorEditorParser CreateClientParser() + { + return new RazorEditorParser(CreateHost(), TestLinePragmaFileName); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Framework/BlockExtensions.cs b/test/Microsoft.AspNet.Razor.Test/Framework/BlockExtensions.cs new file mode 100644 index 0000000000..f309807f83 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Framework/BlockExtensions.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Parser.SyntaxTree; + +namespace Microsoft.AspNet.Razor.Test.Framework +{ + public static class BlockExtensions + { + public static void LinkNodes(this Block self) + { + Span first = null; + Span previous = null; + foreach (Span span in self.Flatten()) + { + if (first == null) + { + first = span; + } + span.Previous = previous; + + if (previous != null) + { + previous.Next = span; + } + previous = span; + } + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Framework/BlockTypes.cs b/test/Microsoft.AspNet.Razor.Test/Framework/BlockTypes.cs new file mode 100644 index 0000000000..18c99e9b1e --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Framework/BlockTypes.cs @@ -0,0 +1,235 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.Collections.Generic; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; + +namespace Microsoft.AspNet.Razor.Test.Framework +{ + // The product code doesn't need this, but having subclasses for the block types makes tests much cleaner :) + + public class StatementBlock : Block + { + private const BlockType ThisBlockType = BlockType.Statement; + + public StatementBlock(IBlockCodeGenerator codeGenerator, IEnumerable children) + : base(ThisBlockType, children, codeGenerator) + { + } + + public StatementBlock(IBlockCodeGenerator codeGenerator, params SyntaxTreeNode[] children) + : this(codeGenerator, (IEnumerable)children) + { + } + + public StatementBlock(params SyntaxTreeNode[] children) + : this(BlockCodeGenerator.Null, children) + { + } + + public StatementBlock(IEnumerable children) + : this(BlockCodeGenerator.Null, children) + { + } + } + + public class DirectiveBlock : Block + { + private const BlockType ThisBlockType = BlockType.Directive; + + public DirectiveBlock(IBlockCodeGenerator codeGenerator, IEnumerable children) + : base(ThisBlockType, children, codeGenerator) + { + } + + public DirectiveBlock(IBlockCodeGenerator codeGenerator, params SyntaxTreeNode[] children) + : this(codeGenerator, (IEnumerable)children) + { + } + + public DirectiveBlock(params SyntaxTreeNode[] children) + : this(BlockCodeGenerator.Null, children) + { + } + + public DirectiveBlock(IEnumerable children) + : this(BlockCodeGenerator.Null, children) + { + } + } + + public class FunctionsBlock : Block + { + private const BlockType ThisBlockType = BlockType.Functions; + + public FunctionsBlock(IBlockCodeGenerator codeGenerator, IEnumerable children) + : base(ThisBlockType, children, codeGenerator) + { + } + + public FunctionsBlock(IBlockCodeGenerator codeGenerator, params SyntaxTreeNode[] children) + : this(codeGenerator, (IEnumerable)children) + { + } + + public FunctionsBlock(params SyntaxTreeNode[] children) + : this(BlockCodeGenerator.Null, children) + { + } + + public FunctionsBlock(IEnumerable children) + : this(BlockCodeGenerator.Null, children) + { + } + } + + public class ExpressionBlock : Block + { + private const BlockType ThisBlockType = BlockType.Expression; + + public ExpressionBlock(IBlockCodeGenerator codeGenerator, IEnumerable children) + : base(ThisBlockType, children, codeGenerator) + { + } + + public ExpressionBlock(IBlockCodeGenerator codeGenerator, params SyntaxTreeNode[] children) + : this(codeGenerator, (IEnumerable)children) + { + } + + public ExpressionBlock(params SyntaxTreeNode[] children) + : this(new ExpressionCodeGenerator(), children) + { + } + + public ExpressionBlock(IEnumerable children) + : this(new ExpressionCodeGenerator(), children) + { + } + } + + public class HelperBlock : Block + { + private const BlockType ThisBlockType = BlockType.Helper; + + public HelperBlock(IBlockCodeGenerator codeGenerator, IEnumerable children) + : base(ThisBlockType, children, codeGenerator) + { + } + + public HelperBlock(IBlockCodeGenerator codeGenerator, params SyntaxTreeNode[] children) + : this(codeGenerator, (IEnumerable)children) + { + } + + public HelperBlock(params SyntaxTreeNode[] children) + : this(BlockCodeGenerator.Null, children) + { + } + + public HelperBlock(IEnumerable children) + : this(BlockCodeGenerator.Null, children) + { + } + } + + public class MarkupBlock : Block + { + private const BlockType ThisBlockType = BlockType.Markup; + + public MarkupBlock(IBlockCodeGenerator codeGenerator, IEnumerable children) + : base(ThisBlockType, children, codeGenerator) + { + } + + public MarkupBlock(IBlockCodeGenerator codeGenerator, params SyntaxTreeNode[] children) + : this(codeGenerator, (IEnumerable)children) + { + } + + public MarkupBlock(params SyntaxTreeNode[] children) + : this(BlockCodeGenerator.Null, children) + { + } + + public MarkupBlock(IEnumerable children) + : this(BlockCodeGenerator.Null, children) + { + } + } + + public class SectionBlock : Block + { + private const BlockType ThisBlockType = BlockType.Section; + + public SectionBlock(IBlockCodeGenerator codeGenerator, IEnumerable children) + : base(ThisBlockType, children, codeGenerator) + { + } + + public SectionBlock(IBlockCodeGenerator codeGenerator, params SyntaxTreeNode[] children) + : this(codeGenerator, (IEnumerable)children) + { + } + + public SectionBlock(params SyntaxTreeNode[] children) + : this(BlockCodeGenerator.Null, children) + { + } + + public SectionBlock(IEnumerable children) + : this(BlockCodeGenerator.Null, children) + { + } + } + + public class TemplateBlock : Block + { + private const BlockType ThisBlockType = BlockType.Template; + + public TemplateBlock(IBlockCodeGenerator codeGenerator, IEnumerable children) + : base(ThisBlockType, children, codeGenerator) + { + } + + public TemplateBlock(IBlockCodeGenerator codeGenerator, params SyntaxTreeNode[] children) + : this(codeGenerator, (IEnumerable)children) + { + } + + public TemplateBlock(params SyntaxTreeNode[] children) + : this(new TemplateBlockCodeGenerator(), children) + { + } + + public TemplateBlock(IEnumerable children) + : this(new TemplateBlockCodeGenerator(), children) + { + } + } + + public class CommentBlock : Block + { + private const BlockType ThisBlockType = BlockType.Comment; + + public CommentBlock(IBlockCodeGenerator codeGenerator, IEnumerable children) + : base(ThisBlockType, children, codeGenerator) + { + } + + public CommentBlock(IBlockCodeGenerator codeGenerator, params SyntaxTreeNode[] children) + : this(codeGenerator, (IEnumerable)children) + { + } + + public CommentBlock(params SyntaxTreeNode[] children) + : this(new RazorCommentCodeGenerator(), children) + { + } + + public CommentBlock(IEnumerable children) + : this(new RazorCommentCodeGenerator(), children) + { + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Framework/CodeParserTestBase.cs b/test/Microsoft.AspNet.Razor.Test/Framework/CodeParserTestBase.cs new file mode 100644 index 0000000000..77e5a3922f --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Framework/CodeParserTestBase.cs @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.Collections.Generic; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; + +namespace Microsoft.AspNet.Razor.Test.Framework +{ + public abstract class CodeParserTestBase : ParserTestBase + { + protected abstract ISet KeywordSet { get; } + + protected override ParserBase SelectActiveParser(ParserBase codeParser, ParserBase markupParser) + { + return codeParser; + } + + protected void ImplicitExpressionTest(string input, params RazorError[] errors) + { + ImplicitExpressionTest(input, AcceptedCharacters.NonWhiteSpace, errors); + } + + protected void ImplicitExpressionTest(string input, AcceptedCharacters acceptedCharacters, params RazorError[] errors) + { + ImplicitExpressionTest(input, input, acceptedCharacters, errors); + } + + protected void ImplicitExpressionTest(string input, string expected, params RazorError[] errors) + { + ImplicitExpressionTest(input, expected, AcceptedCharacters.NonWhiteSpace, errors); + } + + protected override void SingleSpanBlockTest(string document, BlockType blockType, SpanKind spanType, AcceptedCharacters acceptedCharacters = AcceptedCharacters.Any) + { + SingleSpanBlockTest(document, blockType, spanType, acceptedCharacters, expectedError: null); + } + + protected override void SingleSpanBlockTest(string document, string spanContent, BlockType blockType, SpanKind spanType, AcceptedCharacters acceptedCharacters = AcceptedCharacters.Any) + { + SingleSpanBlockTest(document, spanContent, blockType, spanType, acceptedCharacters, expectedErrors: null); + } + + protected override void SingleSpanBlockTest(string document, BlockType blockType, SpanKind spanType, params RazorError[] expectedError) + { + SingleSpanBlockTest(document, document, blockType, spanType, expectedError); + } + + protected override void SingleSpanBlockTest(string document, string spanContent, BlockType blockType, SpanKind spanType, params RazorError[] expectedErrors) + { + SingleSpanBlockTest(document, spanContent, blockType, spanType, AcceptedCharacters.Any, expectedErrors ?? new RazorError[0]); + } + + protected override void SingleSpanBlockTest(string document, BlockType blockType, SpanKind spanType, AcceptedCharacters acceptedCharacters, params RazorError[] expectedError) + { + SingleSpanBlockTest(document, document, blockType, spanType, acceptedCharacters, expectedError); + } + + protected override void SingleSpanBlockTest(string document, string spanContent, BlockType blockType, SpanKind spanType, AcceptedCharacters acceptedCharacters, params RazorError[] expectedErrors) + { + Block b = CreateSimpleBlockAndSpan(spanContent, blockType, spanType, acceptedCharacters); + ParseBlockTest(document, b, expectedErrors ?? new RazorError[0]); + } + + protected void ImplicitExpressionTest(string input, string expected, AcceptedCharacters acceptedCharacters, params RazorError[] errors) + { + var factory = CreateSpanFactory(); + ParseBlockTest(SyntaxConstants.TransitionString + input, + new ExpressionBlock( + factory.CodeTransition(), + factory.Code(expected) + .AsImplicitExpression(KeywordSet) + .Accepts(acceptedCharacters)), + errors); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Framework/CsHtmlCodeParserTestBase.cs b/test/Microsoft.AspNet.Razor.Test/Framework/CsHtmlCodeParserTestBase.cs new file mode 100644 index 0000000000..2e382a5dbd --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Framework/CsHtmlCodeParserTestBase.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.Collections.Generic; +using Microsoft.AspNet.Razor.Parser; + +namespace Microsoft.AspNet.Razor.Test.Framework +{ + public abstract class CsHtmlCodeParserTestBase : CodeParserTestBase + { + protected override ISet KeywordSet + { + get { return CSharpCodeParser.DefaultKeywords; } + } + + protected override SpanFactory CreateSpanFactory() + { + return SpanFactory.CreateCsHtml(); + } + + public override ParserBase CreateMarkupParser() + { + return new HtmlMarkupParser(); + } + + public override ParserBase CreateCodeParser() + { + return new CSharpCodeParser(); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Framework/CsHtmlMarkupParserTestBase.cs b/test/Microsoft.AspNet.Razor.Test/Framework/CsHtmlMarkupParserTestBase.cs new file mode 100644 index 0000000000..058f804378 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Framework/CsHtmlMarkupParserTestBase.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.Collections.Generic; +using Microsoft.AspNet.Razor.Parser; + +namespace Microsoft.AspNet.Razor.Test.Framework +{ + public abstract class CsHtmlMarkupParserTestBase : MarkupParserTestBase + { + protected override ISet KeywordSet + { + get { return CSharpCodeParser.DefaultKeywords; } + } + + protected override SpanFactory CreateSpanFactory() + { + return SpanFactory.CreateCsHtml(); + } + + public override ParserBase CreateMarkupParser() + { + return new HtmlMarkupParser(); + } + + public override ParserBase CreateCodeParser() + { + return new CSharpCodeParser(); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Framework/ErrorCollector.cs b/test/Microsoft.AspNet.Razor.Test/Framework/ErrorCollector.cs new file mode 100644 index 0000000000..b5cf1c2d03 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Framework/ErrorCollector.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Text; +using Microsoft.AspNet.Razor.Utils; + +namespace Microsoft.AspNet.Razor.Test.Framework +{ + public class ErrorCollector + { + private StringBuilder _message = new StringBuilder(); + private int _indent = 0; + + public bool Success { get; private set; } + + public string Message + { + get { return _message.ToString(); } + } + + public ErrorCollector() + { + Success = true; + } + + public void AddError(string msg, params object[] args) + { + Append("F", msg, args); + Success = false; + } + + public void AddMessage(string msg, params object[] args) + { + Append("P", msg, args); + } + + public IDisposable Indent() + { + _indent++; + return new DisposableAction(Unindent); + } + + public void Unindent() + { + _indent--; + } + + private void Append(string prefix, string msg, object[] args) + { + _message.Append(prefix); + _message.Append(":"); + _message.Append(new String('\t', _indent)); + _message.AppendFormat(msg, args); + _message.AppendLine(); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Framework/MarkupParserTestBase.cs b/test/Microsoft.AspNet.Razor.Test/Framework/MarkupParserTestBase.cs new file mode 100644 index 0000000000..f57d1ee31e --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Framework/MarkupParserTestBase.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; + +namespace Microsoft.AspNet.Razor.Test.Framework +{ + public abstract class MarkupParserTestBase : CodeParserTestBase + { + protected override ParserBase SelectActiveParser(ParserBase codeParser, ParserBase markupParser) + { + return markupParser; + } + + protected virtual void SingleSpanDocumentTest(string document, BlockType blockType, SpanKind spanType) + { + Block b = CreateSimpleBlockAndSpan(document, blockType, spanType); + ParseDocumentTest(document, b); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Framework/ParserTestBase.cs b/test/Microsoft.AspNet.Razor.Test/Framework/ParserTestBase.cs new file mode 100644 index 0000000000..0c96acf86a --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Framework/ParserTestBase.cs @@ -0,0 +1,426 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +//#define PARSER_TRACE + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Text; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Framework +{ + public abstract class ParserTestBase + { + protected static Block IgnoreOutput = new IgnoreOutputBlock(); + + public SpanFactory Factory { get; private set; } + + protected ParserTestBase() + { + Factory = CreateSpanFactory(); + } + + public abstract ParserBase CreateMarkupParser(); + public abstract ParserBase CreateCodeParser(); + + protected abstract ParserBase SelectActiveParser(ParserBase codeParser, ParserBase markupParser); + + public virtual ParserContext CreateParserContext(ITextDocument input, ParserBase codeParser, ParserBase markupParser) + { + return new ParserContext(input, codeParser, markupParser, SelectActiveParser(codeParser, markupParser)); + } + + protected abstract SpanFactory CreateSpanFactory(); + + protected virtual void ParseBlockTest(string document) + { + ParseBlockTest(document, null, false, new RazorError[0]); + } + + protected virtual void ParseBlockTest(string document, bool designTimeParser) + { + ParseBlockTest(document, null, designTimeParser, new RazorError[0]); + } + + protected virtual void ParseBlockTest(string document, params RazorError[] expectedErrors) + { + ParseBlockTest(document, false, expectedErrors); + } + + protected virtual void ParseBlockTest(string document, bool designTimeParser, params RazorError[] expectedErrors) + { + ParseBlockTest(document, null, designTimeParser, expectedErrors); + } + + protected virtual void ParseBlockTest(string document, Block expectedRoot) + { + ParseBlockTest(document, expectedRoot, false, null); + } + + protected virtual void ParseBlockTest(string document, Block expectedRoot, bool designTimeParser) + { + ParseBlockTest(document, expectedRoot, designTimeParser, null); + } + + protected virtual void ParseBlockTest(string document, Block expectedRoot, params RazorError[] expectedErrors) + { + ParseBlockTest(document, expectedRoot, false, expectedErrors); + } + + protected virtual void ParseBlockTest(string document, Block expectedRoot, bool designTimeParser, params RazorError[] expectedErrors) + { + RunParseTest(document, parser => parser.ParseBlock, expectedRoot, (expectedErrors ?? new RazorError[0]).ToList(), designTimeParser); + } + + protected virtual void SingleSpanBlockTest(string document, BlockType blockType, SpanKind spanType, AcceptedCharacters acceptedCharacters = AcceptedCharacters.Any) + { + SingleSpanBlockTest(document, blockType, spanType, acceptedCharacters, expectedError: null); + } + + protected virtual void SingleSpanBlockTest(string document, string spanContent, BlockType blockType, SpanKind spanType, AcceptedCharacters acceptedCharacters = AcceptedCharacters.Any) + { + SingleSpanBlockTest(document, spanContent, blockType, spanType, acceptedCharacters, expectedErrors: null); + } + + protected virtual void SingleSpanBlockTest(string document, BlockType blockType, SpanKind spanType, params RazorError[] expectedError) + { + SingleSpanBlockTest(document, document, blockType, spanType, expectedError); + } + + protected virtual void SingleSpanBlockTest(string document, string spanContent, BlockType blockType, SpanKind spanType, params RazorError[] expectedErrors) + { + SingleSpanBlockTest(document, spanContent, blockType, spanType, AcceptedCharacters.Any, expectedErrors ?? new RazorError[0]); + } + + protected virtual void SingleSpanBlockTest(string document, BlockType blockType, SpanKind spanType, AcceptedCharacters acceptedCharacters, params RazorError[] expectedError) + { + SingleSpanBlockTest(document, document, blockType, spanType, acceptedCharacters, expectedError); + } + + protected virtual void SingleSpanBlockTest(string document, string spanContent, BlockType blockType, SpanKind spanType, AcceptedCharacters acceptedCharacters, params RazorError[] expectedErrors) + { + BlockBuilder builder = new BlockBuilder(); + builder.Type = blockType; + ParseBlockTest( + document, + ConfigureAndAddSpanToBlock( + builder, + Factory.Span(spanType, spanContent, spanType == SpanKind.Markup) + .Accepts(acceptedCharacters)), + expectedErrors ?? new RazorError[0]); + } + + protected virtual void ParseDocumentTest(string document) { + ParseDocumentTest(document, null, false); + } + + protected virtual void ParseDocumentTest(string document, Block expectedRoot) { + ParseDocumentTest(document, expectedRoot, false, null); + } + + protected virtual void ParseDocumentTest(string document, Block expectedRoot, params RazorError[] expectedErrors) { + ParseDocumentTest(document, expectedRoot, false, expectedErrors); + } + + protected virtual void ParseDocumentTest(string document, bool designTimeParser) { + ParseDocumentTest(document, null, designTimeParser); + } + + protected virtual void ParseDocumentTest(string document, Block expectedRoot, bool designTimeParser) { + ParseDocumentTest(document, expectedRoot, designTimeParser, null); + } + + protected virtual void ParseDocumentTest(string document, Block expectedRoot, bool designTimeParser, params RazorError[] expectedErrors) { + RunParseTest(document, parser => parser.ParseDocument, expectedRoot, expectedErrors, designTimeParser, parserSelector: c => c.MarkupParser); + } + + protected virtual ParserResults ParseDocument(string document) { + return ParseDocument(document, designTimeParser: false); + } + + protected virtual ParserResults ParseDocument(string document, bool designTimeParser) { + return RunParse(document, parser => parser.ParseDocument, designTimeParser, parserSelector: c => c.MarkupParser); + } + + protected virtual ParserResults ParseBlock(string document) { + return ParseBlock(document, designTimeParser: false); + } + + protected virtual ParserResults ParseBlock(string document, bool designTimeParser) { + return RunParse(document, parser => parser.ParseBlock, designTimeParser); + } + + protected virtual ParserResults RunParse(string document, Func parserActionSelector, bool designTimeParser, Func parserSelector = null) + { + parserSelector = parserSelector ?? (c => c.ActiveParser); + + // Create the source + ParserResults results = null; + using (SeekableTextReader reader = new SeekableTextReader(document)) + { + try + { + ParserBase codeParser = CreateCodeParser(); + ParserBase markupParser = CreateMarkupParser(); + ParserContext context = CreateParserContext(reader, codeParser, markupParser); + context.DesignTimeMode = designTimeParser; + + codeParser.Context = context; + markupParser.Context = context; + + // Run the parser + parserActionSelector(parserSelector(context))(); + results = context.CompleteParse(); + } + finally + { + if (results != null && results.Document != null) + { + WriteTraceLine(String.Empty); + WriteTraceLine("Actual Parse Tree:"); + WriteNode(0, results.Document); + } + } + } + return results; + } + + protected virtual void RunParseTest(string document, Func parserActionSelector, Block expectedRoot, IList expectedErrors, bool designTimeParser, Func parserSelector = null) + { + // Create the source + ParserResults results = RunParse(document, parserActionSelector, designTimeParser, parserSelector); + + // Evaluate the results + if (!ReferenceEquals(expectedRoot, IgnoreOutput)) + { + EvaluateResults(results, expectedRoot, expectedErrors); + } + } + + [Conditional("PARSER_TRACE")] + private void WriteNode(int indent, SyntaxTreeNode node) + { + string content = node.ToString().Replace("\r", "\\r") + .Replace("\n", "\\n") + .Replace("{", "{{") + .Replace("}", "}}"); + if (indent > 0) + { + content = new String('.', indent * 2) + content; + } + WriteTraceLine(content); + Block block = node as Block; + if (block != null) + { + foreach (SyntaxTreeNode child in block.Children) + { + WriteNode(indent + 1, child); + } + } + } + + public static void EvaluateResults(ParserResults results, Block expectedRoot) + { + EvaluateResults(results, expectedRoot, null); + } + + public static void EvaluateResults(ParserResults results, Block expectedRoot, IList expectedErrors) + { + EvaluateParseTree(results.Document, expectedRoot); + EvaluateRazorErrors(results.ParserErrors, expectedErrors); + } + + public static void EvaluateParseTree(Block actualRoot, Block expectedRoot) + { + // Evaluate the result + ErrorCollector collector = new ErrorCollector(); + + // Link all the nodes + expectedRoot.LinkNodes(); + + if (expectedRoot == null) + { + Assert.Null(actualRoot); + } + else + { + Assert.NotNull(actualRoot); + EvaluateSyntaxTreeNode(collector, actualRoot, expectedRoot); + if (collector.Success) + { + WriteTraceLine("Parse Tree Validation Succeeded:\r\n{0}", collector.Message); + } + else + { + Assert.True(false, String.Format("\r\n{0}", collector.Message)); + } + } + } + + private static void EvaluateSyntaxTreeNode(ErrorCollector collector, SyntaxTreeNode actual, SyntaxTreeNode expected) + { + if (actual == null) + { + AddNullActualError(collector, actual, expected); + } + + if (actual.IsBlock != expected.IsBlock) + { + AddMismatchError(collector, actual, expected); + } + else + { + if (expected.IsBlock) + { + EvaluateBlock(collector, (Block)actual, (Block)expected); + } + else + { + EvaluateSpan(collector, (Span)actual, (Span)expected); + } + } + } + + private static void EvaluateSpan(ErrorCollector collector, Span actual, Span expected) + { + if (!Equals(expected, actual)) + { + AddMismatchError(collector, actual, expected); + } + else + { + AddPassedMessage(collector, expected); + } + } + + private static void EvaluateBlock(ErrorCollector collector, Block actual, Block expected) + { + if (actual.Type != expected.Type || !expected.CodeGenerator.Equals(actual.CodeGenerator)) + { + AddMismatchError(collector, actual, expected); + } + else + { + AddPassedMessage(collector, expected); + using (collector.Indent()) + { + IEnumerator expectedNodes = expected.Children.GetEnumerator(); + IEnumerator actualNodes = actual.Children.GetEnumerator(); + while (expectedNodes.MoveNext()) + { + if (!actualNodes.MoveNext()) + { + collector.AddError("{0} - FAILED :: No more elements at this node", expectedNodes.Current); + } + else + { + EvaluateSyntaxTreeNode(collector, actualNodes.Current, expectedNodes.Current); + } + } + while (actualNodes.MoveNext()) + { + collector.AddError("End of Node - FAILED :: Found Node: {0}", actualNodes.Current); + } + } + } + } + + private static void AddPassedMessage(ErrorCollector collector, SyntaxTreeNode expected) + { + collector.AddMessage("{0} - PASSED", expected); + } + + private static void AddMismatchError(ErrorCollector collector, SyntaxTreeNode actual, SyntaxTreeNode expected) + { + collector.AddError("{0} - FAILED :: Actual: {1}", expected, actual); + } + + private static void AddNullActualError(ErrorCollector collector, SyntaxTreeNode actual, SyntaxTreeNode expected) + { + collector.AddError("{0} - FAILED :: Actual: << Null >>", expected); + } + + public static void EvaluateRazorErrors(IList actualErrors, IList expectedErrors) + { + // Evaluate the errors + if (expectedErrors == null || expectedErrors.Count == 0) + { + Assert.True(actualErrors.Count == 0, + String.Format("Expected that no errors would be raised, but the following errors were:\r\n{0}", FormatErrors(actualErrors))); + } + else + { + Assert.True(expectedErrors.Count == actualErrors.Count, + String.Format("Expected that {0} errors would be raised, but {1} errors were.\r\nExpected Errors: \r\n{2}\r\nActual Errors: \r\n{3}", + expectedErrors.Count, + actualErrors.Count, + FormatErrors(expectedErrors), + FormatErrors(actualErrors))); + Assert.Equal(expectedErrors.ToArray(), actualErrors.ToArray()); + } + WriteTraceLine("Expected Errors were raised:\r\n{0}", FormatErrors(expectedErrors)); + } + + public static string FormatErrors(IList errors) + { + if (errors == null) + { + return "\t<< No Errors >>"; + } + + StringBuilder builder = new StringBuilder(); + foreach (RazorError err in errors) + { + builder.AppendFormat("\t{0}", err); + builder.AppendLine(); + } + return builder.ToString(); + } + + [Conditional("PARSER_TRACE")] + private static void WriteTraceLine(string format, params object[] args) + { + Trace.WriteLine(String.Format(format, args)); + } + + protected virtual Block CreateSimpleBlockAndSpan(string spanContent, BlockType blockType, SpanKind spanType, AcceptedCharacters acceptedCharacters = AcceptedCharacters.Any) + { + SpanConstructor span = Factory.Span(spanType, spanContent, spanType == SpanKind.Markup).Accepts(acceptedCharacters); + BlockBuilder b = new BlockBuilder() + { + Type = blockType + }; + return ConfigureAndAddSpanToBlock(b, span); + } + + protected virtual Block ConfigureAndAddSpanToBlock(BlockBuilder block, SpanConstructor span) + { + switch (block.Type) + { + case BlockType.Markup: + span.With(new MarkupCodeGenerator()); + break; + case BlockType.Statement: + span.With(new StatementCodeGenerator()); + break; + case BlockType.Expression: + block.CodeGenerator = new ExpressionCodeGenerator(); + span.With(new ExpressionCodeGenerator()); + break; + } + block.Children.Add(span); + return block.Build(); + } + + private class IgnoreOutputBlock : Block + { + public IgnoreOutputBlock() : base(BlockType.Template, Enumerable.Empty(), null) { } + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Framework/RawTextSymbol.cs b/test/Microsoft.AspNet.Razor.Test/Framework/RawTextSymbol.cs new file mode 100644 index 0000000000..f4532db07e --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Framework/RawTextSymbol.cs @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Globalization; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Text; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.Internal.Web.Utils; + +namespace Microsoft.AspNet.Razor.Test.Framework +{ + internal class RawTextSymbol : ISymbol + { + public SourceLocation Start { get; private set; } + public string Content { get; private set; } + + public RawTextSymbol(SourceLocation start, string content) + { + if (content == null) + { + throw new ArgumentNullException("content"); + } + + Start = start; + Content = content; + } + + public override bool Equals(object obj) + { + RawTextSymbol other = obj as RawTextSymbol; + return Equals(Start, other.Start) && Equals(Content, other.Content); + } + + internal bool EquivalentTo(ISymbol sym) + { + return Equals(Start, sym.Start) && Equals(Content, sym.Content); + } + + public override int GetHashCode() + { + return HashCodeCombiner.Start() + .Add(Start) + .Add(Content) + .CombinedHash; + } + + public void OffsetStart(SourceLocation documentStart) + { + Start = documentStart + Start; + } + + public void ChangeStart(SourceLocation newStart) + { + Start = newStart; + } + + public override string ToString() + { + return String.Format(CultureInfo.InvariantCulture, "{0} RAW - [{1}]", Start, Content); + } + + internal void CalculateStart(Span prev) + { + if (prev == null) + { + Start = SourceLocation.Zero; + } + else + { + Start = new SourceLocationTracker(prev.Start).UpdateLocation(prev.Content).CurrentLocation; + } + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Framework/TestSpanBuilder.cs b/test/Microsoft.AspNet.Razor.Test/Framework/TestSpanBuilder.cs new file mode 100644 index 0000000000..96dc9859e0 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Framework/TestSpanBuilder.cs @@ -0,0 +1,408 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNet.Razor.Editor; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Text; +using Microsoft.AspNet.Razor.Tokenizer; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; + +namespace Microsoft.AspNet.Razor.Test.Framework +{ + public static class SpanFactoryExtensions + { + public static UnclassifiedCodeSpanConstructor EmptyCSharp(this SpanFactory self) + { + return new UnclassifiedCodeSpanConstructor( + self.Span(SpanKind.Code, new CSharpSymbol(self.LocationTracker.CurrentLocation, String.Empty, CSharpSymbolType.Unknown))); + } + + public static UnclassifiedCodeSpanConstructor EmptyVB(this SpanFactory self) + { + return new UnclassifiedCodeSpanConstructor( + self.Span(SpanKind.Code, new VBSymbol(self.LocationTracker.CurrentLocation, String.Empty, VBSymbolType.Unknown))); + } + + public static SpanConstructor EmptyHtml(this SpanFactory self) + { + return self.Span(SpanKind.Markup, new HtmlSymbol(self.LocationTracker.CurrentLocation, String.Empty, HtmlSymbolType.Unknown)) + .With(new MarkupCodeGenerator()); + } + + public static UnclassifiedCodeSpanConstructor Code(this SpanFactory self, string content) + { + return new UnclassifiedCodeSpanConstructor( + self.Span(SpanKind.Code, content, markup: false)); + } + + public static SpanConstructor CodeTransition(this SpanFactory self) + { + return self.Span(SpanKind.Transition, SyntaxConstants.TransitionString, markup: false).Accepts(AcceptedCharacters.None); + } + + public static SpanConstructor CodeTransition(this SpanFactory self, string content) + { + return self.Span(SpanKind.Transition, content, markup: false).Accepts(AcceptedCharacters.None); + } + + public static SpanConstructor CodeTransition(this SpanFactory self, CSharpSymbolType type) + { + return self.Span(SpanKind.Transition, SyntaxConstants.TransitionString, type).Accepts(AcceptedCharacters.None); + } + + public static SpanConstructor CodeTransition(this SpanFactory self, string content, CSharpSymbolType type) + { + return self.Span(SpanKind.Transition, content, type).Accepts(AcceptedCharacters.None); + } + + public static SpanConstructor CodeTransition(this SpanFactory self, VBSymbolType type) + { + return self.Span(SpanKind.Transition, SyntaxConstants.TransitionString, type).Accepts(AcceptedCharacters.None); + } + + public static SpanConstructor CodeTransition(this SpanFactory self, string content, VBSymbolType type) + { + return self.Span(SpanKind.Transition, content, type).Accepts(AcceptedCharacters.None); + } + + public static SpanConstructor MarkupTransition(this SpanFactory self) + { + return self.Span(SpanKind.Transition, SyntaxConstants.TransitionString, markup: true).Accepts(AcceptedCharacters.None); + } + + public static SpanConstructor MarkupTransition(this SpanFactory self, string content) + { + return self.Span(SpanKind.Transition, content, markup: true).Accepts(AcceptedCharacters.None); + } + + public static SpanConstructor MarkupTransition(this SpanFactory self, HtmlSymbolType type) + { + return self.Span(SpanKind.Transition, SyntaxConstants.TransitionString, type).Accepts(AcceptedCharacters.None); + } + + public static SpanConstructor MarkupTransition(this SpanFactory self, string content, HtmlSymbolType type) + { + return self.Span(SpanKind.Transition, content, type).Accepts(AcceptedCharacters.None); + } + + public static SpanConstructor MetaCode(this SpanFactory self, string content) + { + return self.Span(SpanKind.MetaCode, content, markup: false); + } + + public static SpanConstructor MetaCode(this SpanFactory self, string content, CSharpSymbolType type) + { + return self.Span(SpanKind.MetaCode, content, type); + } + + public static SpanConstructor MetaCode(this SpanFactory self, string content, VBSymbolType type) + { + return self.Span(SpanKind.MetaCode, content, type); + } + + public static SpanConstructor MetaMarkup(this SpanFactory self, string content) + { + return self.Span(SpanKind.MetaCode, content, markup: true); + } + + public static SpanConstructor MetaMarkup(this SpanFactory self, string content, HtmlSymbolType type) + { + return self.Span(SpanKind.MetaCode, content, type); + } + + public static SpanConstructor Comment(this SpanFactory self, string content, CSharpSymbolType type) + { + return self.Span(SpanKind.Comment, content, type); + } + + public static SpanConstructor Comment(this SpanFactory self, string content, VBSymbolType type) + { + return self.Span(SpanKind.Comment, content, type); + } + + public static SpanConstructor Comment(this SpanFactory self, string content, HtmlSymbolType type) + { + return self.Span(SpanKind.Comment, content, type); + } + + public static SpanConstructor Markup(this SpanFactory self, string content) + { + return self.Span(SpanKind.Markup, content, markup: true).With(new MarkupCodeGenerator()); + } + + public static SpanConstructor Markup(this SpanFactory self, params string[] content) + { + return self.Span(SpanKind.Markup, content, markup: true).With(new MarkupCodeGenerator()); + } + + public static SourceLocation GetLocationAndAdvance(this SourceLocationTracker self, string content) + { + SourceLocation ret = self.CurrentLocation; + self.UpdateLocation(content); + return ret; + } + } + + public class SpanFactory + { + public Func MarkupTokenizerFactory { get; set; } + public Func CodeTokenizerFactory { get; set; } + public SourceLocationTracker LocationTracker { get; private set; } + + public static SpanFactory CreateCsHtml() + { + return new SpanFactory() + { + MarkupTokenizerFactory = doc => new HtmlTokenizer(doc), + CodeTokenizerFactory = doc => new CSharpTokenizer(doc) + }; + } + + public static SpanFactory CreateVbHtml() + { + return new SpanFactory() + { + MarkupTokenizerFactory = doc => new HtmlTokenizer(doc), + CodeTokenizerFactory = doc => new VBTokenizer(doc) + }; + } + + public SpanFactory() + { + LocationTracker = new SourceLocationTracker(); + } + + public SpanConstructor Span(SpanKind kind, string content, CSharpSymbolType type) + { + return CreateSymbolSpan(kind, content, st => new CSharpSymbol(st, content, type)); + } + + public SpanConstructor Span(SpanKind kind, string content, VBSymbolType type) + { + return CreateSymbolSpan(kind, content, st => new VBSymbol(st, content, type)); + } + + public SpanConstructor Span(SpanKind kind, string content, HtmlSymbolType type) + { + return CreateSymbolSpan(kind, content, st => new HtmlSymbol(st, content, type)); + } + + public SpanConstructor Span(SpanKind kind, string content, bool markup) + { + return new SpanConstructor(kind, Tokenize(new[] { content }, markup)); + } + + public SpanConstructor Span(SpanKind kind, string[] content, bool markup) + { + return new SpanConstructor(kind, Tokenize(content, markup)); + } + + public SpanConstructor Span(SpanKind kind, params ISymbol[] symbols) + { + return new SpanConstructor(kind, symbols); + } + + private SpanConstructor CreateSymbolSpan(SpanKind kind, string content, Func ctor) + { + SourceLocation start = LocationTracker.CurrentLocation; + LocationTracker.UpdateLocation(content); + return new SpanConstructor(kind, new[] { ctor(start) }); + } + + public void Reset() + { + LocationTracker.CurrentLocation = SourceLocation.Zero; + } + + private IEnumerable Tokenize(IEnumerable contentFragments, bool markup) + { + return contentFragments.SelectMany(fragment => Tokenize(fragment, markup)); + } + + private IEnumerable Tokenize(string content, bool markup) + { + ITokenizer tok = MakeTokenizer(markup, new SeekableTextReader(content)); + ISymbol sym; + ISymbol last = null; + while ((sym = tok.NextSymbol()) != null) + { + OffsetStart(sym, LocationTracker.CurrentLocation); + last = sym; + yield return sym; + } + LocationTracker.UpdateLocation(content); + } + + private ITokenizer MakeTokenizer(bool markup, SeekableTextReader seekableTextReader) + { + if (markup) + { + return MarkupTokenizerFactory(seekableTextReader); + } + else + { + return CodeTokenizerFactory(seekableTextReader); + } + } + + private void OffsetStart(ISymbol sym, SourceLocation sourceLocation) + { + sym.OffsetStart(sourceLocation); + } + } + + public static class SpanConstructorExtensions + { + public static SpanConstructor Accepts(this SpanConstructor self, AcceptedCharacters accepted) + { + return self.With(eh => eh.AcceptedCharacters = accepted); + } + + public static SpanConstructor AutoCompleteWith(this SpanConstructor self, string autoCompleteString) + { + return AutoCompleteWith(self, autoCompleteString, atEndOfSpan: false); + } + + public static SpanConstructor AutoCompleteWith(this SpanConstructor self, string autoCompleteString, bool atEndOfSpan) + { + return self.With(new AutoCompleteEditHandler(SpanConstructor.TestTokenizer) { AutoCompleteString = autoCompleteString, AutoCompleteAtEndOfSpan = atEndOfSpan }); + } + + public static SpanConstructor WithEditorHints(this SpanConstructor self, EditorHints hints) + { + return self.With(eh => eh.EditorHints = hints); + } + } + + public class UnclassifiedCodeSpanConstructor + { + SpanConstructor _self; + + public UnclassifiedCodeSpanConstructor(SpanConstructor self) + { + _self = self; + } + + public SpanConstructor AsMetaCode() + { + _self.Builder.Kind = SpanKind.MetaCode; + return _self; + } + + public SpanConstructor AsStatement() + { + return _self.With(new StatementCodeGenerator()); + } + + public SpanConstructor AsExpression() + { + return _self.With(new ExpressionCodeGenerator()); + } + + public SpanConstructor AsImplicitExpression(ISet keywords) + { + return AsImplicitExpression(keywords, acceptTrailingDot: false); + } + + public SpanConstructor AsImplicitExpression(ISet keywords, bool acceptTrailingDot) + { + return _self.With(new ImplicitExpressionEditHandler(SpanConstructor.TestTokenizer, keywords, acceptTrailingDot)) + .With(new ExpressionCodeGenerator()); + } + + public SpanConstructor AsFunctionsBody() + { + return _self.With(new TypeMemberCodeGenerator()); + } + + public SpanConstructor AsNamespaceImport(string ns, int namespaceKeywordLength) + { + return _self.With(new AddImportCodeGenerator(ns, namespaceKeywordLength)); + } + + public SpanConstructor Hidden() + { + return _self.With(SpanCodeGenerator.Null); + } + + public SpanConstructor AsBaseType(string baseType) + { + return _self.With(new SetBaseTypeCodeGenerator(baseType)); + } + + public SpanConstructor AsRazorDirectiveAttribute(string key, string value) + { + return _self.With(new RazorDirectiveAttributeCodeGenerator(key, value)); + } + + public SpanConstructor As(ISpanCodeGenerator codeGenerator) + { + return _self.With(codeGenerator); + } + } + + public class SpanConstructor + { + public SpanBuilder Builder { get; private set; } + + internal static IEnumerable TestTokenizer(string str) + { + yield return new RawTextSymbol(SourceLocation.Zero, str); + } + + public SpanConstructor(SpanKind kind, IEnumerable symbols) + { + Builder = new SpanBuilder(); + Builder.Kind = kind; + Builder.EditHandler = SpanEditHandler.CreateDefault(TestTokenizer); + foreach (ISymbol sym in symbols) + { + Builder.Accept(sym); + } + } + + private Span Build() + { + return Builder.Build(); + } + + public SpanConstructor With(ISpanCodeGenerator generator) + { + Builder.CodeGenerator = generator; + return this; + } + + public SpanConstructor With(SpanEditHandler handler) + { + Builder.EditHandler = handler; + return this; + } + + public SpanConstructor With(Action generatorConfigurer) + { + generatorConfigurer(Builder.CodeGenerator); + return this; + } + + public SpanConstructor With(Action handlerConfigurer) + { + handlerConfigurer(Builder.EditHandler); + return this; + } + + public static implicit operator Span(SpanConstructor self) + { + return self.Build(); + } + + public SpanConstructor Hidden() + { + Builder.CodeGenerator = SpanCodeGenerator.Null; + return this; + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Framework/VBHtmlCodeParserTestBase.cs b/test/Microsoft.AspNet.Razor.Test/Framework/VBHtmlCodeParserTestBase.cs new file mode 100644 index 0000000000..523f59e785 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Framework/VBHtmlCodeParserTestBase.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.Collections.Generic; +using Microsoft.AspNet.Razor.Parser; + +namespace Microsoft.AspNet.Razor.Test.Framework +{ + public abstract class VBHtmlCodeParserTestBase : CodeParserTestBase + { + protected override ISet KeywordSet + { + get { return VBCodeParser.DefaultKeywords; } + } + + protected override SpanFactory CreateSpanFactory() + { + return SpanFactory.CreateVbHtml(); + } + + public override ParserBase CreateMarkupParser() + { + return new HtmlMarkupParser(); + } + + public override ParserBase CreateCodeParser() + { + return new VBCodeParser(); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Framework/VBHtmlMarkupParserTestBase.cs b/test/Microsoft.AspNet.Razor.Test/Framework/VBHtmlMarkupParserTestBase.cs new file mode 100644 index 0000000000..5f72448cbf --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Framework/VBHtmlMarkupParserTestBase.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.Collections.Generic; +using Microsoft.AspNet.Razor.Parser; + +namespace Microsoft.AspNet.Razor.Test.Framework +{ + public abstract class VBHtmlMarkupParserTestBase : MarkupParserTestBase + { + protected override ISet KeywordSet + { + get { return VBCodeParser.DefaultKeywords; } + } + + protected override SpanFactory CreateSpanFactory() + { + return SpanFactory.CreateVbHtml(); + } + + public override ParserBase CreateMarkupParser() + { + return new HtmlMarkupParser(); + } + + public override ParserBase CreateCodeParser() + { + return new VBCodeParser(); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/CSharpRazorCodeGeneratorTest.cs b/test/Microsoft.AspNet.Razor.Test/Generator/CSharpRazorCodeGeneratorTest.cs new file mode 100644 index 0000000000..3bccf09b43 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Generator/CSharpRazorCodeGeneratorTest.cs @@ -0,0 +1,417 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Generator +{ + public class CSharpRazorCodeGeneratorTest : RazorCodeGeneratorTest + { + protected override string FileExtension + { + get { return "cshtml"; } + } + + protected override string LanguageName + { + get { return "CS"; } + } + + protected override string BaselineExtension + { + get { return "cs"; } + } + + private const string TestPhysicalPath = @"C:\Bar.cshtml"; + private const string TestVirtualPath = "~/Foo/Bar.cshtml"; + + [Fact] + public void ConstructorRequiresNonNullClassName() + { + Assert.ThrowsArgumentNullOrEmptyString(() => new CSharpRazorCodeGenerator(null, TestRootNamespaceName, TestPhysicalPath, CreateHost()), "className"); + } + + [Fact] + public void ConstructorRequiresNonEmptyClassName() + { + Assert.ThrowsArgumentNullOrEmptyString(() => new CSharpRazorCodeGenerator(String.Empty, TestRootNamespaceName, TestPhysicalPath, CreateHost()), "className"); + } + + [Fact] + public void ConstructorRequiresNonNullRootNamespaceName() + { + Assert.ThrowsArgumentNull(() => new CSharpRazorCodeGenerator("Foo", null, TestPhysicalPath, CreateHost()), "rootNamespaceName"); + } + + [Fact] + public void ConstructorAllowsEmptyRootNamespaceName() + { + new CSharpRazorCodeGenerator("Foo", String.Empty, TestPhysicalPath, CreateHost()); + } + + [Fact] + public void ConstructorRequiresNonNullHost() + { + Assert.ThrowsArgumentNull(() => new CSharpRazorCodeGenerator("Foo", TestRootNamespaceName, TestPhysicalPath, null), "host"); + } + + [Theory] + [InlineData("NestedCodeBlocks")] + [InlineData("CodeBlock")] + [InlineData("ExplicitExpression")] + [InlineData("MarkupInCodeBlock")] + [InlineData("Blocks")] + [InlineData("ImplicitExpression")] + [InlineData("Imports")] + [InlineData("ExpressionsInCode")] + [InlineData("FunctionsBlock")] + [InlineData("FunctionsBlock_Tabs")] + [InlineData("Templates")] + [InlineData("Sections")] + [InlineData("RazorComments")] + [InlineData("Helpers")] + [InlineData("HelpersMissingCloseParen")] + [InlineData("HelpersMissingOpenBrace")] + [InlineData("HelpersMissingOpenParen")] + [InlineData("NestedHelpers")] + [InlineData("InlineBlocks")] + [InlineData("NestedHelpers")] + [InlineData("LayoutDirective")] + [InlineData("ConditionalAttributes")] + [InlineData("ResolveUrl")] + public void CSharpCodeGeneratorCorrectlyGeneratesRunTimeCode(string testType) + { + RunTest(testType); + } + + [Fact] + public void CSharpCodeGeneratorCorrectlyGeneratesMappingsForSimpleUnspacedIf() + { + RunTest("SimpleUnspacedIf", + "SimpleUnspacedIf.DesignTime.Tabs", + designTimeMode: true, + tabTest: TabTest.Tabs, + expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(1, 2, 1, 15), + /* 02 */ new GeneratedCodeMapping(3, 13, 7, 3), + }); + } + + [Fact] + public void CSharpCodeGeneratorCorrectlyGeneratesMappingsForRazorCommentsAtDesignTime() + { + RunTest("RazorComments", "RazorComments.DesignTime", designTimeMode: true, tabTest: TabTest.NoTabs, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(4, 3, 3, 6), + /* 02 */ new GeneratedCodeMapping(5, 40, 39, 22), + /* 03 */ new GeneratedCodeMapping(6, 50, 49, 58), + /* 04 */ new GeneratedCodeMapping(12, 3, 3, 24), + /* 05 */ new GeneratedCodeMapping(13, 46, 46, 3), + /* 06 */ new GeneratedCodeMapping(15, 3, 7, 1), + /* 07 */ new GeneratedCodeMapping(15, 8, 8, 1) + }); + } + + [Fact] + public void CSharpCodeGeneratorCorrectlyGenerateMappingForOpenedCurlyIf() + { + OpenedIf(withTabs: true); + } + + [Fact] + public void CSharpCodeGeneratorCorrectlyGenerateMappingForOpenedCurlyIfSpaces() + { + OpenedIf(withTabs: false); + } + + private void OpenedIf(bool withTabs) + { + int tabOffsetForMapping = 0; + + // where the test is running with tabs, the offset into the CS buffer changes for the whitespace mapping + // with spaces we get 7xspace -> offset of 8 (column = offset+1) + // with tabs we get tab + 3 spaces -> offset of 4 chars + 1 = 5 + if (withTabs) + { + tabOffsetForMapping = 3; + } + + RunTest("OpenedIf", + "OpenedIf.DesignTime" + (withTabs ? ".Tabs" : ""), + designTimeMode: true, + tabTest: withTabs ? TabTest.Tabs : TabTest.NoTabs, + spans: new TestSpan[] + { + new TestSpan(SpanKind.Markup, 0, 16), + new TestSpan(SpanKind.Transition, 16, 17), + new TestSpan(SpanKind.Code, 17, 31), + new TestSpan(SpanKind.Markup, 31, 38), + new TestSpan(SpanKind.Code, 38, 40), + new TestSpan(SpanKind.Markup, 40, 47), + new TestSpan(SpanKind.Code, 47, 47), + }, + expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(3, 2, 1, 14), + /* 02 */ new GeneratedCodeMapping(4, 8, 8 - tabOffsetForMapping, 2), + /* 03 */ new GeneratedCodeMapping(5, 8, 8 - tabOffsetForMapping, 0), + }); + } + + [Fact] + public void CSharpCodeGeneratorCorrectlyGeneratesImportStatementsAtDesignTime() + { + RunTest("Imports", "Imports.DesignTime", designTimeMode: true, tabTest: TabTest.NoTabs, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(1, 2, 1, 15), + /* 02 */ new GeneratedCodeMapping(2, 2, 1, 32), + /* 03 */ new GeneratedCodeMapping(3, 2, 1, 12), + /* 04 */ new GeneratedCodeMapping(5, 30, 30, 21), + /* 05 */ new GeneratedCodeMapping(6, 36, 36, 20), + }); + } + + [Fact] + public void CSharpCodeGeneratorCorrectlyGeneratesFunctionsBlocksAtDesignTime() + { + RunTest("FunctionsBlock", + "FunctionsBlock.DesignTime", + designTimeMode: true, + tabTest: TabTest.NoTabs, + expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(1, 13, 13, 4), + /* 02 */ new GeneratedCodeMapping(5, 13, 13, 104), + /* 03 */ new GeneratedCodeMapping(12, 26, 26, 11) + }); + } + + [Fact] + public void CSharpCodeGeneratorCorrectlyGeneratesFunctionsBlocksAtDesignTimeTabs() + { + RunTest("FunctionsBlock", + "FunctionsBlock.DesignTime" + ".Tabs", + designTimeMode: true, + tabTest: TabTest.Tabs, + expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(1, 13, 4, 4), + /* 02 */ new GeneratedCodeMapping(5, 13, 4, 104), + /* 03 */ new GeneratedCodeMapping(12, 26, 14, 11) + }); + } + + [Fact] + public void CSharpCodeGeneratorCorrectlyGeneratesMinimalFunctionsBlocksAtDesignTimeTabs() + { + RunTest("FunctionsBlockMinimal", + "FunctionsBlockMinimal.DesignTime" + ".Tabs", + designTimeMode: true, + tabTest: TabTest.Tabs, + expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(3, 13, 7, 55), + }); + } + + [Fact] + public void CSharpCodeGeneratorCorrectlyGeneratesHiddenSpansWithinCode() + { + RunTest("HiddenSpansInCode", designTimeMode: true, tabTest: TabTest.NoTabs, expectedDesignTimePragmas: new List + { + /* 01 */ new GeneratedCodeMapping(1, 3, 3, 6), + /* 02 */ new GeneratedCodeMapping(2, 6, 6, 5) + }); + } + + [Fact] + public void CSharpCodeGeneratorGeneratesCodeWithParserErrorsInDesignTimeMode() + { + RunTest("ParserError", designTimeMode: true, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(1, 3, 3, 31) + }); + } + + [Fact] + public void CSharpCodeGeneratorCorrectlyGeneratesInheritsAtRuntime() + { + RunTest("Inherits", baselineName: "Inherits.Runtime"); + } + + [Fact] + public void CSharpCodeGeneratorCorrectlyGeneratesInheritsAtDesigntime() + { + RunTest("Inherits", baselineName: "Inherits.Designtime", designTimeMode: true, tabTest: TabTest.NoTabs, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(1, 2, 7, 5), + /* 02 */ new GeneratedCodeMapping(3, 11, 11, 25), + }); + } + + [Fact] + public void CSharpCodeGeneratorCorrectlyGeneratesDesignTimePragmasForUnfinishedExpressionsInCode() + { + RunTest("UnfinishedExpressionInCode", tabTest: TabTest.NoTabs, designTimeMode: true, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(1, 3, 3, 2), + /* 02 */ new GeneratedCodeMapping(2, 2, 7, 9), + /* 03 */ new GeneratedCodeMapping(2, 11, 11, 2) + }); + } + + [Fact] + public void CSharpCodeGeneratorCorrectlyGeneratesDesignTimePragmasForUnfinishedExpressionsInCodeTabs() + { + RunTest("UnfinishedExpressionInCode", + "UnfinishedExpressionInCode.Tabs", + tabTest: TabTest.Tabs, + designTimeMode: true, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(1, 3, 3, 2), + /* 02 */ new GeneratedCodeMapping(2, 2, 7, 9), + /* 03 */ new GeneratedCodeMapping(2, 11, 5, 2) + }); + } + + [Fact] + public void CSharpCodeGeneratorCorrectlyGeneratesDesignTimePragmasMarkupAndExpressions() + { + RunTest("DesignTime", + designTimeMode: true, + tabTest: TabTest.NoTabs, + expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(2, 14, 13, 36), + /* 02 */ new GeneratedCodeMapping(3, 23, 23, 1), + /* 03 */ new GeneratedCodeMapping(3, 28, 28, 15), + /* 04 */ new GeneratedCodeMapping(8, 3, 7, 12), + /* 05 */ new GeneratedCodeMapping(9, 2, 7, 4), + /* 06 */ new GeneratedCodeMapping(9, 15, 15, 3), + /* 07 */ new GeneratedCodeMapping(9, 26, 26, 1), + /* 08 */ new GeneratedCodeMapping(14, 6, 7, 3), + /* 09 */ new GeneratedCodeMapping(17, 9, 24, 7), + /* 10 */ new GeneratedCodeMapping(17, 16, 16, 26), + /* 11 */ new GeneratedCodeMapping(19, 19, 19, 9), + /* 12 */ new GeneratedCodeMapping(21, 1, 1, 1) + }); + } + + + [Fact] + public void CSharpCodeGeneratorCorrectlyGeneratesDesignTimePragmasForImplicitExpressionStartedAtEOF() + { + RunTest("ImplicitExpressionAtEOF", designTimeMode: true, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(3, 2, 7, 0) + }); + } + + [Fact] + public void CSharpCodeGeneratorCorrectlyGeneratesDesignTimePragmasForExplicitExpressionStartedAtEOF() + { + RunTest("ExplicitExpressionAtEOF", designTimeMode: true, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(3, 3, 7, 0) + }); + } + + [Fact] + public void CSharpCodeGeneratorCorrectlyGeneratesDesignTimePragmasForCodeBlockStartedAtEOF() + { + RunTest("CodeBlockAtEOF", designTimeMode: true, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(1, 3, 3, 0) + }); + } + + [Fact] + public void CSharpCodeGeneratorCorrectlyGeneratesDesignTimePragmasForEmptyImplicitExpression() + { + RunTest("EmptyImplicitExpression", designTimeMode: true, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(3, 2, 7, 0) + }); + } + + [Fact] + public void CSharpCodeGeneratorCorrectlyGeneratesDesignTimePragmasForEmptyImplicitExpressionInCode() + { + RunTest("EmptyImplicitExpressionInCode", tabTest: TabTest.NoTabs, designTimeMode: true, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(1, 3, 3, 6), + /* 02 */ new GeneratedCodeMapping(2, 6, 7, 0), + /* 03 */ new GeneratedCodeMapping(2, 6, 6, 2) + }); + } + + [Fact] + public void CSharpCodeGeneratorCorrectlyGeneratesDesignTimePragmasForEmptyImplicitExpressionInCodeTabs() + { + RunTest("EmptyImplicitExpressionInCode", + "EmptyImplicitExpressionInCode.Tabs", + tabTest: TabTest.Tabs, + designTimeMode: true, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(1, 3, 3, 6), + /* 02 */ new GeneratedCodeMapping(2, 6, 7, 0), + /* 03 */ new GeneratedCodeMapping(2, 6, 3, 2) + }); + } + + [Fact] + public void CSharpCodeGeneratorCorrectlyGeneratesDesignTimePragmasForEmptyExplicitExpression() + { + RunTest("EmptyExplicitExpression", designTimeMode: true, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(3, 3, 7, 0) + }); + } + + [Fact] + public void CSharpCodeGeneratorCorrectlyGeneratesDesignTimePragmasForEmptyCodeBlock() + { + RunTest("EmptyCodeBlock", designTimeMode: true, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(3, 3, 3, 0) + }); + } + + [Fact] + public void CSharpCodeGeneratorDoesNotRenderLinePragmasIfGenerateLinePragmasIsSetToFalse() + { + RunTest("NoLinePragmas", generatePragmas: false); + } + + [Fact] + public void CSharpCodeGeneratorRendersHelpersBlockCorrectlyWhenInstanceHelperRequested() + { + RunTest("Helpers", baselineName: "Helpers.Instance", hostConfig: h => h.StaticHelpers = false); + } + + [Fact] + public void CSharpCodeGeneratorCorrectlyInstrumentsRazorCodeWhenInstrumentationRequested() + { + RunTest("Instrumented", hostConfig: host => + { + host.EnableInstrumentation = true; + host.InstrumentedSourceFilePath = String.Format("~/{0}.cshtml", host.DefaultClassName); + }); + } + + [Fact] + public void CSharpCodeGeneratorGeneratesUrlsCorrectlyWithCommentsAndQuotes() + { + RunTest("HtmlCommentWithQuote_Single", + tabTest: TabTest.NoTabs); + + RunTest("HtmlCommentWithQuote_Double", + tabTest: TabTest.NoTabs); + + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/GeneratedCodeMappingTest.cs b/test/Microsoft.AspNet.Razor.Test/Generator/GeneratedCodeMappingTest.cs new file mode 100644 index 0000000000..e9c85f661a --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Generator/GeneratedCodeMappingTest.cs @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Generator; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Generator +{ + public class GeneratedCodeMappingTest + { + [Fact] + public void GeneratedCodeMappingsAreEqualIfDataIsEqual() + { + GeneratedCodeMapping left = new GeneratedCodeMapping(12, 34, 56, 78); + GeneratedCodeMapping right = new GeneratedCodeMapping(12, 34, 56, 78); + Assert.True(left == right); + Assert.True(left.Equals(right)); + Assert.True(right.Equals(left)); + Assert.True(Equals(left, right)); + } + + [Fact] + public void GeneratedCodeMappingsAreNotEqualIfCodeLengthIsNotEqual() + { + GeneratedCodeMapping left = new GeneratedCodeMapping(12, 34, 56, 87); + GeneratedCodeMapping right = new GeneratedCodeMapping(12, 34, 56, 78); + Assert.False(left == right); + Assert.False(left.Equals(right)); + Assert.False(right.Equals(left)); + Assert.False(Equals(left, right)); + } + + [Fact] + public void GeneratedCodeMappingsAreNotEqualIfStartGeneratedColumnIsNotEqual() + { + GeneratedCodeMapping left = new GeneratedCodeMapping(12, 34, 56, 87); + GeneratedCodeMapping right = new GeneratedCodeMapping(12, 34, 65, 87); + Assert.False(left == right); + Assert.False(left.Equals(right)); + Assert.False(right.Equals(left)); + Assert.False(Equals(left, right)); + } + + [Fact] + public void GeneratedCodeMappingsAreNotEqualIfStartColumnIsNotEqual() + { + GeneratedCodeMapping left = new GeneratedCodeMapping(12, 34, 56, 87); + GeneratedCodeMapping right = new GeneratedCodeMapping(12, 43, 56, 87); + Assert.False(left == right); + Assert.False(left.Equals(right)); + Assert.False(right.Equals(left)); + Assert.False(Equals(left, right)); + } + + [Fact] + public void GeneratedCodeMappingsAreNotEqualIfStartLineIsNotEqual() + { + GeneratedCodeMapping left = new GeneratedCodeMapping(12, 34, 56, 87); + GeneratedCodeMapping right = new GeneratedCodeMapping(21, 34, 56, 87); + Assert.False(left == right); + Assert.False(left.Equals(right)); + Assert.False(right.Equals(left)); + Assert.False(Equals(left, right)); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/PaddingTest.cs b/test/Microsoft.AspNet.Razor.Test/Generator/PaddingTest.cs new file mode 100644 index 0000000000..8c8b208ac2 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Generator/PaddingTest.cs @@ -0,0 +1,236 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.IO; +using System.Linq; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Generator +{ + public class PaddingTest + { + [Fact] + public void CalculatePaddingForEmptySpanReturnsZero() + { + RazorEngineHost host = CreateHost(designTime: true); + + Span span = new Span(new SpanBuilder()); + + int padding = CodeGeneratorPaddingHelper.CalculatePadding(host, span, 0); + + Assert.Equal(0, padding); + } + + [Theory] + [InlineData(true, false, 4)] + [InlineData(true, false, 2)] + [InlineData(false, true, 4)] + [InlineData(false, true, 2)] + [InlineData(false, false, 4)] + [InlineData(false, false, 2)] + [InlineData(true, true, 4)] + [InlineData(true, true, 2)] + [InlineData(true, true, 1)] + [InlineData(true, true, 0)] + public void CalculatePaddingForEmptySpanWith4Spaces(bool designTime, bool isIndentingWithTabs, int tabSize) + { + RazorEngineHost host = CreateHost(designTime: designTime, isIndentingWithTabs: isIndentingWithTabs, tabSize: tabSize); + + Span span = GenerateSpan(@" @{", SpanKind.Code, 3, ""); + + int padding = CodeGeneratorPaddingHelper.CalculatePadding(host, span, 0); + + Assert.Equal(6, padding); + } + + [Theory] + [InlineData(true, false, 4)] + [InlineData(true, false, 2)] + [InlineData(false, true, 4)] + [InlineData(false, true, 2)] + [InlineData(false, false, 4)] + [InlineData(false, false, 2)] + [InlineData(true, true, 4)] + [InlineData(true, true, 2)] + [InlineData(true, true, 1)] + [InlineData(true, true, 0)] + public void CalculatePaddingForIfSpanWith4Spaces(bool designTime, bool isIndentingWithTabs, int tabSize) + { + RazorEngineHost host = CreateHost(designTime: designTime, isIndentingWithTabs: isIndentingWithTabs, tabSize: tabSize); + + Span span = GenerateSpan(@" @if (true)", SpanKind.Code, 2, "if (true)"); + + int padding = CodeGeneratorPaddingHelper.CalculatePadding(host, span, 1); + + Assert.Equal(4, padding); + } + + [Theory] + [InlineData(true, false, 4, 0, 4)] + [InlineData(true, false, 2, 0, 4)] + [InlineData(true, true, 4, 1, 0)] + [InlineData(true, true, 2, 2, 0)] + [InlineData(true, true, 1, 4, 0)] + [InlineData(true, true, 0, 4, 0)] + [InlineData(true, true, 3, 1, 1)] + + // in non design time mode padding falls back to spaces to keep runtime code identical to v2 code. + [InlineData(false, true, 4, 0, 5)] + [InlineData(false, true, 2, 0, 5)] + + [InlineData(false, false, 4, 0, 5)] + [InlineData(false, false, 2, 0, 5)] + public void VerifyPaddingForIfSpanWith4Spaces(bool designTime, bool isIndentingWithTabs, int tabSize, int numTabs, int numSpaces) + { + RazorEngineHost host = CreateHost(designTime: designTime, isIndentingWithTabs: isIndentingWithTabs, tabSize: tabSize); + + // no new lines involved + Span span = GenerateSpan(" @if (true)", SpanKind.Code, 2, "if (true)"); + + int generatedStart = 1; + string code = " if (true)"; + int paddingCharCount; + + string padded = CodeGeneratorPaddingHelper.PadStatement(host, code, span, ref generatedStart, out paddingCharCount); + + VerifyPadded(numTabs, numSpaces, code, padded, paddingCharCount); + + // with new lines involved + Span newLineSpan = GenerateSpan("\t
\r\n @if (true)", SpanKind.Code, 3, "if (true)"); + + string newLinePadded = CodeGeneratorPaddingHelper.PadStatement(host, code, span, ref generatedStart, out paddingCharCount); + + VerifyPadded(numTabs, numSpaces, code, newLinePadded, paddingCharCount); + } + + [Theory] + [InlineData(true, false, 4, 0, 8)] + [InlineData(true, false, 2, 0, 4)] + [InlineData(true, true, 4, 2, 0)] + [InlineData(true, true, 2, 2, 0)] + [InlineData(true, true, 1, 2, 0)] + [InlineData(true, true, 0, 2, 0)] + [InlineData(true, true, 3, 2, 0)] + + // in non design time mode padding falls back to spaces to keep runtime code identical to v2 code. + [InlineData(false, true, 4, 0, 9)] + [InlineData(false, true, 2, 0, 5)] + + [InlineData(false, false, 4, 0, 9)] + [InlineData(false, false, 2, 0, 5)] + public void VerifyPaddingForIfSpanWithTwoTabs(bool designTime, bool isIndentingWithTabs, int tabSize, int numTabs, int numSpaces) + { + RazorEngineHost host = CreateHost(designTime: designTime, isIndentingWithTabs: isIndentingWithTabs, tabSize: tabSize); + + // no new lines involved + Span span = GenerateSpan("\t\t@if (true)", SpanKind.Code, 2, "if (true)"); + + int generatedStart = 1; + string code = " if (true)"; + int paddingCharCount; + + string padded = CodeGeneratorPaddingHelper.PadStatement(host, code, span, ref generatedStart, out paddingCharCount); + + VerifyPadded(numTabs, numSpaces, code, padded, paddingCharCount); + + // with new lines involved + Span newLineSpan = GenerateSpan("\t
\r\n\t\t@if (true)", SpanKind.Code, 3, "if (true)"); + + string newLinePadded = CodeGeneratorPaddingHelper.PadStatement(host, code, span, ref generatedStart, out paddingCharCount); + + VerifyPadded(numTabs, numSpaces, code, newLinePadded, paddingCharCount); + } + + [Theory] + [InlineData(true, false, 4, 0, 8)] + [InlineData(true, false, 2, 0, 4)] + [InlineData(true, true, 4, 2, 0)] + [InlineData(true, true, 2, 2, 0)] + [InlineData(true, true, 1, 2, 0)] + [InlineData(true, true, 0, 2, 0)] + + // in non design time mode padding falls back to spaces to keep runtime code identical to v2 code. + [InlineData(false, true, 4, 0, 9)] + [InlineData(false, true, 2, 0, 5)] + + [InlineData(false, false, 4, 0, 9)] + [InlineData(false, false, 2, 0, 5)] + public void CalculatePaddingForOpenedIf(bool designTime, bool isIndentingWithTabs, int tabSize, int numTabs, int numSpaces) + { + RazorEngineHost host = CreateHost(designTime: designTime, isIndentingWithTabs: isIndentingWithTabs, tabSize: tabSize); + + string text = "\r\n\r\n\r\n\t\t@if (true) { \r\n\r\n"; + + Span span = GenerateSpan(text, SpanKind.Code, 3, "if (true) { \r\n"); + + int generatedStart = 1; + string code = " if (true) { \r\n"; + int paddingCharCount; + string padded = CodeGeneratorPaddingHelper.PadStatement(host, code, span, ref generatedStart, out paddingCharCount); + + VerifyPadded(numTabs, numSpaces, code, padded, paddingCharCount); + } + + private static void VerifyPadded(int numTabs, int numSpaces, string code, string padded, int paddingCharCount) + { + Assert.Equal(numTabs + numSpaces + code.Length, padded.Length); + + if (numTabs > 0 || numSpaces > 0) + { + Assert.True(padded.Length > numTabs + numSpaces, "padded string too short"); + } + + for (int i = 0; i < numTabs; i++) + { + Assert.Equal('\t', padded[i]); + } + + for (int i = numTabs; i < numTabs + numSpaces; i++) + { + Assert.Equal(' ', padded[i]); + } + + Assert.Equal(numSpaces + numTabs, paddingCharCount); + } + + private static RazorEngineHost CreateHost(bool designTime, bool isIndentingWithTabs = false, int tabSize = 4) + { + return new RazorEngineHost(new CSharpRazorCodeLanguage()) + { + DesignTimeMode = designTime, + IsIndentingWithTabs = isIndentingWithTabs, + TabSize = tabSize, + }; + } + + private static Span GenerateSpan(string text, SpanKind spanKind, int spanIndex, string spanText) + { + Span[] spans = GenerateSpans(text, spanKind, spanIndex, spanText); + + return spans[spanIndex]; + } + + private static Span[] GenerateSpans(string text, SpanKind spanKind, int spanIndex, string spanText) + { + Assert.True(spanIndex > 0); + + RazorParser parser = new RazorParser(new CSharpCodeParser(), new HtmlMarkupParser()); + + Span[] spans; + + using (var reader = new StringReader(text)) + { + ParserResults results = parser.Parse(reader); + spans = results.Document.Flatten().ToArray(); + } + + Assert.True(spans.Length > spanIndex); + Assert.Equal(spanKind, spans[spanIndex].Kind); + Assert.Equal(spanText, spans[spanIndex].Content); + + return spans; + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/RazorCodeGeneratorTest.cs b/test/Microsoft.AspNet.Razor.Test/Generator/RazorCodeGeneratorTest.cs new file mode 100644 index 0000000000..9fc20ae4e0 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Generator/RazorCodeGeneratorTest.cs @@ -0,0 +1,245 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +//#define GENERATE_BASELINES + +using System.CodeDom; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Test.Utils; +using System.Web.WebPages.TestUtils; +using Microsoft.TestCommon; +using System; + +namespace Microsoft.AspNet.Razor.Test.Generator +{ + public abstract class RazorCodeGeneratorTest + where TLanguage : RazorCodeLanguage, new() + { + protected static readonly string TestRootNamespaceName = "TestOutput"; + + protected abstract string FileExtension { get; } + protected abstract string LanguageName { get; } + protected abstract string BaselineExtension { get; } + + protected RazorEngineHost CreateHost() + { + return new RazorEngineHost(new TLanguage()); + } + + protected void RunTest(string name, + string baselineName = null, + bool generatePragmas = true, + bool designTimeMode = false, + IList expectedDesignTimePragmas = null, + TestSpan[] spans = null, + TabTest tabTest = TabTest.Both, + Action hostConfig = null) + { + bool testRun = false; + + if ((tabTest & TabTest.Tabs) == TabTest.Tabs) + { + // CodeDOM will inject its strings into the generated file header, so we force English. + using (new CultureReplacer()) + { + RunTestInternal( + name: name, + baselineName: baselineName, + generatePragmas: generatePragmas, + designTimeMode: designTimeMode, + expectedDesignTimePragmas: expectedDesignTimePragmas, + spans: spans, + withTabs: true, + hostConfig: hostConfig); + } + + testRun = true; + } + + if ((tabTest & TabTest.NoTabs) == TabTest.NoTabs) + { + // CodeDOM will inject its strings into the generated file header, so we force English. + using (new CultureReplacer()) + { + RunTestInternal( + name: name, + baselineName: baselineName, + generatePragmas: generatePragmas, + designTimeMode: designTimeMode, + expectedDesignTimePragmas: expectedDesignTimePragmas, + spans: spans, + withTabs: false, + hostConfig: hostConfig); + } + + testRun = true; + } + + Assert.True(testRun, "No test was run because TabTest is not set correctly"); + } + + private void RunTestInternal(string name, + string baselineName, + bool generatePragmas, + bool designTimeMode, + IList expectedDesignTimePragmas, + TestSpan[] spans, + bool withTabs, + Action hostConfig) + { + // Load the test files + if (baselineName == null) + { + baselineName = name; + } + + string source = TestFile.Create(String.Format("CodeGenerator.{1}.Source.{0}.{2}", name, LanguageName, FileExtension)).ReadAllText(); + string expectedOutput = TestFile.Create(String.Format("CodeGenerator.{1}.Output.{0}.{2}", baselineName, LanguageName, BaselineExtension)).ReadAllText(); + + // Set up the host and engine + RazorEngineHost host = CreateHost(); + host.NamespaceImports.Add("System"); + host.DesignTimeMode = designTimeMode; + host.StaticHelpers = true; + host.DefaultClassName = name; + + // Add support for templates, etc. + host.GeneratedClassContext = new GeneratedClassContext(GeneratedClassContext.DefaultExecuteMethodName, + GeneratedClassContext.DefaultWriteMethodName, + GeneratedClassContext.DefaultWriteLiteralMethodName, + "WriteTo", + "WriteLiteralTo", + "Template", + "DefineSection", + "BeginContext", + "EndContext") + { + LayoutPropertyName = "Layout", + ResolveUrlMethodName = "Href" + }; + if (hostConfig != null) + { + hostConfig(host); + } + + host.IsIndentingWithTabs = withTabs; + + RazorTemplateEngine engine = new RazorTemplateEngine(host); + + // Generate code for the file + GeneratorResults results = null; + using (StringTextBuffer buffer = new StringTextBuffer(source)) + { + results = engine.GenerateCode(buffer, className: name, rootNamespace: TestRootNamespaceName, sourceFileName: generatePragmas ? String.Format("{0}.{1}", name, FileExtension) : null); + } + + // Generate code + CodeCompileUnit ccu = results.GeneratedCode; + CodeDomProvider codeProvider = (CodeDomProvider)Activator.CreateInstance(host.CodeLanguage.CodeDomProviderType); + + CodeGeneratorOptions options = new CodeGeneratorOptions(); + + // Both run-time and design-time use these settings. See: + // * $/Dev10/pu/SP_WebTools/venus/html/Razor/Impl/RazorCodeGenerator.cs:204 + // * $/Dev10/Releases/RTMRel/ndp/fx/src/xsp/System/Web/Compilation/BuildManagerHost.cs:373 + options.BlankLinesBetweenMembers = false; + options.IndentString = String.Empty; + + StringBuilder output = new StringBuilder(); + using (StringWriter writer = new StringWriter(output)) + { + codeProvider.GenerateCodeFromCompileUnit(ccu, writer, options); + } + + WriteBaseline(String.Format(@"test\Microsoft.AspNet.Razor.Test\TestFiles\CodeGenerator\{0}\Output\{1}.{2}", LanguageName, baselineName, BaselineExtension), MiscUtils.StripRuntimeVersion(output.ToString())); + +#if !GENERATE_BASELINES + string textOutput = MiscUtils.StripRuntimeVersion(output.ToString()); + + //// Verify code against baseline + Assert.Equal(expectedOutput, textOutput); +#endif + + IEnumerable generatedSpans = results.Document.Flatten(); + + foreach (var span in generatedSpans) + { + VerifyNoBrokenEndOfLines(span.Content); + } + + // Verify design-time pragmas + if (designTimeMode) + { + if (spans != null) + { + Assert.Equal(spans, generatedSpans.Select(span => new TestSpan(span)).ToArray()); + } + + if (expectedDesignTimePragmas != null) + { + Assert.True(results.DesignTimeLineMappings != null && results.DesignTimeLineMappings.Count > 0); + + Assert.Equal(expectedDesignTimePragmas.Count, results.DesignTimeLineMappings.Count); + + Assert.Equal( + expectedDesignTimePragmas.ToArray(), + results.DesignTimeLineMappings + .OrderBy(p => p.Key) + .Select(p => p.Value) + .ToArray()); + } + } + } + + [Conditional("GENERATE_BASELINES")] + private void WriteBaseline(string baselineFile, string output) + { + string root = RecursiveFind("Runtime.sln", Path.GetFullPath(".")); + string baselinePath = Path.Combine(root, baselineFile); + + // Update baseline + // IMPORTANT! Replace this path with the local path on your machine to the baseline files! + if (File.Exists(baselinePath)) + { + File.Delete(baselinePath); + } + File.WriteAllText(baselinePath, output.ToString()); + } + + private string RecursiveFind(string path, string start) + { + string test = Path.Combine(start, path); + if (File.Exists(test)) + { + return start; + } + else + { + return RecursiveFind(path, new DirectoryInfo(start).Parent.FullName); + } + } + + private void VerifyNoBrokenEndOfLines(string text) + { + for (int i = 0; i < text.Length; i++) + { + if (text[i] == '\r') + { + Assert.True(text.Length > i + 1); + Assert.Equal('\n', text[i + 1]); + } + else if (text[i] == '\n') + { + Assert.True(i > 0); + Assert.Equal('\r', text[i - 1]); + } + } + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/TabTest.cs b/test/Microsoft.AspNet.Razor.Test/Generator/TabTest.cs new file mode 100644 index 0000000000..73f40ac3dd --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Generator/TabTest.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +namespace Microsoft.AspNet.Razor.Test.Generator +{ + [Flags] + public enum TabTest + { + NoTabs = 1, + Tabs = 2, + Both = 3 + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/TestSpan.cs b/test/Microsoft.AspNet.Razor.Test/Generator/TestSpan.cs new file mode 100644 index 0000000000..45a3254989 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Generator/TestSpan.cs @@ -0,0 +1,59 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; + +namespace Microsoft.AspNet.Razor.Test.Generator +{ + public class TestSpan + { + /// + /// Test span to simplify the generation of the actual Span in test initializer. + /// + /// Span kind + /// Zero indexed start char index in the buffer. + /// End Column, if the text length is zero Start == End. + public TestSpan(SpanKind kind, int start, int end) + { + Kind = kind; + Start = start; + End = end; + } + + public TestSpan(Span span) + : this(span.Kind, + span.Start.AbsoluteIndex, + span.Start.AbsoluteIndex + span.Length) + { + } + + public SpanKind Kind { get; private set; } + public int Start { get; private set; } + public int End { get; private set; } + + public override string ToString() + { + return String.Format("{0}: {1}-{2}", Kind, Start, End); + } + + public override bool Equals(object obj) + { + TestSpan other = obj as TestSpan; + + if (other != null) + { + return (Kind == other.Kind) && + (Start == other.Start) && + (End == other.End); + } + + return false; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/VBRazorCodeGeneratorTest.cs b/test/Microsoft.AspNet.Razor.Test/Generator/VBRazorCodeGeneratorTest.cs new file mode 100644 index 0000000000..04ba001dce --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Generator/VBRazorCodeGeneratorTest.cs @@ -0,0 +1,281 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Generator +{ + public class VBRazorCodeGeneratorTest : RazorCodeGeneratorTest + { + private const string TestPhysicalPath = @"C:\Bar.vbhtml"; + private const string TestVirtualPath = "~/Foo/Bar.vbhtml"; + + protected override string FileExtension + { + get { return "vbhtml"; } + } + + protected override string LanguageName + { + get { return "VB"; } + } + + protected override string BaselineExtension + { + get { return "vb"; } + } + + [Fact] + public void ConstructorRequiresNonNullClassName() + { + Assert.ThrowsArgumentNullOrEmptyString(() => new VBRazorCodeGenerator(null, TestRootNamespaceName, TestPhysicalPath, CreateHost()), "className"); + } + + [Fact] + public void ConstructorRequiresNonEmptyClassName() + { + Assert.ThrowsArgumentNullOrEmptyString(() => new VBRazorCodeGenerator(String.Empty, TestRootNamespaceName, TestPhysicalPath, CreateHost()), "className"); + } + + [Fact] + public void ConstructorRequiresNonNullRootNamespaceName() + { + Assert.ThrowsArgumentNull(() => new VBRazorCodeGenerator("Foo", null, TestPhysicalPath, CreateHost()), "rootNamespaceName"); + } + + [Fact] + public void ConstructorAllowsEmptyRootNamespaceName() + { + new VBRazorCodeGenerator("Foo", String.Empty, TestPhysicalPath, CreateHost()); + } + + [Fact] + public void ConstructorRequiresNonNullHost() + { + Assert.ThrowsArgumentNull(() => new VBRazorCodeGenerator("Foo", TestRootNamespaceName, TestPhysicalPath, null), "host"); + } + + [Theory] + [InlineData("NestedCodeBlocks")] + [InlineData("NestedCodeBlocks")] + [InlineData("CodeBlock")] + [InlineData("ExplicitExpression")] + [InlineData("MarkupInCodeBlock")] + [InlineData("Blocks")] + [InlineData("ImplicitExpression")] + [InlineData("Imports")] + [InlineData("ExpressionsInCode")] + [InlineData("FunctionsBlock")] + [InlineData("Options")] + [InlineData("Templates")] + [InlineData("RazorComments")] + [InlineData("Sections")] + [InlineData("EmptySection")] // this scenario causes a crash in Razor V2.0 + [InlineData("Helpers")] + [InlineData("HelpersMissingCloseParen")] + [InlineData("HelpersMissingOpenParen")] + [InlineData("NestedHelpers")] + [InlineData("LayoutDirective")] + [InlineData("ConditionalAttributes")] + [InlineData("ResolveUrl")] + public void VBCodeGeneratorCorrectlyGeneratesRunTimeCode(string testName) + { + RunTest(testName); + } + + [Fact] + public void VBCodeGeneratorCorrectlyGeneratesMappingsForRazorCommentsAtDesignTime() + { + // (4, 6) -> (?, 6) [6] + // ( 5, 40) -> (?, 39) [2] + // ( 8, 6) -> (?, 6) [33] + // ( 9, 46) -> (?, 46) [3] + // ( 12, 3) -> (?, 7) [3] + // ( 12, 8) -> (?, 8) [1] + RunTest("RazorComments", "RazorComments.DesignTime", designTimeMode: true, tabTest: TabTest.NoTabs, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(4, 6, 6, 6), + /* 02 */ new GeneratedCodeMapping(5, 40, 39, 2), + /* 03 */ new GeneratedCodeMapping(8, 6, 6, 33), + /* 04 */ new GeneratedCodeMapping(9, 46, 46, 3), + /* 05 */ new GeneratedCodeMapping(12, 3, 7, 1), + /* 06 */ new GeneratedCodeMapping(12, 8, 8, 1) + }); + } + + [Fact] + public void VBCodeGeneratorCorrectlyGeneratesHelperMissingNameAtDesignTime() + { + RunTest("HelpersMissingName", designTimeMode: true); + } + + [Fact] + public void VBCodeGeneratorCorrectlyGeneratesImportStatementsAtDesignTimeButCannotWrapPragmasAroundImportStatement() + { + RunTest("Imports", "Imports.DesignTime", designTimeMode: true, tabTest: TabTest.NoTabs, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(1, 2, 1, 19), + /* 02 */ new GeneratedCodeMapping(2, 2, 1, 36), + /* 03 */ new GeneratedCodeMapping(3, 2, 1, 16), + /* 04 */ new GeneratedCodeMapping(5, 30, 30, 22), + /* 05 */ new GeneratedCodeMapping(6, 36, 36, 21), + }); + } + + [Fact] + public void VBCodeGeneratorCorrectlyGeneratesFunctionsBlocksAtDesignTime() + { + RunTest("FunctionsBlock", "FunctionsBlock.DesignTime", designTimeMode: true, tabTest: TabTest.NoTabs, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(1, 11, 11, 4), + /* 02 */ new GeneratedCodeMapping(5, 11, 11, 129), + /* 03 */ new GeneratedCodeMapping(12, 26, 26, 11) + }); + } + + [Fact] + public void VBCodeGeneratorCorrectlyGeneratesFunctionsBlocksAtDesignTimeTabs() + { + RunTest("FunctionsBlock", "FunctionsBlock.DesignTime.Tabs", designTimeMode: true, tabTest: TabTest.Tabs, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(1, 11, 5, 4), + /* 02 */ new GeneratedCodeMapping(5, 11, 5, 129), + /* 03 */ new GeneratedCodeMapping(12, 26, 14, 11) + }); + } + + [Fact] + public void VBCodeGeneratorGeneratesCodeWithParserErrorsInDesignTimeMode() + { + RunTest("ParserError", designTimeMode: true, tabTest: TabTest.NoTabs, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(1, 6, 6, 16) + }); + } + + [Fact] + public void VBCodeGeneratorCorrectlyGeneratesInheritsAtRuntime() + { + RunTest("Inherits", baselineName: "Inherits.Runtime"); + } + + [Fact] + public void VBCodeGeneratorCorrectlyGeneratesInheritsAtDesigntime() + { + RunTest("Inherits", baselineName: "Inherits.Designtime", designTimeMode: true, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(1, 11, 25, 27) + }); + } + + [Fact] + public void VBCodeGeneratorCorrectlyGeneratesDesignTimePragmasForUnfinishedExpressionsInCode() + { + RunTest("UnfinishedExpressionInCode", designTimeMode: true, tabTest: TabTest.NoTabs, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(1, 6, 6, 2), + /* 02 */ new GeneratedCodeMapping(2, 2, 7, 9), + /* 03 */ new GeneratedCodeMapping(2, 11, 11, 2) + }); + } + + [Fact] + public void VBCodeGeneratorCorrectlyGeneratesDesignTimePragmasMarkupAndExpressions() + { + RunTest("DesignTime", designTimeMode: true, tabTest: TabTest.NoTabs, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(2, 14, 13, 17), + /* 02 */ new GeneratedCodeMapping(3, 20, 20, 1), + /* 03 */ new GeneratedCodeMapping(3, 25, 25, 20), + /* 04 */ new GeneratedCodeMapping(8, 3, 7, 12), + /* 05 */ new GeneratedCodeMapping(9, 2, 7, 4), + /* 06 */ new GeneratedCodeMapping(9, 16, 16, 3), + /* 07 */ new GeneratedCodeMapping(9, 27, 27, 1), + /* 08 */ new GeneratedCodeMapping(14, 6, 7, 3), + /* 09 */ new GeneratedCodeMapping(17, 9, 24, 5), + /* 10 */ new GeneratedCodeMapping(17, 14, 14, 28), + /* 11 */ new GeneratedCodeMapping(19, 20, 20, 14) + }); + } + + [Fact] + public void VBCodeGeneratorCorrectlyGeneratesDesignTimePragmasForImplicitExpressionStartedAtEOF() + { + RunTest("ImplicitExpressionAtEOF", designTimeMode: true, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(3, 2, 7, 0) + }); + } + + [Fact] + public void VBCodeGeneratorCorrectlyGeneratesDesignTimePragmasForExplicitExpressionStartedAtEOF() + { + RunTest("ExplicitExpressionAtEOF", designTimeMode: true, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(3, 3, 7, 0) + }); + } + + [Fact] + public void VBCodeGeneratorCorrectlyGeneratesDesignTimePragmasForCodeBlockStartedAtEOF() + { + RunTest("CodeBlockAtEOF", designTimeMode: true, tabTest: TabTest.NoTabs, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(3, 6, 6, 0) + }); + } + + [Fact] + public void VBCodeGeneratorCorrectlyGeneratesDesignTimePragmasForEmptyImplicitExpression() + { + RunTest("EmptyImplicitExpression", designTimeMode: true, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(3, 2, 7, 0) + }); + } + + [Fact] + public void VBCodeGeneratorCorrectlyGeneratesDesignTimePragmasForEmptyImplicitExpressionInCode() + { + RunTest("EmptyImplicitExpressionInCode", designTimeMode: true, tabTest: TabTest.NoTabs, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(1, 6, 6, 6), + /* 02 */ new GeneratedCodeMapping(2, 6, 7, 0), + /* 03 */ new GeneratedCodeMapping(2, 6, 6, 2) + }); + } + + [Fact] + public void VBCodeGeneratorCorrectlyGeneratesDesignTimePragmasForEmptyExplicitExpression() + { + RunTest("EmptyExplicitExpression", designTimeMode: true, expectedDesignTimePragmas: new List() + { + /* 01 */ new GeneratedCodeMapping(3, 3, 7, 0) + }); + } + + [Fact] + public void VBCodeGeneratorDoesNotRenderLinePragmasIfGenerateLinePragmasIsSetToFalse() + { + RunTest("NoLinePragmas", generatePragmas: false); + } + + [Fact] + public void VBCodeGeneratorRendersHelpersBlockCorrectlyWhenInstanceHelperRequested() + { + RunTest("Helpers", baselineName: "Helpers.Instance", hostConfig: h => h.StaticHelpers = false); + } + + [Fact] + public void VBCodeGeneratorCorrectlyInstrumentsRazorCodeWhenInstrumentationRequested() + { + RunTest("Instrumented", hostConfig: host => + { + host.EnableInstrumentation = true; + host.InstrumentedSourceFilePath = String.Format("~/{0}.vbhtml", host.DefaultClassName); + }); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Microsoft.AspNet.Razor.Test.csproj b/test/Microsoft.AspNet.Razor.Test/Microsoft.AspNet.Razor.Test.csproj new file mode 100644 index 0000000000..a04a33cb04 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Microsoft.AspNet.Razor.Test.csproj @@ -0,0 +1,468 @@ + + + + {0BB62A1D-E6B5-49FA-9E3C-6AF679A66DFE} + Library + Properties + Microsoft.AspNet.Razor.Test + Microsoft.AspNet.Razor.Test + ..\..\bin\$(Configuration)\Test\ + 0618 + v4.5.1 + + + + false + + + + + False + ..\..\packages\Moq.4.0.10827\lib\NET40\Moq.dll + + + + 3.5 + + + False + ..\..\packages\xunit.1.9.1\lib\net20\xunit.dll + unused + + + False + ..\..\packages\xunit.extensions.1.9.1\lib\net20\xunit.extensions.dll + unused + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Code + + + Code + + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Code + + + + + + + + + + {e75d8296-3ba6-4e67-afeb-90ff77460b15} + Microsoft.AspNet.Razor + + + {FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0} + Microsoft.TestCommon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/BlockTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/BlockTest.cs new file mode 100644 index 0000000000..671f73ec30 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/BlockTest.cs @@ -0,0 +1,124 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.Linq; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using System.Web.WebPages.TestUtils; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser +{ + public class BlockTest + { + [Fact] + public void ConstructorWithBlockBuilderSetsParent() + { + // Arrange + BlockBuilder builder = new BlockBuilder() { Type = BlockType.Comment }; + Span span = new SpanBuilder() { Kind = SpanKind.Code }.Build(); + builder.Children.Add(span); + + // Act + Block block = builder.Build(); + + // Assert + Assert.Same(block, span.Parent); + } + + [Fact] + public void ConstructorCopiesBasicValuesFromBlockBuilder() + { + // Arrange + BlockBuilder builder = new BlockBuilder() + { + Name = "Foo", + Type = BlockType.Helper + }; + + // Act + Block actual = builder.Build(); + + // Assert + Assert.Equal("Foo", actual.Name); + Assert.Equal(BlockType.Helper, actual.Type); + } + + [Fact] + public void ConstructorTransfersInstanceOfCodeGeneratorFromBlockBuilder() + { + // Arrange + IBlockCodeGenerator expected = new ExpressionCodeGenerator(); + BlockBuilder builder = new BlockBuilder() + { + Type = BlockType.Helper, + CodeGenerator = expected + }; + + // Act + Block actual = builder.Build(); + + // Assert + Assert.Same(expected, actual.CodeGenerator); + } + + [Fact] + public void ConstructorTransfersChildrenFromBlockBuilder() + { + // Arrange + Span expected = new SpanBuilder() { Kind = SpanKind.Code }.Build(); + BlockBuilder builder = new BlockBuilder() + { + Type = BlockType.Functions + }; + builder.Children.Add(expected); + + // Act + Block block = builder.Build(); + + // Assert + Assert.Same(expected, block.Children.Single()); + } + + [Fact] + public void LocateOwnerReturnsNullIfNoSpanReturnsTrueForOwnsSpan() + { + // Arrange + var factory = SpanFactory.CreateCsHtml(); + Block block = new MarkupBlock( + factory.Markup("Foo "), + new StatementBlock( + factory.CodeTransition(), + factory.Code("bar").AsStatement()), + factory.Markup(" Baz")); + TextChange change = new TextChange(128, 1, new StringTextBuffer("Foo @bar Baz"), 1, new StringTextBuffer("Foo @bor Baz")); + + // Act + Span actual = block.LocateOwner(change); + + // Assert + Assert.Null(actual); + } + + [Fact] + public void LocateOwnerReturnsNullIfChangeCrossesMultipleSpans() + { + // Arrange + var factory = SpanFactory.CreateCsHtml(); + Block block = new MarkupBlock( + factory.Markup("Foo "), + new StatementBlock( + factory.CodeTransition(), + factory.Code("bar").AsStatement()), + factory.Markup(" Baz")); + TextChange change = new TextChange(4, 10, new StringTextBuffer("Foo @bar Baz"), 10, new StringTextBuffer("Foo @bor Baz")); + + // Act + Span actual = block.LocateOwner(change); + + // Assert + Assert.Null(actual); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpAutoCompleteTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpAutoCompleteTest.cs new file mode 100644 index 0000000000..01f69de471 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpAutoCompleteTest.cs @@ -0,0 +1,175 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.CSharp +{ + public class CSharpAutoCompleteTest : CsHtmlCodeParserTestBase + { + [Fact] + public void FunctionsDirectiveAutoCompleteAtEOF() + { + ParseBlockTest("@functions{", + new FunctionsBlock( + Factory.CodeTransition("@") + .Accepts(AcceptedCharacters.None), + Factory.MetaCode("functions{") + .Accepts(AcceptedCharacters.None), + Factory.EmptyCSharp() + .AsFunctionsBody() + .With(new AutoCompleteEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString) + { + AutoCompleteString = "}" + })), + new RazorError(String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, "functions", "}", "{"), + 1, 0, 1)); + } + + [Fact] + public void HelperDirectiveAutoCompleteAtEOF() + { + ParseBlockTest("@helper Strong(string value) {", + new HelperBlock(new HelperCodeGenerator(new LocationTagged("Strong(string value) {", 8, 0, 8), headerComplete: true), + Factory.CodeTransition(), + Factory.MetaCode("helper ") + .Accepts(AcceptedCharacters.None), + Factory.Code("Strong(string value) {") + .Hidden() + .Accepts(AcceptedCharacters.None), + new StatementBlock( + Factory.EmptyCSharp() + .AsStatement() + .With(new AutoCompleteEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString) { AutoCompleteString = "}" }) + ) + ), + new RazorError(String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, "helper", "}", "{"), + 1, 0, 1)); + } + + [Fact] + public void SectionDirectiveAutoCompleteAtEOF() + { + ParseBlockTest("@section Header {", + new SectionBlock(new SectionCodeGenerator("Header"), + Factory.CodeTransition(), + Factory.MetaCode("section Header {") + .AutoCompleteWith("}", atEndOfSpan: true) + .Accepts(AcceptedCharacters.Any), + new MarkupBlock()), + new RazorError( + String.Format(RazorResources.ParseError_Expected_X, "}"), + 17, 0, 17)); + } + + [Fact] + public void VerbatimBlockAutoCompleteAtEOF() + { + ParseBlockTest("@{", + new StatementBlock( + Factory.CodeTransition(), + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.EmptyCSharp() + .AsStatement() + .With(new AutoCompleteEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString) { AutoCompleteString = "}" }) + ), + new RazorError(String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, RazorResources.BlockName_Code, "}", "{"), + 1, 0, 1)); + } + + [Fact] + public void FunctionsDirectiveAutoCompleteAtStartOfFile() + { + ParseBlockTest("@functions{" + Environment.NewLine + + "foo", + new FunctionsBlock( + Factory.CodeTransition("@") + .Accepts(AcceptedCharacters.None), + Factory.MetaCode("functions{") + .Accepts(AcceptedCharacters.None), + Factory.Code("\r\nfoo") + .AsFunctionsBody() + .With(new AutoCompleteEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString) + { + AutoCompleteString = "}" + })), + new RazorError(String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, "functions", "}", "{"), + 1, 0, 1)); + } + + [Fact] + public void HelperDirectiveAutoCompleteAtStartOfFile() + { + ParseBlockTest("@helper Strong(string value) {" + Environment.NewLine + + "

", + new HelperBlock(new HelperCodeGenerator(new LocationTagged("Strong(string value) {", 8, 0, 8), headerComplete: true), + Factory.CodeTransition(), + Factory.MetaCode("helper ") + .Accepts(AcceptedCharacters.None), + Factory.Code("Strong(string value) {") + .Hidden() + .Accepts(AcceptedCharacters.None), + new StatementBlock( + Factory.Code("\r\n") + .AsStatement() + .With(new AutoCompleteEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString) { AutoCompleteString = "}" }), + new MarkupBlock( + Factory.Markup(@"

") + .With(new MarkupCodeGenerator()) + .Accepts(AcceptedCharacters.None) + ), + Factory.Span(SpanKind.Code, new CSharpSymbol(Factory.LocationTracker.CurrentLocation, String.Empty, CSharpSymbolType.Unknown)) + .With(new StatementCodeGenerator()) + ) + ), + new RazorError(String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, "helper", "}", "{"), + 1, 0, 1)); + } + + [Fact] + public void SectionDirectiveAutoCompleteAtStartOfFile() + { + ParseBlockTest("@section Header {" + Environment.NewLine + + "

Foo

", + new SectionBlock(new SectionCodeGenerator("Header"), + Factory.CodeTransition(), + Factory.MetaCode("section Header {") + .AutoCompleteWith("}", atEndOfSpan: true) + .Accepts(AcceptedCharacters.Any), + new MarkupBlock( + Factory.Markup("\r\n

Foo

"))), + new RazorError(String.Format(RazorResources.ParseError_Expected_X, "}"), + 29, 1, 10)); + } + + [Fact] + public void VerbatimBlockAutoCompleteAtStartOfFile() + { + ParseBlockTest("@{" + Environment.NewLine + + "

", + new StatementBlock( + Factory.CodeTransition(), + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code("\r\n") + .AsStatement() + .With(new AutoCompleteEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString) { AutoCompleteString = "}" }), + new MarkupBlock( + Factory.Markup(@"

") + .With(new MarkupCodeGenerator()) + .Accepts(AcceptedCharacters.None) + ), + Factory.Span(SpanKind.Code, new CSharpSymbol(Factory.LocationTracker.CurrentLocation, String.Empty, CSharpSymbolType.Unknown)) + .With(new StatementCodeGenerator()) + ), + new RazorError(String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, RazorResources.BlockName_Code, "}", "{"), + 1, 0, 1)); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpBlockTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpBlockTest.cs new file mode 100644 index 0000000000..5dcc16a68f --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpBlockTest.cs @@ -0,0 +1,733 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.CSharp +{ + public class CSharpBlockTest : CsHtmlCodeParserTestBase + { + [Fact] + public void ParseBlockMethodThrowsArgNullExceptionOnNullContext() + { + // Arrange + CSharpCodeParser parser = new CSharpCodeParser(); + + // Act and Assert + Assert.Throws(() => parser.ParseBlock(), RazorResources.Parser_Context_Not_Set); + } + + [Fact] + public void BalancingBracketsIgnoresStringLiteralCharactersAndBracketsInsideSingleLineComments() + { + SingleSpanBlockTest(@"if(foo) { + // bar } "" baz ' + zoop(); +}", BlockType.Statement, SpanKind.Code); + } + + [Fact] + public void NestedCodeBlockWithAtCausesError() + { + ParseBlockTest("if (true) { @if(false) { } }", + new StatementBlock( + Factory.Code("if (true) { ").AsStatement(), + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("if(false) { }").AsStatement() + ), + Factory.Code(" }").AsStatement()), + new RazorError( + String.Format(RazorResources.ParseError_Unexpected_Keyword_After_At, + "if"), + new SourceLocation(13, 0, 13))); + } + + [Fact] + public void BalancingBracketsIgnoresStringLiteralCharactersAndBracketsInsideBlockComments() + { + SingleSpanBlockTest( + @"if(foo) { + /* bar } "" */ ' baz } ' + zoop(); +}", BlockType.Statement, SpanKind.Code); + } + + [Fact] + public void ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsForKeyword() + { + SingleSpanBlockTest("for(int i = 0; i < 10; new Foo { Bar = \"baz\" }) { Debug.WriteLine(@\"foo } bar\"); }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsForeachKeyword() + { + SingleSpanBlockTest("foreach(int i = 0; i < 10; new Foo { Bar = \"baz\" }) { Debug.WriteLine(@\"foo } bar\"); }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsWhileKeyword() + { + SingleSpanBlockTest("while(int i = 0; i < 10; new Foo { Bar = \"baz\" }) { Debug.WriteLine(@\"foo } bar\"); }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsUsingKeywordFollowedByParen() + { + SingleSpanBlockTest("using(int i = 0; i < 10; new Foo { Bar = \"baz\" }) { Debug.WriteLine(@\"foo } bar\"); }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockSupportsUsingsNestedWithinOtherBlocks() + { + SingleSpanBlockTest("if(foo) { using(int i = 0; i < 10; new Foo { Bar = \"baz\" }) { Debug.WriteLine(@\"foo } bar\"); } }", BlockType.Statement, SpanKind.Code); + } + + [Fact] + public void ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsIfKeywordWithNoElseBranches() + { + SingleSpanBlockTest("if(int i = 0; i < 10; new Foo { Bar = \"baz\" }) { Debug.WriteLine(@\"foo } bar\"); }", BlockType.Statement, SpanKind.Code); + } + + [Fact] + public void ParseBlockAllowsEmptyBlockStatement() + { + SingleSpanBlockTest("if(false) { }", BlockType.Statement, SpanKind.Code); + } + + [Fact] + public void ParseBlockTerminatesParenBalancingAtEOF() + { + ImplicitExpressionTest("Html.En(code()", "Html.En(code()", + AcceptedCharacters.Any, + new RazorError( + String.Format(RazorResources.ParseError_Expected_CloseBracket_Before_EOF, + "(", ")"), + new SourceLocation(8, 0, 8))); + } + + [Fact] + public void ParseBlockSupportsBlockCommentBetweenIfAndElseClause() + { + SingleSpanBlockTest("if(foo) { bar(); } /* Foo */ /* Bar */ else { baz(); }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockSupportsRazorCommentBetweenIfAndElseClause() + { + RunRazorCommentBetweenClausesTest("if(foo) { bar(); } ", " else { baz(); }", acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockSupportsBlockCommentBetweenElseIfAndElseClause() + { + SingleSpanBlockTest("if(foo) { bar(); } else if(bar) { baz(); } /* Foo */ /* Bar */ else { biz(); }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockSupportsRazorCommentBetweenElseIfAndElseClause() + { + RunRazorCommentBetweenClausesTest("if(foo) { bar(); } else if(bar) { baz(); } ", " else { baz(); }", acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockSupportsBlockCommentBetweenIfAndElseIfClause() + { + SingleSpanBlockTest("if(foo) { bar(); } /* Foo */ /* Bar */ else if(bar) { baz(); }", BlockType.Statement, SpanKind.Code); + } + + [Fact] + public void ParseBlockSupportsRazorCommentBetweenIfAndElseIfClause() + { + RunRazorCommentBetweenClausesTest("if(foo) { bar(); } ", " else if(bar) { baz(); }"); + } + + [Fact] + public void ParseBlockSupportsLineCommentBetweenIfAndElseClause() + { + SingleSpanBlockTest(@"if(foo) { bar(); } +// Foo +// Bar +else { baz(); }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockSupportsLineCommentBetweenElseIfAndElseClause() + { + SingleSpanBlockTest(@"if(foo) { bar(); } else if(bar) { baz(); } +// Foo +// Bar +else { biz(); }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockSupportsLineCommentBetweenIfAndElseIfClause() + { + SingleSpanBlockTest(@"if(foo) { bar(); } +// Foo +// Bar +else if(bar) { baz(); }", BlockType.Statement, SpanKind.Code); + } + + [Fact] + public void ParseBlockParsesElseIfBranchesOfIfStatement() + { + const string ifStatement = @"if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) { + Debug.WriteLine(@""foo } bar""); +}"; + const string elseIfBranch = @" else if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) { + Debug.WriteLine(@""bar } baz""); +}"; + const string document = ifStatement + elseIfBranch; + + SingleSpanBlockTest(document, BlockType.Statement, SpanKind.Code); + } + + [Fact] + public void ParseBlockParsesMultipleElseIfBranchesOfIfStatement() + { + const string ifStatement = @"if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) { + Debug.WriteLine(@""foo } bar""); +}"; + const string elseIfBranch = @" else if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) { + Debug.WriteLine(@""bar } baz""); +}"; + const string document = ifStatement + elseIfBranch + elseIfBranch + elseIfBranch + elseIfBranch; + SingleSpanBlockTest(document, BlockType.Statement, SpanKind.Code); + } + + [Fact] + public void ParseBlockParsesMultipleElseIfBranchesOfIfStatementFollowedByOneElseBranch() + { + const string ifStatement = @"if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) { + Debug.WriteLine(@""foo } bar""); +}"; + const string elseIfBranch = @" else if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) { + Debug.WriteLine(@""bar } baz""); +}"; + const string elseBranch = @" else { Debug.WriteLine(@""bar } baz""); }"; + const string document = ifStatement + elseIfBranch + elseIfBranch + elseBranch; + + SingleSpanBlockTest(document, BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockStopsParsingCodeAfterElseBranch() + { + const string ifStatement = @"if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) { + Debug.WriteLine(@""foo } bar""); +}"; + const string elseIfBranch = @" else if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) { + Debug.WriteLine(@""bar } baz""); +}"; + const string elseBranch = @" else { Debug.WriteLine(@""bar } baz""); }"; + const string document = ifStatement + elseIfBranch + elseBranch + elseIfBranch; + const string expected = ifStatement + elseIfBranch + elseBranch; + + ParseBlockTest(document, new StatementBlock(Factory.Code(expected).AsStatement().Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockStopsParsingIfIfStatementNotFollowedByElse() + { + const string document = @"if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) { + Debug.WriteLine(@""foo } bar""); +}"; + + SingleSpanBlockTest(document, BlockType.Statement, SpanKind.Code); + } + + [Fact] + public void ParseBlockAcceptsElseIfWithNoCondition() + { + // We don't want to be a full C# parser - If the else if is missing it's condition, the C# compiler can handle that, we have all the info we need to keep parsing + const string ifBranch = @"if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) { + Debug.WriteLine(@""foo } bar""); +}"; + const string elseIfBranch = @" else if { foo(); }"; + const string document = ifBranch + elseIfBranch; + + SingleSpanBlockTest(document, BlockType.Statement, SpanKind.Code); + } + + [Fact] + public void ParseBlockCorrectlyParsesDoWhileBlock() + { + SingleSpanBlockTest("do { var foo = bar; } while(foo != bar);", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockCorrectlyParsesDoWhileBlockMissingSemicolon() + { + SingleSpanBlockTest("do { var foo = bar; } while(foo != bar)", BlockType.Statement, SpanKind.Code); + } + + [Fact] + public void ParseBlockCorrectlyParsesDoWhileBlockMissingWhileCondition() + { + SingleSpanBlockTest("do { var foo = bar; } while", BlockType.Statement, SpanKind.Code); + } + + [Fact] + public void ParseBlockCorrectlyParsesDoWhileBlockMissingWhileConditionWithSemicolon() + { + SingleSpanBlockTest("do { var foo = bar; } while;", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockCorrectlyParsesDoWhileBlockMissingWhileClauseEntirely() + { + SingleSpanBlockTest("do { var foo = bar; } narf;", "do { var foo = bar; }", BlockType.Statement, SpanKind.Code); + } + + [Fact] + public void ParseBlockSupportsBlockCommentBetweenDoAndWhileClause() + { + SingleSpanBlockTest("do { var foo = bar; } /* Foo */ /* Bar */ while(true);", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockSupportsLineCommentBetweenDoAndWhileClause() + { + SingleSpanBlockTest(@"do { var foo = bar; } +// Foo +// Bar +while(true);", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockSupportsRazorCommentBetweenDoAndWhileClause() + { + RunRazorCommentBetweenClausesTest("do { var foo = bar; } ", " while(true);", acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockCorrectlyParsesMarkupInDoWhileBlock() + { + ParseBlockTest("@do { var foo = bar;

Foo

foo++; } while (foo);", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("do { var foo = bar;").AsStatement(), + new MarkupBlock( + Factory.Markup("

Foo

").Accepts(AcceptedCharacters.None) + ), + Factory.Code("foo++; } while (foo);").AsStatement().Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsSwitchKeyword() + { + SingleSpanBlockTest(@"switch(foo) { + case 0: + break; + case 1: + { + break; + } + case 2: + return; + default: + return; +}", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsLockKeyword() + { + SingleSpanBlockTest("lock(foo) { Debug.WriteLine(@\"foo } bar\"); }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockHasErrorsIfNamespaceImportMissingSemicolon() + { + NamespaceImportTest("using Foo.Bar.Baz", " Foo.Bar.Baz", acceptedCharacters: AcceptedCharacters.NonWhiteSpace | AcceptedCharacters.WhiteSpace, location: new SourceLocation(17, 0, 17)); + } + + [Fact] + public void ParseBlockHasErrorsIfNamespaceAliasMissingSemicolon() + { + NamespaceImportTest("using Foo.Bar.Baz = FooBarBaz", " Foo.Bar.Baz = FooBarBaz", acceptedCharacters: AcceptedCharacters.NonWhiteSpace | AcceptedCharacters.WhiteSpace, location: new SourceLocation(29, 0, 29)); + } + + [Fact] + public void ParseBlockParsesNamespaceImportWithSemicolonForUsingKeywordIfIsInValidFormat() + { + NamespaceImportTest("using Foo.Bar.Baz;", " Foo.Bar.Baz", AcceptedCharacters.NonWhiteSpace | AcceptedCharacters.WhiteSpace); + } + + [Fact] + public void ParseBlockDoesntCaptureWhitespaceAfterUsing() + { + ParseBlockTest("using Foo ", + new DirectiveBlock( + Factory.Code("using Foo") + .AsNamespaceImport(" Foo", CSharpCodeParser.UsingKeywordLength) + .Accepts(AcceptedCharacters.NonWhiteSpace | AcceptedCharacters.WhiteSpace))); + } + + [Fact] + public void ParseBlockParsesNamespaceAliasWithSemicolonForUsingKeywordIfIsInValidFormat() + { + NamespaceImportTest("using FooBarBaz = FooBarBaz;", " FooBarBaz = FooBarBaz", AcceptedCharacters.NonWhiteSpace | AcceptedCharacters.WhiteSpace); + } + + [Fact] + public void ParseBlockTerminatesUsingKeywordAtEOFAndOutputsFileCodeBlock() + { + SingleSpanBlockTest("using ", BlockType.Statement, SpanKind.Code); + } + + [Fact] + public void ParseBlockTerminatesSingleLineCommentAtEndOfFile() + { + const string document = "foreach(var f in Foo) { // foo bar baz"; + SingleSpanBlockTest(document, document, BlockType.Statement, SpanKind.Code, + new RazorError(String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, "foreach", '}', '{'), SourceLocation.Zero)); + } + + [Fact] + public void ParseBlockTerminatesBlockCommentAtEndOfFile() + { + const string document = "foreach(var f in Foo) { /* foo bar baz"; + SingleSpanBlockTest(document, document, BlockType.Statement, SpanKind.Code, + new RazorError(String.Format(RazorResources.ParseError_BlockComment_Not_Terminated), 24, 0, 24), + new RazorError(String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, "foreach", '}', '{'), SourceLocation.Zero)); + } + + [Fact] + public void ParseBlockTerminatesSingleSlashAtEndOfFile() + { + const string document = "foreach(var f in Foo) { / foo bar baz"; + SingleSpanBlockTest(document, document, BlockType.Statement, SpanKind.Code, + new RazorError(String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, "foreach", '}', '{'), SourceLocation.Zero)); + } + + [Fact] + public void ParseBlockSupportsBlockCommentBetweenTryAndFinallyClause() + { + SingleSpanBlockTest("try { bar(); } /* Foo */ /* Bar */ finally { baz(); }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockSupportsRazorCommentBetweenTryAndFinallyClause() + { + RunRazorCommentBetweenClausesTest("try { bar(); } ", " finally { biz(); }", acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockSupportsBlockCommentBetweenCatchAndFinallyClause() + { + SingleSpanBlockTest("try { bar(); } catch(bar) { baz(); } /* Foo */ /* Bar */ finally { biz(); }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockSupportsRazorCommentBetweenCatchAndFinallyClause() + { + RunRazorCommentBetweenClausesTest("try { bar(); } catch(bar) { baz(); } ", " finally { biz(); }", acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockSupportsBlockCommentBetweenTryAndCatchClause() + { + SingleSpanBlockTest("try { bar(); } /* Foo */ /* Bar */ catch(bar) { baz(); }", BlockType.Statement, SpanKind.Code); + } + + [Fact] + public void ParseBlockSupportsRazorCommentBetweenTryAndCatchClause() + { + RunRazorCommentBetweenClausesTest("try { bar(); }", " catch(bar) { baz(); }"); + } + + [Fact] + public void ParseBlockSupportsLineCommentBetweenTryAndFinallyClause() + { + SingleSpanBlockTest(@"try { bar(); } +// Foo +// Bar +finally { baz(); }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockSupportsLineCommentBetweenCatchAndFinallyClause() + { + SingleSpanBlockTest(@"try { bar(); } catch(bar) { baz(); } +// Foo +// Bar +finally { biz(); }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockSupportsLineCommentBetweenTryAndCatchClause() + { + SingleSpanBlockTest(@"try { bar(); } +// Foo +// Bar +catch(bar) { baz(); }", BlockType.Statement, SpanKind.Code); + } + + [Fact] + public void ParseBlockSupportsTryStatementWithNoAdditionalClauses() + { + SingleSpanBlockTest("try { var foo = new { } }", BlockType.Statement, SpanKind.Code); + } + + [Fact] + public void ParseBlockSupportsMarkupWithinTryClause() + { + RunSimpleWrappedMarkupTest("try {", "

Foo

", "}"); + } + + [Fact] + public void ParseBlockSupportsTryStatementWithOneCatchClause() + { + SingleSpanBlockTest("try { var foo = new { } } catch(Foo Bar Baz) { var foo = new { } }", BlockType.Statement, SpanKind.Code); + } + + [Fact] + public void ParseBlockSupportsMarkupWithinCatchClause() + { + RunSimpleWrappedMarkupTest("try { var foo = new { } } catch(Foo Bar Baz) {", "

Foo

", "}"); + } + + [Fact] + public void ParseBlockSupportsTryStatementWithMultipleCatchClause() + { + SingleSpanBlockTest("try { var foo = new { } } catch(Foo Bar Baz) { var foo = new { } } catch(Foo Bar Baz) { var foo = new { } } catch(Foo Bar Baz) { var foo = new { } }", BlockType.Statement, SpanKind.Code); + } + + [Fact] + public void ParseBlockSupportsExceptionLessCatchClauses() + { + SingleSpanBlockTest("try { var foo = new { } } catch { var foo = new { } }", BlockType.Statement, SpanKind.Code); + } + + [Fact] + public void ParseBlockSupportsMarkupWithinAdditionalCatchClauses() + { + RunSimpleWrappedMarkupTest("try { var foo = new { } } catch(Foo Bar Baz) { var foo = new { } } catch(Foo Bar Baz) { var foo = new { } } catch(Foo Bar Baz) {", "

Foo

", "}"); + } + + [Fact] + public void ParseBlockSupportsTryStatementWithFinallyClause() + { + SingleSpanBlockTest("try { var foo = new { } } finally { var foo = new { } }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockSupportsMarkupWithinFinallyClause() + { + RunSimpleWrappedMarkupTest("try { var foo = new { } } finally {", "

Foo

", "}", acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockStopsParsingCatchClausesAfterFinallyBlock() + { + string expectedContent = "try { var foo = new { } } finally { var foo = new { } }"; + SingleSpanBlockTest(expectedContent + " catch(Foo Bar Baz) { }", expectedContent, BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockDoesNotAllowMultipleFinallyBlocks() + { + string expectedContent = "try { var foo = new { } } finally { var foo = new { } }"; + SingleSpanBlockTest(expectedContent + " finally { }", expectedContent, BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockAcceptsTrailingDotIntoImplicitExpressionWhenEmbeddedInCode() + { + // Arrange + ParseBlockTest(@"if(foo) { @foo. }", + new StatementBlock( + Factory.Code("if(foo) { ").AsStatement(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("foo.") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true) + .Accepts(AcceptedCharacters.NonWhiteSpace) + ), + Factory.Code(" }").AsStatement() + )); + } + + [Fact] + public void ParseBlockParsesExpressionOnSwitchCharacterFollowedByOpenParen() + { + // Arrange + ParseBlockTest(@"if(foo) { @(foo + bar) }", + new StatementBlock( + Factory.Code("if(foo) { ").AsStatement(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.MetaCode("(").Accepts(AcceptedCharacters.None), + Factory.Code("foo + bar").AsExpression(), + Factory.MetaCode(")").Accepts(AcceptedCharacters.None) + ), + Factory.Code(" }").AsStatement() + )); + } + + [Fact] + public void ParseBlockParsesExpressionOnSwitchCharacterFollowedByIdentifierStart() + { + // Arrange + ParseBlockTest(@"if(foo) { @foo[4].bar() }", + new StatementBlock( + Factory.Code("if(foo) { ").AsStatement(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("foo[4].bar()") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true) + .Accepts(AcceptedCharacters.NonWhiteSpace) + ), + Factory.Code(" }").AsStatement() + )); + } + + [Fact] + public void ParseBlockTreatsDoubleAtSignAsEscapeSequenceIfAtStatementStart() + { + // Arrange + ParseBlockTest(@"if(foo) { @@class.Foo() }", + new StatementBlock( + Factory.Code("if(foo) { ").AsStatement(), + Factory.Code("@").Hidden(), + Factory.Code("@class.Foo() }").AsStatement() + )); + } + + [Fact] + public void ParseBlockTreatsAtSignsAfterFirstPairAsPartOfCSharpStatement() + { + // Arrange + ParseBlockTest(@"if(foo) { @@@@class.Foo() }", + new StatementBlock( + Factory.Code("if(foo) { ").AsStatement(), + Factory.Code("@").Hidden(), + Factory.Code("@@@class.Foo() }").AsStatement() + )); + } + + [Fact] + public void ParseBlockDoesNotParseMarkupStatementOrExpressionOnSwitchCharacterNotFollowedByOpenAngleOrColon() + { + // Arrange + ParseBlockTest("if(foo) { @\"Foo\".ToString(); }", + new StatementBlock( + Factory.Code("if(foo) { @\"Foo\".ToString(); }").AsStatement())); + } + + [Fact] + public void ParsersCanNestRecursively() + { + // Arrange + ParseBlockTest("foreach(var c in db.Categories) {" + Environment.NewLine + + "
" + Environment.NewLine + + "

@c.Name

" + Environment.NewLine + + "
    " + Environment.NewLine + + " @foreach(var p in c.Products) {" + Environment.NewLine + + "
  • @p.Name
  • " + Environment.NewLine + + " }" + Environment.NewLine + + "
" + Environment.NewLine + + "
" + Environment.NewLine + + " }", + new StatementBlock( + Factory.Code("foreach(var c in db.Categories) {\r\n").AsStatement(), + new MarkupBlock( + Factory.Markup("
\r\n

"), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("c.Name") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Markup("

\r\n
    \r\n"), + new StatementBlock( + Factory.Code(@" ").AsStatement(), + Factory.CodeTransition(), + Factory.Code("foreach(var p in c.Products) {\r\n").AsStatement(), + new MarkupBlock( + Factory.Markup("
  • (" href=\"", 193, 5, 30), new LocationTagged("\"", 256, 5, 93)), + Factory.Markup(" href=\"").With(SpanCodeGenerator.Null), + new MarkupBlock(new DynamicAttributeBlockCodeGenerator(new LocationTagged(String.Empty, 200, 5, 37), 200, 5, 37), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("Html.ActionUrl(\"Products\", \"Detail\", new { id = p.Id })") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace))), + Factory.Markup("\"").With(SpanCodeGenerator.Null)), + Factory.Markup(">"), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("p.Name") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Markup("
  • \r\n").Accepts(AcceptedCharacters.None)), + Factory.Code(" }\r\n").AsStatement().Accepts(AcceptedCharacters.None)), + Factory.Markup("
\r\n
\r\n") + .Accepts(AcceptedCharacters.None)), + Factory.Code(" }").AsStatement().Accepts(AcceptedCharacters.None))); + } + + private void RunRazorCommentBetweenClausesTest(string preComment, string postComment, AcceptedCharacters acceptedCharacters = AcceptedCharacters.Any) + { + ParseBlockTest(preComment + "@* Foo *@ @* Bar *@" + postComment, + new StatementBlock( + Factory.Code(preComment).AsStatement(), + new CommentBlock( + Factory.CodeTransition(CSharpSymbolType.RazorCommentTransition), + Factory.MetaCode("*", CSharpSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.Comment(" Foo ", CSharpSymbolType.RazorComment), + Factory.MetaCode("*", CSharpSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.CodeTransition(CSharpSymbolType.RazorCommentTransition) + ), + Factory.Code(" ").AsStatement(), + new CommentBlock( + Factory.CodeTransition(CSharpSymbolType.RazorCommentTransition), + Factory.MetaCode("*", CSharpSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.Comment(" Bar ", CSharpSymbolType.RazorComment), + Factory.MetaCode("*", CSharpSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.CodeTransition(CSharpSymbolType.RazorCommentTransition) + ), + Factory.Code(postComment).AsStatement().Accepts(acceptedCharacters))); + } + + private void RunSimpleWrappedMarkupTest(string prefix, string markup, string suffix, AcceptedCharacters acceptedCharacters = AcceptedCharacters.Any) + { + ParseBlockTest(prefix + markup + suffix, + new StatementBlock( + Factory.Code(prefix).AsStatement(), + new MarkupBlock( + Factory.Markup(markup).Accepts(AcceptedCharacters.None) + ), + Factory.Code(suffix).AsStatement().Accepts(acceptedCharacters) + )); + } + + private void NamespaceImportTest(string content, string expectedNS, AcceptedCharacters acceptedCharacters = AcceptedCharacters.None, string errorMessage = null, SourceLocation? location = null) + { + var errors = new RazorError[0]; + if (!String.IsNullOrEmpty(errorMessage) && location.HasValue) + { + errors = new RazorError[] + { + new RazorError(errorMessage, location.Value) + }; + } + ParseBlockTest(content, + new DirectiveBlock( + Factory.Code(content) + .AsNamespaceImport(expectedNS, CSharpCodeParser.UsingKeywordLength) + .Accepts(acceptedCharacters)), + errors); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpDirectivesTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpDirectivesTest.cs new file mode 100644 index 0000000000..ab7702c1df --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpDirectivesTest.cs @@ -0,0 +1,163 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.CSharp +{ + public class CSharpDirectivesTest : CsHtmlCodeParserTestBase + { + [Fact] + public void InheritsDirective() + { + ParseBlockTest("@inherits System.Web.WebPages.WebPage", + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode(SyntaxConstants.CSharp.InheritsKeyword + " ") + .Accepts(AcceptedCharacters.None), + Factory.Code("System.Web.WebPages.WebPage") + .AsBaseType("System.Web.WebPages.WebPage"))); + } + + [Fact] + public void InheritsDirectiveSupportsArrays() + { + ParseBlockTest("@inherits string[[]][]", + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode(SyntaxConstants.CSharp.InheritsKeyword + " ") + .Accepts(AcceptedCharacters.None), + Factory.Code("string[[]][]") + .AsBaseType("string[[]][]"))); + } + + [Fact] + public void InheritsDirectiveSupportsNestedGenerics() + { + ParseBlockTest("@inherits System.Web.Mvc.WebViewPage>", + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode(SyntaxConstants.CSharp.InheritsKeyword + " ") + .Accepts(AcceptedCharacters.None), + Factory.Code("System.Web.Mvc.WebViewPage>") + .AsBaseType("System.Web.Mvc.WebViewPage>"))); + } + + [Fact] + public void InheritsDirectiveSupportsTypeKeywords() + { + ParseBlockTest("@inherits string", + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode(SyntaxConstants.CSharp.InheritsKeyword + " ") + .Accepts(AcceptedCharacters.None), + Factory.Code("string") + .AsBaseType("string"))); + } + + [Fact] + public void InheritsDirectiveSupportsVSTemplateTokens() + { + ParseBlockTest("@inherits $rootnamespace$.MyBase", + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode(SyntaxConstants.CSharp.InheritsKeyword + " ") + .Accepts(AcceptedCharacters.None), + Factory.Code("$rootnamespace$.MyBase") + .AsBaseType("$rootnamespace$.MyBase"))); + } + + [Fact] + public void SessionStateDirectiveWorks() + { + ParseBlockTest("@sessionstate InProc", + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode(SyntaxConstants.CSharp.SessionStateKeyword + " ") + .Accepts(AcceptedCharacters.None), + Factory.Code("InProc") + .AsRazorDirectiveAttribute("sessionstate", "InProc") + )); + } + + [Fact] + public void SessionStateDirectiveParsesInvalidSessionValue() + { + ParseBlockTest("@sessionstate Blah", + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode(SyntaxConstants.CSharp.SessionStateKeyword + " ") + .Accepts(AcceptedCharacters.None), + Factory.Code("Blah") + .AsRazorDirectiveAttribute("sessionstate", "Blah") + )); + } + + [Fact] + public void FunctionsDirective() + { + ParseBlockTest("@functions { foo(); bar(); }", + new FunctionsBlock( + Factory.CodeTransition(), + Factory.MetaCode(SyntaxConstants.CSharp.FunctionsKeyword + " {") + .Accepts(AcceptedCharacters.None), + Factory.Code(" foo(); bar(); ") + .AsFunctionsBody(), + Factory.MetaCode("}") + .Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void EmptyFunctionsDirective() + { + ParseBlockTest("@functions { }", + new FunctionsBlock( + Factory.CodeTransition(), + Factory.MetaCode(SyntaxConstants.CSharp.FunctionsKeyword + " {") + .Accepts(AcceptedCharacters.None), + Factory.Code(" ") + .AsFunctionsBody(), + Factory.MetaCode("}") + .Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void SectionDirective() + { + ParseBlockTest("@section Header {

F{o}o

}", + new SectionBlock(new SectionCodeGenerator("Header"), + Factory.CodeTransition(), + Factory.MetaCode("section Header {") + .AutoCompleteWith(null, atEndOfSpan: true) + .Accepts(AcceptedCharacters.Any), + new MarkupBlock( + Factory.Markup("

F", "{", "o", "}", "o", "

")), + Factory.MetaCode("}") + .Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void HelperDirective() + { + ParseBlockTest("@helper Strong(string value) { foo(); }", + new HelperBlock(new HelperCodeGenerator(new LocationTagged("Strong(string value) {", new SourceLocation(8, 0, 8)), headerComplete: true), + Factory.CodeTransition(), + Factory.MetaCode("helper ") + .Accepts(AcceptedCharacters.None), + Factory.Code("Strong(string value) {") + .Hidden() + .Accepts(AcceptedCharacters.None), + new StatementBlock( + Factory.Code(" foo(); ") + .AsStatement() + .With(new StatementCodeGenerator())), + Factory.Code("}") + .Hidden() + .Accepts(AcceptedCharacters.None))); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpErrorTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpErrorTest.cs new file mode 100644 index 0000000000..bd33a35daa --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpErrorTest.cs @@ -0,0 +1,624 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.CSharp +{ + public class CSharpErrorTest : CsHtmlCodeParserTestBase + { + [Fact] + public void ParseBlockHandlesQuotesAfterTransition() + { + ParseBlockTest("@\"", + new ExpressionBlock( + Factory.CodeTransition(), + Factory.EmptyCSharp() + .AsImplicitExpression(KeywordSet) + .Accepts(AcceptedCharacters.NonWhiteSpace) + ), + new RazorError( + String.Format(RazorResources.ParseError_Unexpected_Character_At_Start_Of_CodeBlock_CS, '"'), + 1, 0, 1)); + } + + [Fact] + public void ParseBlockCapturesWhitespaceToEndOfLineInInvalidUsingStatementAndTreatsAsFileCode() + { + ParseBlockTest("using " + Environment.NewLine + + Environment.NewLine, + new StatementBlock( + Factory.Code("using \r\n").AsStatement() + )); + } + + [Fact] + public void ParseBlockMethodOutputsOpenCurlyAsCodeSpanIfEofFoundAfterOpenCurlyBrace() + { + ParseBlockTest("{", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.EmptyCSharp() + .AsStatement() + .With(new AutoCompleteEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString) { AutoCompleteString = "}" }) + ), + new RazorError( + String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, + RazorResources.BlockName_Code, + "}", "{"), + SourceLocation.Zero)); + } + + [Fact] + public void ParseBlockMethodOutputsZeroLengthCodeSpanIfStatementBlockEmpty() + { + ParseBlockTest("{}", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.EmptyCSharp().AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockMethodProducesErrorIfNewlineFollowsTransition() + { + ParseBlockTest("@" + Environment.NewLine, + new ExpressionBlock( + Factory.CodeTransition(), + Factory.EmptyCSharp() + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + new RazorError(RazorResources.ParseError_Unexpected_WhiteSpace_At_Start_Of_CodeBlock_CS, new SourceLocation(1, 0, 1))); + } + + [Fact] + public void ParseBlockMethodProducesErrorIfWhitespaceBetweenTransitionAndBlockStartInEmbeddedExpression() + { + ParseBlockTest("{" + Environment.NewLine + + " @ {}" + Environment.NewLine + + "}", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code("\r\n ").AsStatement(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.EmptyCSharp() + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Code(" {}\r\n").AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None) + ), + new RazorError(RazorResources.ParseError_Unexpected_WhiteSpace_At_Start_Of_CodeBlock_CS, 8, 1, 5)); + } + + [Fact] + public void ParseBlockMethodProducesErrorIfEOFAfterTransitionInEmbeddedExpression() + { + ParseBlockTest("{" + Environment.NewLine + + " @", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code("\r\n ").AsStatement(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.EmptyCSharp() + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.EmptyCSharp().AsStatement() + ), + new RazorError(RazorResources.ParseError_Unexpected_EndOfFile_At_Start_Of_CodeBlock, 8, 1, 5), + new RazorError( + String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, RazorResources.BlockName_Code, "}", "{"), + SourceLocation.Zero)); + } + + [Fact] + public void ParseBlockMethodParsesNothingIfFirstCharacterIsNotIdentifierStartOrParenOrBrace() + { + ParseBlockTest("@!!!", + new ExpressionBlock( + Factory.CodeTransition(), + Factory.EmptyCSharp() + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + new RazorError( + String.Format(RazorResources.ParseError_Unexpected_Character_At_Start_Of_CodeBlock_CS, "!"), + 1, 0, 1)); + } + + [Fact] + public void ParseBlockShouldReportErrorAndTerminateAtEOFIfIfParenInExplicitExpressionUnclosed() + { + ParseBlockTest("(foo bar" + Environment.NewLine + + "baz", + new ExpressionBlock( + Factory.MetaCode("(").Accepts(AcceptedCharacters.None), + Factory.Code("foo bar\r\nbaz").AsExpression() + ), + new RazorError( + String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, + RazorResources.BlockName_ExplicitExpression, ')', '('), + new SourceLocation(0, 0, 0))); + } + + [Fact] + public void ParseBlockShouldReportErrorAndTerminateAtMarkupIfIfParenInExplicitExpressionUnclosed() + { + ParseBlockTest("(foo bar" + Environment.NewLine + + "" + Environment.NewLine + + "baz" + Environment.NewLine + + "@Html.Foo(Bar);" + Environment.NewLine, + new ExpressionBlock( + Factory.Code("Href(\r\n") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + ), + new RazorError( + String.Format(RazorResources.ParseError_Expected_CloseBracket_Before_EOF, + "(", ")"), + new SourceLocation(4, 0, 4))); + } + + [Fact] + // Test for fix to Dev10 884975 - Incorrect Error Messaging + public void ParseBlockShouldReportErrorAndTerminateAtEOFIfParenInImplicitExpressionUnclosed() + { + ParseBlockTest("Foo(Bar(Baz)" + Environment.NewLine + + "Biz" + Environment.NewLine + + "Boz", + new ExpressionBlock( + Factory.Code("Foo(Bar(Baz)\r\nBiz\r\nBoz") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + ), + new RazorError(String.Format(RazorResources.ParseError_Expected_CloseBracket_Before_EOF, + "(", ")"), + new SourceLocation(3, 0, 3))); + } + + [Fact] + // Test for fix to Dev10 884975 - Incorrect Error Messaging + public void ParseBlockShouldReportErrorAndTerminateAtMarkupIfParenInImplicitExpressionUnclosed() + { + ParseBlockTest("Foo(Bar(Baz)" + Environment.NewLine + + "Biz" + Environment.NewLine + + "" + Environment.NewLine + + "Boz" + Environment.NewLine + + "", + new ExpressionBlock( + Factory.Code("Foo(Bar(Baz)\r\nBiz\r\n") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + ), + new RazorError(String.Format(RazorResources.ParseError_Expected_CloseBracket_Before_EOF, + "(", ")"), + new SourceLocation(3, 0, 3))); + } + + [Fact] + // Test for fix to Dev10 884975 - Incorrect Error Messaging + public void ParseBlockShouldReportErrorAndTerminateAtEOFIfBracketInImplicitExpressionUnclosed() + { + ParseBlockTest("Foo[Bar[Baz]" + Environment.NewLine + + "Biz" + Environment.NewLine + + "Boz", + new ExpressionBlock( + Factory.Code("Foo[Bar[Baz]\r\nBiz\r\nBoz") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + ), + new RazorError( + String.Format(RazorResources.ParseError_Expected_CloseBracket_Before_EOF, + "[", "]"), + new SourceLocation(3, 0, 3))); + } + + [Fact] + // Test for fix to Dev10 884975 - Incorrect Error Messaging + public void ParseBlockShouldReportErrorAndTerminateAtMarkupIfBracketInImplicitExpressionUnclosed() + { + ParseBlockTest("Foo[Bar[Baz]" + Environment.NewLine + + "Biz" + Environment.NewLine + + "" + Environment.NewLine + + "Boz" + Environment.NewLine + + "", + new ExpressionBlock( + Factory.Code("Foo[Bar[Baz]\r\nBiz\r\n") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + ), + new RazorError( + String.Format(RazorResources.ParseError_Expected_CloseBracket_Before_EOF, + "[", "]"), + new SourceLocation(3, 0, 3))); + } + + // Simple EOF handling errors: + [Fact] + public void ParseBlockReportsErrorIfExplicitCodeBlockUnterminatedAtEOF() + { + ParseBlockTest("{ var foo = bar; if(foo != null) { bar(); } ", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code(" var foo = bar; if(foo != null) { bar(); } ").AsStatement() + ), + new RazorError( + String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, + RazorResources.BlockName_Code, '}', '{'), + SourceLocation.Zero)); + } + + [Fact] + public void ParseBlockReportsErrorIfClassBlockUnterminatedAtEOF() + { + ParseBlockTest("functions { var foo = bar; if(foo != null) { bar(); } ", + new FunctionsBlock( + Factory.MetaCode("functions {").Accepts(AcceptedCharacters.None), + Factory.Code(" var foo = bar; if(foo != null) { bar(); } ").AsFunctionsBody() + ), + new RazorError( + String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, + "functions", '}', '{'), + SourceLocation.Zero)); + } + + [Fact] + public void ParseBlockReportsErrorIfIfBlockUnterminatedAtEOF() + { + RunUnterminatedSimpleKeywordBlock("if"); + } + + [Fact] + public void ParseBlockReportsErrorIfElseBlockUnterminatedAtEOF() + { + ParseBlockTest("if(foo) { baz(); } else { var foo = bar; if(foo != null) { bar(); } ", + new StatementBlock( + Factory.Code("if(foo) { baz(); } else { var foo = bar; if(foo != null) { bar(); } ").AsStatement() + ), + new RazorError( + String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, + "else", '}', '{'), + new SourceLocation(19, 0, 19))); + } + + [Fact] + public void ParseBlockReportsErrorIfElseIfBlockUnterminatedAtEOF() + { + ParseBlockTest("if(foo) { baz(); } else if { var foo = bar; if(foo != null) { bar(); } ", + new StatementBlock( + Factory.Code("if(foo) { baz(); } else if { var foo = bar; if(foo != null) { bar(); } ").AsStatement() + ), + new RazorError( + String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, + "else if", '}', '{'), + new SourceLocation(19, 0, 19))); + } + + [Fact] + public void ParseBlockReportsErrorIfDoBlockUnterminatedAtEOF() + { + ParseBlockTest("do { var foo = bar; if(foo != null) { bar(); } ", + new StatementBlock( + Factory.Code("do { var foo = bar; if(foo != null) { bar(); } ").AsStatement() + ), + new RazorError( + String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, + "do", '}', '{'), + SourceLocation.Zero)); + } + + [Fact] + public void ParseBlockReportsErrorIfTryBlockUnterminatedAtEOF() + { + ParseBlockTest("try { var foo = bar; if(foo != null) { bar(); } ", + new StatementBlock( + Factory.Code("try { var foo = bar; if(foo != null) { bar(); } ").AsStatement() + ), + new RazorError( + String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, + "try", '}', '{'), + SourceLocation.Zero)); + } + + [Fact] + public void ParseBlockReportsErrorIfCatchBlockUnterminatedAtEOF() + { + ParseBlockTest("try { baz(); } catch(Foo) { var foo = bar; if(foo != null) { bar(); } ", + new StatementBlock( + Factory.Code("try { baz(); } catch(Foo) { var foo = bar; if(foo != null) { bar(); } ").AsStatement() + ), + new RazorError( + String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, + "catch", '}', '{'), + new SourceLocation(15, 0, 15))); + } + + [Fact] + public void ParseBlockReportsErrorIfFinallyBlockUnterminatedAtEOF() + { + ParseBlockTest("try { baz(); } finally { var foo = bar; if(foo != null) { bar(); } ", + new StatementBlock( + Factory.Code("try { baz(); } finally { var foo = bar; if(foo != null) { bar(); } ").AsStatement() + ), + new RazorError( + String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, + "finally", '}', '{'), + new SourceLocation(15, 0, 15))); + } + + [Fact] + public void ParseBlockReportsErrorIfForBlockUnterminatedAtEOF() + { + RunUnterminatedSimpleKeywordBlock("for"); + } + + [Fact] + public void ParseBlockReportsErrorIfForeachBlockUnterminatedAtEOF() + { + RunUnterminatedSimpleKeywordBlock("foreach"); + } + + [Fact] + public void ParseBlockReportsErrorIfWhileBlockUnterminatedAtEOF() + { + RunUnterminatedSimpleKeywordBlock("while"); + } + + [Fact] + public void ParseBlockReportsErrorIfSwitchBlockUnterminatedAtEOF() + { + RunUnterminatedSimpleKeywordBlock("switch"); + } + + [Fact] + public void ParseBlockReportsErrorIfLockBlockUnterminatedAtEOF() + { + RunUnterminatedSimpleKeywordBlock("lock"); + } + + [Fact] + public void ParseBlockReportsErrorIfUsingBlockUnterminatedAtEOF() + { + RunUnterminatedSimpleKeywordBlock("using"); + } + + [Fact] + public void ParseBlockRequiresControlFlowStatementsToHaveBraces() + { + string expectedMessage = String.Format(RazorResources.ParseError_SingleLine_ControlFlowStatements_Not_Allowed, "{", "<"); + ParseBlockTest("if(foo)

Bar

else if(bar)

Baz

else

Boz

", + new StatementBlock( + Factory.Code("if(foo) ").AsStatement(), + new MarkupBlock( + Factory.Markup("

Bar

").Accepts(AcceptedCharacters.None) + ), + Factory.Code("else if(bar) ").AsStatement(), + new MarkupBlock( + Factory.Markup("

Baz

").Accepts(AcceptedCharacters.None) + ), + Factory.Code("else ").AsStatement(), + new MarkupBlock( + Factory.Markup("

Boz

").Accepts(AcceptedCharacters.None) + ), + Factory.EmptyCSharp().AsStatement() + ), + new RazorError(expectedMessage, 8, 0, 8), + new RazorError(expectedMessage, 32, 0, 32), + new RazorError(expectedMessage, 48, 0, 48)); + } + + [Fact] + public void ParseBlockIncludesUnexpectedCharacterInSingleStatementControlFlowStatementError() + { + ParseBlockTest("if(foo)) { var bar = foo; }", + new StatementBlock( + Factory.Code("if(foo)) { var bar = foo; }").AsStatement() + ), + new RazorError( + String.Format(RazorResources.ParseError_SingleLine_ControlFlowStatements_Not_Allowed, + "{", ")"), + new SourceLocation(7, 0, 7))); + } + + [Fact] + public void ParseBlockOutputsErrorIfAtSignFollowedByLessThanSignAtStatementStart() + { + ParseBlockTest("if(foo) { @

Bar

}", + new StatementBlock( + Factory.Code("if(foo) {").AsStatement(), + new MarkupBlock( + Factory.Markup(" "), + Factory.MarkupTransition(), + Factory.Markup("

Bar

").Accepts(AcceptedCharacters.None) + ), + Factory.Code("}").AsStatement() + ), + new RazorError( + RazorResources.ParseError_AtInCode_Must_Be_Followed_By_Colon_Paren_Or_Identifier_Start, + 10, 0, 10)); + } + + [Fact] + public void ParseBlockTerminatesIfBlockAtEOLWhenRecoveringFromMissingCloseParen() + { + ParseBlockTest("if(foo bar" + Environment.NewLine + + "baz", + new StatementBlock( + Factory.Code("if(foo bar\r\n").AsStatement() + ), + new RazorError( + String.Format(RazorResources.ParseError_Expected_CloseBracket_Before_EOF, + "(", ")"), + new SourceLocation(2, 0, 2))); + } + + [Fact] + public void ParseBlockTerminatesForeachBlockAtEOLWhenRecoveringFromMissingCloseParen() + { + ParseBlockTest("foreach(foo bar" + Environment.NewLine + + "baz", + new StatementBlock( + Factory.Code("foreach(foo bar\r\n").AsStatement() + ), + new RazorError( + String.Format(RazorResources.ParseError_Expected_CloseBracket_Before_EOF, + "(", ")"), + new SourceLocation(7, 0, 7))); + } + + [Fact] + public void ParseBlockTerminatesWhileClauseInDoStatementAtEOLWhenRecoveringFromMissingCloseParen() + { + ParseBlockTest("do { } while(foo bar" + Environment.NewLine + + "baz", + new StatementBlock( + Factory.Code("do { } while(foo bar\r\n").AsStatement() + ), + new RazorError( + String.Format(RazorResources.ParseError_Expected_CloseBracket_Before_EOF, + "(", ")"), + new SourceLocation(12, 0, 12))); + } + + [Fact] + public void ParseBlockTerminatesUsingBlockAtEOLWhenRecoveringFromMissingCloseParen() + { + ParseBlockTest("using(foo bar" + Environment.NewLine + + "baz", + new StatementBlock( + Factory.Code("using(foo bar\r\n").AsStatement() + ), + new RazorError( + String.Format(RazorResources.ParseError_Expected_CloseBracket_Before_EOF, + "(", ")"), + new SourceLocation(5, 0, 5))); + } + + [Fact] + public void ParseBlockResumesIfStatementAfterOpenParen() + { + ParseBlockTest("if(" + Environment.NewLine + + "else {

Foo

}", + new StatementBlock( + Factory.Code("if(\r\nelse {").AsStatement(), + new MarkupBlock( + Factory.Markup("

Foo

").Accepts(AcceptedCharacters.None) + ), + Factory.Code("}").AsStatement().Accepts(AcceptedCharacters.None) + ), + new RazorError( + String.Format(RazorResources.ParseError_Expected_CloseBracket_Before_EOF, + "(", ")"), + new SourceLocation(2, 0, 2))); + } + + [Fact] + public void ParseBlockTerminatesNormalCSharpStringsAtEOLIfEndQuoteMissing() + { + SingleSpanBlockTest("if(foo) {" + Environment.NewLine + + " var p = \"foo bar baz" + Environment.NewLine + + ";" + Environment.NewLine + + "}", + BlockType.Statement, SpanKind.Code, + new RazorError(RazorResources.ParseError_Unterminated_String_Literal, 23, 1, 12)); + } + + [Fact] + public void ParseBlockTerminatesNormalStringAtEndOfFile() + { + SingleSpanBlockTest("if(foo) { var foo = \"blah blah blah blah blah", BlockType.Statement, SpanKind.Code, + new RazorError(RazorResources.ParseError_Unterminated_String_Literal, 20, 0, 20), + new RazorError(String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, "if", '}', '{'), SourceLocation.Zero)); + } + + [Fact] + public void ParseBlockTerminatesVerbatimStringAtEndOfFile() + { + SingleSpanBlockTest("if(foo) { var foo = @\"blah " + Environment.NewLine + + "blah; " + Environment.NewLine + + "

Foo

" + Environment.NewLine + + "blah " + Environment.NewLine + + "blah", + BlockType.Statement, SpanKind.Code, + new RazorError(RazorResources.ParseError_Unterminated_String_Literal, 20, 0, 20), + new RazorError(String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, "if", '}', '{'), SourceLocation.Zero)); + } + + [Fact] + public void ParseBlockCorrectlyParsesMarkupIncorrectyAssumedToBeWithinAStatement() + { + ParseBlockTest("if(foo) {" + Environment.NewLine + + " var foo = \"foo bar baz" + Environment.NewLine + + "

Foo is @foo

" + Environment.NewLine + + "}", + new StatementBlock( + Factory.Code("if(foo) {\r\n var foo = \"foo bar baz\r\n ").AsStatement(), + new MarkupBlock( + Factory.Markup("

Foo is "), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("foo") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Markup("

\r\n").Accepts(AcceptedCharacters.None)), + Factory.Code("}").AsStatement() + ), + new RazorError( + RazorResources.ParseError_Unterminated_String_Literal, + 25, 1, 14)); + } + + [Fact] + public void ParseBlockCorrectlyParsesAtSignInDelimitedBlock() + { + ParseBlockTest("(Request[\"description\"] ?? @photo.Description)", + new ExpressionBlock( + Factory.MetaCode("(").Accepts(AcceptedCharacters.None), + Factory.Code("Request[\"description\"] ?? @photo.Description").AsExpression(), + Factory.MetaCode(")").Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockCorrectlyRecoversFromMissingCloseParenInExpressionWithinCode() + { + ParseBlockTest(@"{String.Format(}", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code("String.Format(") + .AsStatement(), + new MarkupBlock( + Factory.Markup("").Accepts(AcceptedCharacters.None)), + Factory.EmptyCSharp().AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + expectedErrors: new[] { + new RazorError( + String.Format(RazorResources.ParseError_Expected_CloseBracket_Before_EOF, "(", ")"), + 14, 0, 14) + }); + + } + + private void RunUnterminatedSimpleKeywordBlock(string keyword) + { + SingleSpanBlockTest(keyword + " (foo) { var foo = bar; if(foo != null) { bar(); } ", BlockType.Statement, SpanKind.Code, + new RazorError(String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, keyword, '}', '{'), SourceLocation.Zero)); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpExplicitExpressionTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpExplicitExpressionTest.cs new file mode 100644 index 0000000000..d4806c6f81 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpExplicitExpressionTest.cs @@ -0,0 +1,142 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.CSharp +{ + public class CSharpExplicitExpressionTest : CsHtmlCodeParserTestBase + { + [Fact] + public void ParseBlockShouldOutputZeroLengthCodeSpanIfExplicitExpressionIsEmpty() + { + ParseBlockTest("@()", + new ExpressionBlock( + Factory.CodeTransition(), + Factory.MetaCode("(").Accepts(AcceptedCharacters.None), + Factory.EmptyCSharp().AsExpression(), + Factory.MetaCode(")").Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockShouldOutputZeroLengthCodeSpanIfEOFOccursAfterStartOfExplicitExpression() + { + ParseBlockTest("@(", + new ExpressionBlock( + Factory.CodeTransition(), + Factory.MetaCode("(").Accepts(AcceptedCharacters.None), + Factory.EmptyCSharp().AsExpression() + ), + new RazorError( + String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, + RazorResources.BlockName_ExplicitExpression, + ")", "("), + new SourceLocation(1, 0, 1))); + } + + [Fact] + public void ParseBlockShouldAcceptEscapedQuoteInNonVerbatimStrings() + { + ParseBlockTest("@(\"\\\"\")", + new ExpressionBlock( + Factory.CodeTransition(), + Factory.MetaCode("(").Accepts(AcceptedCharacters.None), + Factory.Code("\"\\\"\"").AsExpression(), + Factory.MetaCode(")").Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockShouldAcceptEscapedQuoteInVerbatimStrings() + { + ParseBlockTest("@(@\"\"\"\")", + new ExpressionBlock( + Factory.CodeTransition(), + Factory.MetaCode("(").Accepts(AcceptedCharacters.None), + Factory.Code("@\"\"\"\"").AsExpression(), + Factory.MetaCode(")").Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockShouldAcceptMultipleRepeatedEscapedQuoteInVerbatimStrings() + { + ParseBlockTest("@(@\"\"\"\"\"\")", + new ExpressionBlock( + Factory.CodeTransition(), + Factory.MetaCode("(").Accepts(AcceptedCharacters.None), + Factory.Code("@\"\"\"\"\"\"").AsExpression(), + Factory.MetaCode(")").Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockShouldAcceptMultiLineVerbatimStrings() + { + ParseBlockTest(@"@(@""" + Environment.NewLine + + @"Foo" + Environment.NewLine + + @"Bar" + Environment.NewLine + + @"Baz" + Environment.NewLine + + @""")", + new ExpressionBlock( + Factory.CodeTransition(), + Factory.MetaCode("(").Accepts(AcceptedCharacters.None), + Factory.Code("@\"\r\nFoo\r\nBar\r\nBaz\r\n\"").AsExpression(), + Factory.MetaCode(")").Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockShouldAcceptMultipleEscapedQuotesInNonVerbatimStrings() + { + ParseBlockTest("@(\"\\\"hello, world\\\"\")", + new ExpressionBlock( + Factory.CodeTransition(), + Factory.MetaCode("(").Accepts(AcceptedCharacters.None), + Factory.Code("\"\\\"hello, world\\\"\"").AsExpression(), + Factory.MetaCode(")").Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockShouldAcceptMultipleEscapedQuotesInVerbatimStrings() + { + ParseBlockTest("@(@\"\"\"hello, world\"\"\")", + new ExpressionBlock( + Factory.CodeTransition(), + Factory.MetaCode("(").Accepts(AcceptedCharacters.None), + Factory.Code("@\"\"\"hello, world\"\"\"").AsExpression(), + Factory.MetaCode(")").Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockShouldAcceptConsecutiveEscapedQuotesInNonVerbatimStrings() + { + ParseBlockTest("@(\"\\\"\\\"\")", + new ExpressionBlock( + Factory.CodeTransition(), + Factory.MetaCode("(").Accepts(AcceptedCharacters.None), + Factory.Code("\"\\\"\\\"\"").AsExpression(), + Factory.MetaCode(")").Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockShouldAcceptConsecutiveEscapedQuotesInVerbatimStrings() + { + ParseBlockTest("@(@\"\"\"\"\"\")", + new ExpressionBlock( + Factory.CodeTransition(), + Factory.MetaCode("(").Accepts(AcceptedCharacters.None), + Factory.Code("@\"\"\"\"\"\"").AsExpression(), + Factory.MetaCode(")").Accepts(AcceptedCharacters.None) + )); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpHelperTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpHelperTest.cs new file mode 100644 index 0000000000..0e1e2fcc4b --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpHelperTest.cs @@ -0,0 +1,347 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.CSharp +{ + public class CSharpHelperTest : CsHtmlMarkupParserTestBase + { + [Fact] + public void ParseHelperCorrectlyParsesHelperWithNoSpaceInBody() + { + ParseDocumentTest("@helper Foo(){@Bar()}", + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock(new HelperCodeGenerator(new LocationTagged("Foo(){", 8, 0, 8), headerComplete: true), + Factory.CodeTransition(), + Factory.MetaCode("helper ").Accepts(AcceptedCharacters.None), + Factory.Code("Foo(){").Hidden().Accepts(AcceptedCharacters.None), + new StatementBlock( + Factory.EmptyCSharp().AsStatement(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("Bar()") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.EmptyCSharp().AsStatement()), + Factory.Code("}").Hidden().Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void ParseHelperCorrectlyParsesIncompleteHelperPreceedingCodeBlock() + { + ParseDocumentTest("@helper" + Environment.NewLine + + "@{}", + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock( + Factory.CodeTransition(), + Factory.MetaCode("helper")), + Factory.Markup("\r\n"), + new StatementBlock( + Factory.CodeTransition(), + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.EmptyCSharp().AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml()), + new RazorError( + String.Format( + RazorResources.ParseError_Unexpected_Character_At_Helper_Name_Start, + RazorResources.ErrorComponent_Newline), + 7, 0, 7)); + } + + [Fact] + public void ParseHelperRequiresSpaceBeforeSignature() + { + ParseDocumentTest("@helper{", + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock( + Factory.CodeTransition(), + Factory.MetaCode("helper")), + Factory.Markup("{")), + new RazorError( + String.Format( + RazorResources.ParseError_Unexpected_Character_At_Helper_Name_Start, + String.Format(RazorResources.ErrorComponent_Character, "{")), + 7, 0, 7)); + } + + [Fact] + public void ParseHelperOutputsErrorButContinuesIfLParenFoundAfterHelperKeyword() + { + ParseDocumentTest("@helper () {", + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock(new HelperCodeGenerator(new LocationTagged("() {", 8, 0, 8), headerComplete: true), + Factory.CodeTransition(), + Factory.MetaCode("helper ").Accepts(AcceptedCharacters.None), + Factory.Code("() {").Hidden().Accepts(AcceptedCharacters.None), + new StatementBlock( + Factory.EmptyCSharp() + .AsStatement() + .AutoCompleteWith("}")))), + new RazorError( + String.Format( + RazorResources.ParseError_Unexpected_Character_At_Helper_Name_Start, + String.Format(RazorResources.ErrorComponent_Character, "(")), + 8, 0, 8), + new RazorError( + String.Format( + RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, + "helper", "}", "{"), + 1, 0, 1)); + } + + [Fact] + public void ParseHelperStatementOutputsMarkerHelperHeaderSpanOnceKeywordComplete() + { + ParseDocumentTest("@helper ", + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock(new HelperCodeGenerator(new LocationTagged(String.Empty, 8, 0, 8), headerComplete: false), + Factory.CodeTransition(), + Factory.MetaCode("helper ").Accepts(AcceptedCharacters.None), + Factory.EmptyCSharp().Hidden())), + new RazorError( + String.Format( + RazorResources.ParseError_Unexpected_Character_At_Helper_Name_Start, + RazorResources.ErrorComponent_EndOfFile), + 8, 0, 8)); + } + + [Fact] + public void ParseHelperStatementMarksHelperSpanAsCanGrowIfMissingTrailingSpace() + { + ParseDocumentTest("@helper", + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock( + Factory.CodeTransition(), + Factory.MetaCode("helper").Accepts(AcceptedCharacters.Any))), + new RazorError( + String.Format( + RazorResources.ParseError_Unexpected_Character_At_Helper_Name_Start, + RazorResources.ErrorComponent_EndOfFile), + 7, 0, 7)); + } + + [Fact] + public void ParseHelperStatementCapturesWhitespaceToEndOfLineIfHelperStatementMissingName() + { + ParseDocumentTest("@helper " + Environment.NewLine + + " ", + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock(new HelperCodeGenerator(new LocationTagged(" ", 8, 0, 8), headerComplete: false), + Factory.CodeTransition(), + Factory.MetaCode("helper ").Accepts(AcceptedCharacters.None), + Factory.Code(" \r\n").Hidden()), + Factory.Markup(@" ")), + new RazorError( + String.Format( + RazorResources.ParseError_Unexpected_Character_At_Helper_Name_Start, + RazorResources.ErrorComponent_Newline), + 30, 0, 30)); + } + + [Fact] + public void ParseHelperStatementCapturesWhitespaceToEndOfLineIfHelperStatementMissingOpenParen() + { + ParseDocumentTest("@helper Foo " + Environment.NewLine + + " ", + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock(new HelperCodeGenerator(new LocationTagged("Foo ", 8, 0, 8), headerComplete: false), + Factory.CodeTransition(), + Factory.MetaCode("helper ").Accepts(AcceptedCharacters.None), + Factory.Code("Foo \r\n").Hidden()), + Factory.Markup(" ")), + new RazorError( + String.Format(RazorResources.ParseError_MissingCharAfterHelperName, "("), + 15, 0, 15)); + } + + [Fact] + public void ParseHelperStatementCapturesAllContentToEndOfFileIfHelperStatementMissingCloseParenInParameterList() + { + ParseDocumentTest("@helper Foo(Foo Bar" + Environment.NewLine + + "Biz" + Environment.NewLine + + "Boz", + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock(new HelperCodeGenerator(new LocationTagged("Foo(Foo Bar\r\nBiz\r\nBoz", 8, 0, 8), headerComplete: false), + Factory.CodeTransition(), + Factory.MetaCode("helper ").Accepts(AcceptedCharacters.None), + Factory.Code("Foo(Foo Bar\r\nBiz\r\nBoz").Hidden())), + new RazorError( + RazorResources.ParseError_UnterminatedHelperParameterList, + 11, 0, 11)); + } + + [Fact] + public void ParseHelperStatementCapturesWhitespaceToEndOfLineIfHelperStatementMissingOpenBraceAfterParameterList() + { + ParseDocumentTest("@helper Foo(string foo) " + Environment.NewLine, + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock(new HelperCodeGenerator(new LocationTagged("Foo(string foo) ", 8, 0, 8), headerComplete: false), + Factory.CodeTransition(), + Factory.MetaCode("helper ").Accepts(AcceptedCharacters.None), + Factory.Code("Foo(string foo) \r\n").Hidden())), + new RazorError( + String.Format(RazorResources.ParseError_MissingCharAfterHelperParameters, "{"), + 29, 1, 0)); + } + + [Fact] + public void ParseHelperStatementContinuesParsingHelperUntilEOF() + { + ParseDocumentTest("@helper Foo(string foo) { " + Environment.NewLine + + "

Foo

", + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock(new HelperCodeGenerator(new LocationTagged("Foo(string foo) {", 8, 0, 8), headerComplete: true), + Factory.CodeTransition(), + Factory.MetaCode("helper ").Accepts(AcceptedCharacters.None), + Factory.Code(@"Foo(string foo) {").Hidden().Accepts(AcceptedCharacters.None), + new StatementBlock( + Factory.Code(" \r\n") + .AsStatement() + .AutoCompleteWith("}"), + new MarkupBlock( + Factory.Markup("

Foo

").Accepts(AcceptedCharacters.None)), + Factory.EmptyCSharp().AsStatement()))), + new RazorError( + String.Format( + RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, + "helper", "}", "{"), + 1, 0, 1)); + } + + [Fact] + public void ParseHelperStatementCorrectlyParsesHelperWithEmbeddedCode() + { + ParseDocumentTest("@helper Foo(string foo) { " + Environment.NewLine + + "

@foo

" + Environment.NewLine + + "}", + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock(new HelperCodeGenerator(new LocationTagged("Foo(string foo) {", 8, 0, 8), headerComplete: true), + Factory.CodeTransition(), + Factory.MetaCode("helper ").Accepts(AcceptedCharacters.None), + Factory.Code(@"Foo(string foo) {").Hidden().Accepts(AcceptedCharacters.None), + new StatementBlock( + Factory.Code(" \r\n").AsStatement(), + new MarkupBlock( + Factory.Markup("

"), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("foo") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Markup("

\r\n").Accepts(AcceptedCharacters.None)), + Factory.EmptyCSharp().AsStatement()), + Factory.Code("}").Hidden().Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void ParseHelperStatementCorrectlyParsesHelperWithNewlinesBetweenCloseParenAndOpenBrace() + { + ParseDocumentTest("@helper Foo(string foo)" + Environment.NewLine + + Environment.NewLine + + Environment.NewLine + + Environment.NewLine + + "{ " + Environment.NewLine + + "

@foo

" + Environment.NewLine + + "}", + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock(new HelperCodeGenerator(new LocationTagged("Foo(string foo)\r\n\r\n\r\n\r\n{", 8, 0, 8), headerComplete: true), + Factory.CodeTransition(), + Factory.MetaCode("helper ").Accepts(AcceptedCharacters.None), + Factory.Code("Foo(string foo)\r\n\r\n\r\n\r\n{").Hidden().Accepts(AcceptedCharacters.None), + new StatementBlock( + Factory.Code(" \r\n").AsStatement(), + new MarkupBlock( + Factory.Markup(@"

"), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("foo") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Markup("

\r\n").Accepts(AcceptedCharacters.None)), + Factory.EmptyCSharp().AsStatement()), + Factory.Code("}").Hidden().Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void ParseHelperStatementGivesWhitespaceAfterOpenBraceToMarkupInDesignMode() + { + ParseDocumentTest("@helper Foo(string foo) { " + Environment.NewLine + + " ", + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock(new HelperCodeGenerator(new LocationTagged("Foo(string foo) {", 8, 0, 8), headerComplete: true), + Factory.CodeTransition(), + Factory.MetaCode("helper ").Accepts(AcceptedCharacters.None), + Factory.Code(@"Foo(string foo) {").Hidden().Accepts(AcceptedCharacters.None), + new StatementBlock( + Factory.Code(" \r\n ") + .AsStatement() + .AutoCompleteWith("}")))), + designTimeParser: true, + expectedErrors: new[] + { + new RazorError( + String.Format( + RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, + "helper", "}", "{"), + new SourceLocation(1, 0, 1)) + }); + } + + [Fact] + public void ParseHelperAcceptsNestedHelpersButOutputsError() + { + ParseDocumentTest(@"@helper Foo(string foo) {" + Environment.NewLine + + " @helper Bar(string baz) {" + Environment.NewLine + + " }" + Environment.NewLine + + "}", + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock(new HelperCodeGenerator(new LocationTagged("Foo(string foo) {", 8, 0, 8), headerComplete: true), + Factory.CodeTransition(), + Factory.MetaCode("helper ").Accepts(AcceptedCharacters.None), + Factory.Code(@"Foo(string foo) {").Hidden().Accepts(AcceptedCharacters.None), + new StatementBlock( + Factory.Code("\r\n ").AsStatement(), + new HelperBlock(new HelperCodeGenerator(new LocationTagged("Bar(string baz) {", 39, 1, 12), headerComplete: true), + Factory.CodeTransition(), + Factory.MetaCode("helper ").Accepts(AcceptedCharacters.None), + Factory.Code(@"Bar(string baz) {").Hidden().Accepts(AcceptedCharacters.None), + new StatementBlock( + Factory.Code("\r\n ").AsStatement()), + Factory.Code("}").Hidden().Accepts(AcceptedCharacters.None)), + Factory.Code("\r\n").AsStatement()), + Factory.Code("}").Hidden().Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml()), + designTimeParser: true, + expectedErrors: new[] + { + new RazorError(RazorResources.ParseError_Helpers_Cannot_Be_Nested, 38, 1, 11) + }); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpImplicitExpressionTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpImplicitExpressionTest.cs new file mode 100644 index 0000000000..9e5d5de86a --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpImplicitExpressionTest.cs @@ -0,0 +1,204 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.CSharp +{ + public class CSharpImplicitExpressionTest : CsHtmlCodeParserTestBase + { + private const string TestExtraKeyword = "model"; + + public override ParserBase CreateCodeParser() + { + return new CSharpCodeParser(); + } + + [Fact] + public void NestedImplicitExpression() + { + ParseBlockTest("if (true) { @foo }", + new StatementBlock( + Factory.Code("if (true) { ").AsStatement(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("foo") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Code(" }").AsStatement())); + } + + [Fact] + public void ParseBlockAcceptsNonEnglishCharactersThatAreValidIdentifiers() + { + ImplicitExpressionTest("हळूँजद॔.", "हळूँजद॔"); + } + + [Fact] + public void ParseBlockOutputsZeroLengthCodeSpanIfInvalidCharacterFollowsTransition() + { + ParseBlockTest("@/", + new ExpressionBlock( + Factory.CodeTransition(), + Factory.EmptyCSharp() + .AsImplicitExpression(KeywordSet) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + new RazorError( + String.Format(RazorResources.ParseError_Unexpected_Character_At_Start_Of_CodeBlock_CS, "/"), + new SourceLocation(1, 0, 1))); + } + + [Fact] + public void ParseBlockOutputsZeroLengthCodeSpanIfEOFOccursAfterTransition() + { + ParseBlockTest("@", + new ExpressionBlock( + Factory.CodeTransition(), + Factory.EmptyCSharp() + .AsImplicitExpression(KeywordSet) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + new RazorError( + RazorResources.ParseError_Unexpected_EndOfFile_At_Start_Of_CodeBlock, + new SourceLocation(1, 0, 1))); + } + + [Fact] + public void ParseBlockSupportsSlashesWithinComplexImplicitExpressions() + { + ImplicitExpressionTest("DataGridColumn.Template(\"Years of Service\", e => (int)Math.Round((DateTime.Now - dt).TotalDays / 365))"); + } + + [Fact] + public void ParseBlockMethodParsesSingleIdentifierAsImplicitExpression() + { + ImplicitExpressionTest("foo"); + } + + [Fact] + public void ParseBlockMethodDoesNotAcceptSemicolonIfExpressionTerminatedByWhitespace() + { + ImplicitExpressionTest("foo ;", "foo"); + } + + [Fact] + public void ParseBlockMethodIgnoresSemicolonAtEndOfSimpleImplicitExpression() + { + RunTrailingSemicolonTest("foo"); + } + + [Fact] + public void ParseBlockMethodParsesDottedIdentifiersAsImplicitExpression() + { + ImplicitExpressionTest("foo.bar.baz"); + } + + [Fact] + public void ParseBlockMethodIgnoresSemicolonAtEndOfDottedIdentifiers() + { + RunTrailingSemicolonTest("foo.bar.baz"); + } + + [Fact] + public void ParseBlockMethodDoesNotIncludeDotAtEOFInImplicitExpression() + { + ImplicitExpressionTest("foo.bar.", "foo.bar"); + } + + [Fact] + public void ParseBlockMethodDoesNotIncludeDotFollowedByInvalidIdentifierCharacterInImplicitExpression() + { + ImplicitExpressionTest("foo.bar.0", "foo.bar"); + ImplicitExpressionTest("foo.bar.

", "foo.bar"); + } + + [Fact] + public void ParseBlockMethodDoesNotIncludeSemicolonAfterDot() + { + ImplicitExpressionTest("foo.bar.;", "foo.bar"); + } + + [Fact] + public void ParseBlockMethodTerminatesAfterIdentifierUnlessFollowedByDotOrParenInImplicitExpression() + { + ImplicitExpressionTest("foo.bar

", "foo.bar"); + } + + [Fact] + public void ParseBlockProperlyParsesParenthesesAndBalancesThemInImplicitExpression() + { + ImplicitExpressionTest(@"foo().bar(""bi\""z"", 4)(""chained method; call"").baz(@""bo""""z"", '\'', () => { return 4; }, (4+5+new { foo = bar[4] }))"); + } + + [Fact] + public void ParseBlockProperlyParsesBracketsAndBalancesThemInImplicitExpression() + { + ImplicitExpressionTest(@"foo.bar[4 * (8 + 7)][""fo\""o""].baz"); + } + + [Fact] + public void ParseBlockTerminatesImplicitExpressionAtHtmlEndTag() + { + ImplicitExpressionTest("foo().bar.baz

zoop", "foo().bar.baz"); + } + + [Fact] + public void ParseBlockTerminatesImplicitExpressionAtHtmlStartTag() + { + ImplicitExpressionTest("foo().bar.baz

zoop", "foo().bar.baz"); + } + + [Fact] + public void ParseBlockTerminatesImplicitExpressionBeforeDotIfDotNotFollowedByIdentifierStartCharacter() + { + ImplicitExpressionTest("foo().bar.baz.42", "foo().bar.baz"); + } + + [Fact] + public void ParseBlockStopsBalancingParenthesesAtEOF() + { + ImplicitExpressionTest("foo(()", "foo(()", + acceptedCharacters: AcceptedCharacters.Any, + errors: new RazorError(String.Format(RazorResources.ParseError_Expected_CloseBracket_Before_EOF, "(", ")"), new SourceLocation(4, 0, 4))); + } + + [Fact] + public void ParseBlockTerminatesImplicitExpressionIfCloseParenFollowedByAnyWhiteSpace() + { + ImplicitExpressionTest("foo.bar() (baz)", "foo.bar()"); + } + + [Fact] + public void ParseBlockTerminatesImplicitExpressionIfIdentifierFollowedByAnyWhiteSpace() + { + ImplicitExpressionTest("foo .bar() (baz)", "foo"); + } + + [Fact] + public void ParseBlockTerminatesImplicitExpressionAtLastValidPointIfDotFollowedByWhitespace() + { + ImplicitExpressionTest("foo. bar() (baz)", "foo"); + } + + [Fact] + public void ParseBlockOutputExpressionIfModuleTokenNotFollowedByBrace() + { + ImplicitExpressionTest("module.foo()"); + } + + private void RunTrailingSemicolonTest(string expr) + { + ParseBlockTest(SyntaxConstants.TransitionString + expr + ";", + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code(expr) + .AsImplicitExpression(KeywordSet) + .Accepts(AcceptedCharacters.NonWhiteSpace) + )); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpLayoutDirectiveTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpLayoutDirectiveTest.cs new file mode 100644 index 0000000000..fb99bcce28 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpLayoutDirectiveTest.cs @@ -0,0 +1,109 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Editor; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.CSharp +{ + public class CSharpLayoutDirectiveTest : CsHtmlCodeParserTestBase + { + [Theory] + [InlineData("Layout")] + [InlineData("LAYOUT")] + [InlineData("layOut")] + [InlineData("LayOut")] + private void LayoutKeywordIsCaseSensitive(string word) + { + ParseBlockTest(word, + new ExpressionBlock( + Factory.Code(word) + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace) + )); + } + + [Fact] + public void LayoutDirectiveAcceptsAllTextToEndOfLine() + { + ParseBlockTest("@layout Foo Bar Baz", + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode("layout ").Accepts(AcceptedCharacters.None), + Factory.MetaCode("Foo Bar Baz") + .With(new SetLayoutCodeGenerator("Foo Bar Baz")) + .WithEditorHints(EditorHints.VirtualPath | EditorHints.LayoutPage) + ) + ); + } + + [Fact] + public void LayoutDirectiveAcceptsAnyIfNoWhitespaceFollowingLayoutKeyword() + { + ParseBlockTest("@layout", + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode("layout") + ) + ); + } + + [Fact] + public void LayoutDirectiveOutputsMarkerSpanIfAnyWhitespaceAfterLayoutKeyword() + { + ParseBlockTest("@layout ", + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode("layout ").Accepts(AcceptedCharacters.None), + Factory.EmptyCSharp() + .AsMetaCode() + .With(new SetLayoutCodeGenerator(String.Empty)) + .WithEditorHints(EditorHints.VirtualPath | EditorHints.LayoutPage) + ) + ); + } + + [Fact] + public void LayoutDirectiveAcceptsTrailingNewlineButDoesNotIncludeItInLayoutPath() + { + ParseBlockTest("@layout Foo" + Environment.NewLine, + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode("layout ").Accepts(AcceptedCharacters.None), + Factory.MetaCode("Foo\r\n") + .With(new SetLayoutCodeGenerator("Foo")) + .Accepts(AcceptedCharacters.None) + .WithEditorHints(EditorHints.VirtualPath | EditorHints.LayoutPage) + ) + ); + } + + [Fact] + public void LayoutDirectiveCorrectlyRestoresContextAfterCompleting() + { + ParseDocumentTest("@layout Foo" + Environment.NewLine + + "@foo", + new MarkupBlock( + Factory.EmptyHtml(), + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode("layout ").Accepts(AcceptedCharacters.None), + Factory.MetaCode("Foo\r\n") + .With(new SetLayoutCodeGenerator("Foo")) + .Accepts(AcceptedCharacters.None) + .WithEditorHints(EditorHints.VirtualPath | EditorHints.LayoutPage) + ), + Factory.EmptyHtml(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("foo") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: false) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.EmptyHtml())); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpNestedStatementsTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpNestedStatementsTest.cs new file mode 100644 index 0000000000..843f88efea --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpNestedStatementsTest.cs @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.CSharp +{ + public class CSharpNestedStatementsTest : CsHtmlCodeParserTestBase + { + [Fact] + public void NestedSimpleStatement() + { + ParseBlockTest("@while(true) { foo(); }", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("while(true) { foo(); }") + .AsStatement() + .Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void NestedKeywordStatement() + { + ParseBlockTest("@while(true) { for(int i = 0; i < 10; i++) { foo(); } }", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("while(true) { for(int i = 0; i < 10; i++) { foo(); } }") + .AsStatement() + .Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void NestedCodeBlock() + { + ParseBlockTest("@while(true) { { { { foo(); } } } }", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("while(true) { { { { foo(); } } } }") + .AsStatement() + .Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void NestedImplicitExpression() + { + ParseBlockTest("@while(true) { @foo }", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("while(true) { ") + .AsStatement(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("foo") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Code(" }") + .AsStatement() + .Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void NestedExplicitExpression() + { + ParseBlockTest("@while(true) { @(foo) }", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("while(true) { ") + .AsStatement(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.MetaCode("(") + .Accepts(AcceptedCharacters.None), + Factory.Code("foo") + .AsExpression(), + Factory.MetaCode(")") + .Accepts(AcceptedCharacters.None)), + Factory.Code(" }") + .AsStatement() + .Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void NestedMarkupBlock() + { + ParseBlockTest("@while(true) {

Hello

}", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("while(true) {") + .AsStatement(), + new MarkupBlock( + Factory.Markup("

Hello

") + .With(new MarkupCodeGenerator()) + .Accepts(AcceptedCharacters.None)), + Factory.Code("}") + .AsStatement() + .Accepts(AcceptedCharacters.None))); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpRazorCommentsTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpRazorCommentsTest.cs new file mode 100644 index 0000000000..c3f4aefc23 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpRazorCommentsTest.cs @@ -0,0 +1,174 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.CSharp +{ + public class CSharpRazorCommentsTest : CsHtmlMarkupParserTestBase + { + [Fact] + public void UnterminatedRazorComment() + { + ParseDocumentTest("@*", + new MarkupBlock( + Factory.EmptyHtml(), + new CommentBlock( + Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition) + .Accepts(AcceptedCharacters.None), + Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar) + .Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Comment, new HtmlSymbol( + Factory.LocationTracker.CurrentLocation, + String.Empty, + HtmlSymbolType.Unknown)) + .Accepts(AcceptedCharacters.Any))), + new RazorError(RazorResources.ParseError_RazorComment_Not_Terminated, 0, 0, 0)); + } + + [Fact] + public void EmptyRazorComment() + { + ParseDocumentTest("@**@", + new MarkupBlock( + Factory.EmptyHtml(), + new CommentBlock( + Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition) + .Accepts(AcceptedCharacters.None), + Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar) + .Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Comment, new HtmlSymbol( + Factory.LocationTracker.CurrentLocation, + String.Empty, + HtmlSymbolType.Unknown)) + .Accepts(AcceptedCharacters.Any), + Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar) + .Accepts(AcceptedCharacters.None), + Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition) + .Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void RazorCommentInImplicitExpressionMethodCall() + { + ParseDocumentTest("@foo(" + Environment.NewLine + + "@**@" + Environment.NewLine, + new MarkupBlock( + Factory.EmptyHtml(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("foo(\r\n") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords), + new CommentBlock( + Factory.CodeTransition(CSharpSymbolType.RazorCommentTransition) + .Accepts(AcceptedCharacters.None), + Factory.MetaCode("*", CSharpSymbolType.RazorCommentStar) + .Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Comment, new CSharpSymbol( + Factory.LocationTracker.CurrentLocation, + String.Empty, + CSharpSymbolType.Unknown)) + .Accepts(AcceptedCharacters.Any), + Factory.MetaCode("*", CSharpSymbolType.RazorCommentStar) + .Accepts(AcceptedCharacters.None), + Factory.CodeTransition(CSharpSymbolType.RazorCommentTransition) + .Accepts(AcceptedCharacters.None)), + Factory.Code("\r\n") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords))), + new RazorError( + String.Format(RazorResources.ParseError_Expected_CloseBracket_Before_EOF, "(", ")"), + 4, 0, 4)); + } + + [Fact] + public void UnterminatedRazorCommentInImplicitExpressionMethodCall() + { + ParseDocumentTest("@foo(@*", + new MarkupBlock( + Factory.EmptyHtml(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("foo(") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords), + new CommentBlock( + Factory.CodeTransition(CSharpSymbolType.RazorCommentTransition) + .Accepts(AcceptedCharacters.None), + Factory.MetaCode("*", CSharpSymbolType.RazorCommentStar) + .Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Comment, new CSharpSymbol( + Factory.LocationTracker.CurrentLocation, + String.Empty, + CSharpSymbolType.Unknown)) + .Accepts(AcceptedCharacters.Any)))), + new RazorError(RazorResources.ParseError_RazorComment_Not_Terminated, 5, 0, 5), + new RazorError(String.Format(RazorResources.ParseError_Expected_CloseBracket_Before_EOF, "(", ")"), 4, 0, 4)); + } + + [Fact] + public void RazorCommentInVerbatimBlock() + { + ParseDocumentTest("@{" + Environment.NewLine + + " Foo

}", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator(String.Empty), + Factory.CodeTransition(), + Factory.MetaCode("section ")), + Factory.Markup("9 {

Foo

}")), + new RazorError( + String.Format( + RazorResources.ParseError_Unexpected_Character_At_Section_Name_Start, + String.Format(RazorResources.ErrorComponent_Character, "9")), + 9, 0, 9)); + } + + [Fact] + public void ParseSectionBlockReportsErrorAndTerminatesSectionBlockIfNameNotFollowedByOpenBrace() + { + ParseDocumentTest("@section foo-bar {

Foo

}", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("foo"), + Factory.CodeTransition(), + Factory.MetaCode("section foo")), + Factory.Markup("-bar {

Foo

}")), + new RazorError(RazorResources.ParseError_MissingOpenBraceAfterSection, 12, 0, 12)); + } + + [Fact] + public void ParserOutputsErrorOnNestedSections() + { + ParseDocumentTest("@section foo { @section bar {

Foo

} }", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("foo"), + Factory.CodeTransition(), + Factory.MetaCode("section foo {") + .AutoCompleteWith(null, atEndOfSpan: true), + new MarkupBlock( + Factory.Markup(" "), + new SectionBlock(new SectionCodeGenerator("bar"), + Factory.CodeTransition(), + Factory.MetaCode("section bar {") + .AutoCompleteWith(null, atEndOfSpan: true), + new MarkupBlock( + Factory.Markup("

Foo

")), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + Factory.Markup(" ")), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml()), + new RazorError( + String.Format( + RazorResources.ParseError_Sections_Cannot_Be_Nested, + RazorResources.SectionExample_CS), + 23, 0, 23)); + } + + [Fact] + public void ParseSectionBlockHandlesEOFAfterOpenBrace() + { + ParseDocumentTest("@section foo {", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("foo"), + Factory.CodeTransition(), + Factory.MetaCode("section foo {") + .AutoCompleteWith("}", atEndOfSpan: true), + new MarkupBlock())), + new RazorError( + String.Format(RazorResources.ParseError_Expected_X, "}"), + 14, 0, 14)); + } + + [Fact] + public void ParseSectionBlockHandlesUnterminatedSection() + { + ParseDocumentTest("@section foo {

Foo{}

", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("foo"), + Factory.CodeTransition(), + Factory.MetaCode("section foo {") + .AutoCompleteWith("}", atEndOfSpan: true), + new MarkupBlock( + // Need to provide the markup span as fragments, since the parser will split the {} into separate symbols. + Factory.Markup("

Foo", "{", "}", "

")))), + new RazorError( + String.Format(RazorResources.ParseError_Expected_X, "}"), + 27, 0, 27)); + } + + [Fact] + public void ParseSectionBlockReportsErrorAndAcceptsWhitespaceToEndOfLineIfSectionNotFollowedByOpenBrace() + { + ParseDocumentTest("@section foo " + Environment.NewLine, + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("foo"), + Factory.CodeTransition(), + Factory.MetaCode("section foo \r\n"))), + new RazorError(RazorResources.ParseError_MissingOpenBraceAfterSection, 12, 0, 12)); + } + + [Fact] + public void ParseSectionBlockAcceptsOpenBraceMultipleLinesBelowSectionName() + { + ParseDocumentTest("@section foo " + Environment.NewLine + + Environment.NewLine + + Environment.NewLine + + Environment.NewLine + + Environment.NewLine + + Environment.NewLine + + "{" + Environment.NewLine + + "

Foo

" + Environment.NewLine + + "}", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("foo"), + Factory.CodeTransition(), + Factory.MetaCode("section foo \r\n\r\n\r\n\r\n\r\n\r\n{") + .AutoCompleteWith(null, atEndOfSpan: true), + new MarkupBlock( + Factory.Markup("\r\n

Foo

\r\n")), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void ParseSectionBlockParsesNamedSectionCorrectly() + { + ParseDocumentTest("@section foo {

Foo

}", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("foo"), + Factory.CodeTransition(), + Factory.MetaCode("section foo {") + .AutoCompleteWith(null, atEndOfSpan: true), + new MarkupBlock( + Factory.Markup("

Foo

")), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void ParseSectionBlockDoesNotRequireSpaceBetweenSectionNameAndOpenBrace() + { + ParseDocumentTest("@section foo{

Foo

}", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("foo"), + Factory.CodeTransition(), + Factory.MetaCode("section foo{") + .AutoCompleteWith(null, atEndOfSpan: true), + new MarkupBlock( + Factory.Markup("

Foo

")), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void ParseSectionBlockBalancesBraces() + { + ParseDocumentTest("@section foo { }", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("foo"), + Factory.CodeTransition(), + Factory.MetaCode("section foo {") + .AutoCompleteWith(null, atEndOfSpan: true), + new MarkupBlock( + Factory.Markup(" ")), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void ParseSectionBlockAllowsBracesInCSharpExpression() + { + ParseDocumentTest("@section foo { I really want to render a close brace, so here I go: @(\"}\") }", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("foo"), + Factory.CodeTransition(), + Factory.MetaCode("section foo {") + .AutoCompleteWith(null, atEndOfSpan: true), + new MarkupBlock( + Factory.Markup(" I really want to render a close brace, so here I go: "), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.MetaCode("(").Accepts(AcceptedCharacters.None), + Factory.Code("\"}\"").AsExpression(), + Factory.MetaCode(")").Accepts(AcceptedCharacters.None)), + Factory.Markup(" ")), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void SectionIsCorrectlyTerminatedWhenCloseBraceImmediatelyFollowsCodeBlock() + { + ParseDocumentTest("@section Foo {" + Environment.NewLine + + "@if(true) {" + Environment.NewLine + + "}" + Environment.NewLine + + "}", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("Foo"), + Factory.CodeTransition(), + Factory.MetaCode("section Foo {") + .AutoCompleteWith(null, atEndOfSpan: true), + new MarkupBlock( + Factory.Markup("\r\n"), + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("if(true) {\r\n}\r\n").AsStatement() + )), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void SectionIsCorrectlyTerminatedWhenCloseBraceImmediatelyFollowsCodeBlockNoWhitespace() + { + ParseDocumentTest("@section Foo {" + Environment.NewLine + + "@if(true) {" + Environment.NewLine + + "}}", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("Foo"), + Factory.CodeTransition(), + Factory.MetaCode("section Foo {") + .AutoCompleteWith(null, atEndOfSpan: true), + new MarkupBlock( + Factory.Markup("\r\n"), + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("if(true) {\r\n}").AsStatement() + )), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void ParseSectionBlockCorrectlyTerminatesWhenCloseBraceImmediatelyFollowsMarkup() + { + ParseDocumentTest("@section foo {something}", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("foo"), + Factory.CodeTransition(), + Factory.MetaCode("section foo {") + .AutoCompleteWith(null, atEndOfSpan: true), + new MarkupBlock( + Factory.Markup("something")), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void ParseSectionBlockParsesComment() + { + ParseDocumentTest("@section s {}", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("s"), + Factory.CodeTransition(), + Factory.MetaCode("section s {") + .AutoCompleteWith(null, atEndOfSpan: true), + new MarkupBlock( + Factory.Markup("")), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + // This was a user reported bug (codeplex #710), the section parser wasn't handling + // comments. + [Fact] + public void ParseSectionBlockParsesCommentWithDelimiters() + { + ParseDocumentTest("@section s {}", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("s"), + Factory.CodeTransition(), + Factory.MetaCode("section s {") + .AutoCompleteWith(null, atEndOfSpan: true), + new MarkupBlock( + Factory.Markup("")), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void ParseSectionBlockCommentRecoversFromUnclosedTag() + { + ParseDocumentTest( + "@section s {" + Environment.NewLine + " \" '-->}", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("s"), + Factory.CodeTransition(), + Factory.MetaCode("section s {") + .AutoCompleteWith(null, atEndOfSpan: true), + new MarkupBlock( + Factory.Markup(Environment.NewLine + " \" '-->")), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void ParseSectionBlockParsesXmlProcessingInstruction() + { + ParseDocumentTest( + "@section s { }", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("s"), + Factory.CodeTransition(), + Factory.MetaCode("section s {") + .AutoCompleteWith(null, atEndOfSpan: true), + new MarkupBlock( + Factory.Markup(" ")), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpSpecialBlockTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpSpecialBlockTest.cs new file mode 100644 index 0000000000..ef749a554f --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpSpecialBlockTest.cs @@ -0,0 +1,198 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.CSharp +{ + public class CSharpSpecialBlockTest : CsHtmlCodeParserTestBase + { + [Fact] + public void ParseInheritsStatementMarksInheritsSpanAsCanGrowIfMissingTrailingSpace() + { + ParseBlockTest("inherits", + new DirectiveBlock( + Factory.MetaCode("inherits").Accepts(AcceptedCharacters.Any) + ), + new RazorError( + RazorResources.ParseError_InheritsKeyword_Must_Be_Followed_By_TypeName, + new SourceLocation(8, 0, 8))); + } + + [Fact] + public void InheritsBlockAcceptsMultipleGenericArguments() + { + ParseBlockTest("inherits Foo.Bar, string, int>.Baz", + new DirectiveBlock( + Factory.MetaCode("inherits ").Accepts(AcceptedCharacters.None), + Factory.Code("Foo.Bar, string, int>.Baz") + .AsBaseType("Foo.Bar, string, int>.Baz") + )); + } + + [Fact] + public void InheritsBlockOutputsErrorIfInheritsNotFollowedByTypeButAcceptsEntireLineAsCode() + { + ParseBlockTest("inherits " + Environment.NewLine + + "foo", + new DirectiveBlock( + Factory.MetaCode("inherits ").Accepts(AcceptedCharacters.None), + Factory.Code(" \r\n") + .AsBaseType(String.Empty) + ), + new RazorError(RazorResources.ParseError_InheritsKeyword_Must_Be_Followed_By_TypeName, 24, 0, 24)); + } + + [Fact] + public void NamespaceImportInsideCodeBlockCausesError() + { + ParseBlockTest("{ using Foo.Bar.Baz; var foo = bar; }", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code(" using Foo.Bar.Baz; var foo = bar; ").AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None) + ), + new RazorError( + RazorResources.ParseError_NamespaceImportAndTypeAlias_Cannot_Exist_Within_CodeBlock, + new SourceLocation(2, 0, 2))); + } + + [Fact] + public void TypeAliasInsideCodeBlockIsNotHandledSpecially() + { + ParseBlockTest("{ using Foo = Bar.Baz; var foo = bar; }", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code(" using Foo = Bar.Baz; var foo = bar; ").AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None) + ), + new RazorError( + RazorResources.ParseError_NamespaceImportAndTypeAlias_Cannot_Exist_Within_CodeBlock, + new SourceLocation(2, 0, 2))); + } + + [Fact] + public void Plan9FunctionsKeywordInsideCodeBlockIsNotHandledSpecially() + { + ParseBlockTest("{ functions Foo; }", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code(" functions Foo; ").AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void NonKeywordStatementInCodeBlockIsHandledCorrectly() + { + ParseBlockTest("{" + Environment.NewLine + + " List photos = gallery.Photo.ToList();" + Environment.NewLine + + "}", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code("\r\n List photos = gallery.Photo.ToList();\r\n").AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockBalancesBracesOutsideStringsIfFirstCharacterIsBraceAndReturnsSpanOfTypeCode() + { + // Arrange + const string code = "foo\"b}ar\" if(condition) { String.Format(\"{0}\"); } "; + + // Act/Assert + ParseBlockTest("{" + code + "}", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code(code).AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockBalancesParensOutsideStringsIfFirstCharacterIsParenAndReturnsSpanOfTypeExpression() + { + // Arrange + const string code = "foo\"b)ar\" if(condition) { String.Format(\"{0}\"); } "; + + // Act/Assert + ParseBlockTest("(" + code + ")", + new ExpressionBlock( + Factory.MetaCode("(").Accepts(AcceptedCharacters.None), + Factory.Code(code).AsExpression(), + Factory.MetaCode(")").Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockBalancesBracesAndOutputsContentAsClassLevelCodeSpanIfFirstIdentifierIsFunctionsKeyword() + { + const string code = " foo(); \"bar}baz\" "; + ParseBlockTest("functions {" + code + "} zoop", + new FunctionsBlock( + Factory.MetaCode("functions {").Accepts(AcceptedCharacters.None), + Factory.Code(code).AsFunctionsBody(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockDoesNoErrorRecoveryForFunctionsBlock() + { + ParseBlockTest("functions { { { { { } zoop", + new FunctionsBlock( + Factory.MetaCode("functions {").Accepts(AcceptedCharacters.None), + Factory.Code(" { { { { } zoop").AsFunctionsBody() + ), + new RazorError( + String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, "functions", "}", "{"), + SourceLocation.Zero)); + } + + [Fact] + public void ParseBlockIgnoresFunctionsUnlessAllLowerCase() + { + ParseBlockTest("Functions { foo() }", + new ExpressionBlock( + Factory.Code("Functions") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace))); + } + + [Fact] + public void ParseBlockIgnoresSingleSlashAtStart() + { + ParseBlockTest("@/ foo", + new ExpressionBlock( + Factory.CodeTransition(), + Factory.EmptyCSharp() + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + new RazorError( + String.Format(RazorResources.ParseError_Unexpected_Character_At_Start_Of_CodeBlock_CS, "/"), + 1, 0, 1)); + } + + [Fact] + public void ParseBlockTerminatesSingleLineCommentAtEndOfLine() + { + ParseBlockTest("if(!false) {" + Environment.NewLine + + " // Foo" + Environment.NewLine + + "\t

A real tag!

" + Environment.NewLine + + "}", + new StatementBlock( + Factory.Code("if(!false) {\r\n // Foo\r\n").AsStatement(), + new MarkupBlock( + Factory.Markup("\t

A real tag!

\r\n") + .Accepts(AcceptedCharacters.None)), + Factory.Code("}").AsStatement() + )); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpStatementTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpStatementTest.cs new file mode 100644 index 0000000000..fdf67a2efc --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpStatementTest.cs @@ -0,0 +1,210 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.CSharp +{ + // Basic Tests for C# Statements: + // * Basic case for each statement + // * Basic case for ALL clauses + + // This class DOES NOT contain + // * Error cases + // * Tests for various types of nested statements + // * Comment tests + + public class CSharpStatementTest : CsHtmlCodeParserTestBase + { + [Fact] + public void ForStatement() + { + ParseBlockTest("@for(int i = 0; i++; i < length) { foo(); }", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("for(int i = 0; i++; i < length) { foo(); }") + .AsStatement() + .Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ForEachStatement() + { + ParseBlockTest("@foreach(var foo in bar) { foo(); }", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("foreach(var foo in bar) { foo(); }") + .AsStatement() + .Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void WhileStatement() + { + ParseBlockTest("@while(true) { foo(); }", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("while(true) { foo(); }") + .AsStatement() + .Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void SwitchStatement() + { + ParseBlockTest("@switch(foo) { foo(); }", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("switch(foo) { foo(); }") + .AsStatement() + .Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void LockStatement() + { + ParseBlockTest("@lock(baz) { foo(); }", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("lock(baz) { foo(); }") + .AsStatement() + .Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void IfStatement() + { + ParseBlockTest("@if(true) { foo(); }", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("if(true) { foo(); }") + .AsStatement() + )); + } + + [Fact] + public void ElseIfClause() + { + ParseBlockTest("@if(true) { foo(); } else if(false) { foo(); } else if(!false) { foo(); }", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("if(true) { foo(); } else if(false) { foo(); } else if(!false) { foo(); }") + .AsStatement() + )); + } + + [Fact] + public void ElseClause() + { + ParseBlockTest("@if(true) { foo(); } else { foo(); }", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("if(true) { foo(); } else { foo(); }") + .AsStatement() + .Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void TryStatement() + { + ParseBlockTest("@try { foo(); }", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("try { foo(); }") + .AsStatement() + )); + } + + [Fact] + public void CatchClause() + { + ParseBlockTest("@try { foo(); } catch(IOException ioex) { handleIO(); } catch(Exception ex) { handleOther(); }", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("try { foo(); } catch(IOException ioex) { handleIO(); } catch(Exception ex) { handleOther(); }") + .AsStatement() + )); + } + + [Fact] + public void FinallyClause() + { + ParseBlockTest("@try { foo(); } finally { Dispose(); }", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("try { foo(); } finally { Dispose(); }") + .AsStatement() + .Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void UsingStatement() + { + ParseBlockTest("@using(var foo = new Foo()) { foo.Bar(); }", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("using(var foo = new Foo()) { foo.Bar(); }") + .AsStatement() + .Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void UsingTypeAlias() + { + ParseBlockTest("@using StringDictionary = System.Collections.Generic.Dictionary", + new DirectiveBlock( + Factory.CodeTransition(), + Factory.Code("using StringDictionary = System.Collections.Generic.Dictionary") + .AsNamespaceImport(" StringDictionary = System.Collections.Generic.Dictionary", 5) + .Accepts(AcceptedCharacters.AnyExceptNewline) + )); + } + + [Fact] + public void UsingNamespaceImport() + { + ParseBlockTest("@using System.Text.Encoding.ASCIIEncoding", + new DirectiveBlock( + Factory.CodeTransition(), + Factory.Code("using System.Text.Encoding.ASCIIEncoding") + .AsNamespaceImport(" System.Text.Encoding.ASCIIEncoding", 5) + .Accepts(AcceptedCharacters.AnyExceptNewline) + )); + } + + [Fact] + public void DoStatement() + { + ParseBlockTest("@do { foo(); } while(true);", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("do { foo(); } while(true);") + .AsStatement() + .Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void NonBlockKeywordTreatedAsImplicitExpression() + { + ParseBlockTest("@is foo", + new ExpressionBlock(new ExpressionCodeGenerator(), + Factory.CodeTransition(), + Factory.Code("is") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace) + )); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpTemplateTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpTemplateTest.cs new file mode 100644 index 0000000000..e8e6289122 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpTemplateTest.cs @@ -0,0 +1,261 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Editor; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.CSharp +{ + public class CSharpTemplateTest : CsHtmlCodeParserTestBase + { + private const string TestTemplateCode = " @

Foo #@item

"; + + private TemplateBlock TestTemplate() + { + return new TemplateBlock( + new MarkupBlock( + Factory.MarkupTransition(), + Factory.Markup("

Foo #"), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("item") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace) + ), + Factory.Markup("

").Accepts(AcceptedCharacters.None) + ) + ); + } + + private const string TestNestedTemplateCode = " @

Foo #@Html.Repeat(10, @

@item

)

"; + + private TemplateBlock TestNestedTemplate() + { + return new TemplateBlock( + new MarkupBlock( + Factory.MarkupTransition(), + Factory.Markup("

Foo #"), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("Html.Repeat(10, ") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords), + new TemplateBlock( + new MarkupBlock( + Factory.MarkupTransition(), + Factory.Markup("

"), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("item") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace) + ), + Factory.Markup("

").Accepts(AcceptedCharacters.None) + ) + ), + Factory.Code(")") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace) + ), + Factory.Markup("

").Accepts(AcceptedCharacters.None) + ) + ); + } + + [Fact] + public void ParseBlockHandlesSingleLineTemplate() + { + ParseBlockTest("{ var foo = @: bar" + Environment.NewLine + + "; }", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code(" var foo = ").AsStatement(), + new TemplateBlock( + new MarkupBlock( + Factory.MarkupTransition(), + Factory.MetaMarkup(":", HtmlSymbolType.Colon), + Factory.Markup(" bar\r\n") + .With(new SingleLineMarkupEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString)) + .Accepts(AcceptedCharacters.None) + ) + ), + Factory.Code("; ").AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockHandlesSingleLineImmediatelyFollowingStatementChar() + { + ParseBlockTest("{i@: bar" + Environment.NewLine + + "}", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code("i").AsStatement(), + new TemplateBlock( + new MarkupBlock( + Factory.MarkupTransition(), + Factory.MetaMarkup(":", HtmlSymbolType.Colon), + Factory.Markup(" bar\r\n") + .With(new SingleLineMarkupEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString)) + .Accepts(AcceptedCharacters.None) + ) + ), + Factory.EmptyCSharp().AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockHandlesSimpleTemplateInExplicitExpressionParens() + { + ParseBlockTest("(Html.Repeat(10," + TestTemplateCode + "))", + new ExpressionBlock( + Factory.MetaCode("(").Accepts(AcceptedCharacters.None), + Factory.Code("Html.Repeat(10, ").AsExpression(), + TestTemplate(), + Factory.Code(")").AsExpression(), + Factory.MetaCode(")").Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockHandlesSimpleTemplateInImplicitExpressionParens() + { + ParseBlockTest("Html.Repeat(10," + TestTemplateCode + ")", + new ExpressionBlock( + Factory.Code("Html.Repeat(10, ") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords), + TestTemplate(), + Factory.Code(")") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace) + )); + } + + [Fact] + public void ParseBlockHandlesTwoTemplatesInImplicitExpressionParens() + { + ParseBlockTest("Html.Repeat(10," + TestTemplateCode + "," + TestTemplateCode + ")", + new ExpressionBlock( + Factory.Code("Html.Repeat(10, ") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords), + TestTemplate(), + Factory.Code(", ") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords), + TestTemplate(), + Factory.Code(")") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharacters.NonWhiteSpace) + )); + } + + [Fact] + public void ParseBlockProducesErrorButCorrectlyParsesNestedTemplateInImplicitExpressionParens() + { + ParseBlockTest("Html.Repeat(10," + TestNestedTemplateCode + ")", + new ExpressionBlock( + Factory.Code("Html.Repeat(10, ") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords), + TestNestedTemplate(), + Factory.Code(")") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace) + ), + GetNestedTemplateError(42)); + } + + [Fact] + public void ParseBlockHandlesSimpleTemplateInStatementWithinCodeBlock() + { + ParseBlockTest("foreach(foo in Bar) { Html.ExecuteTemplate(foo," + TestTemplateCode + "); }", + new StatementBlock( + Factory.Code("foreach(foo in Bar) { Html.ExecuteTemplate(foo, ").AsStatement(), + TestTemplate(), + Factory.Code("); }") + .AsStatement() + .Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockHandlesTwoTemplatesInStatementWithinCodeBlock() + { + ParseBlockTest("foreach(foo in Bar) { Html.ExecuteTemplate(foo," + TestTemplateCode + "," + TestTemplateCode + "); }", + new StatementBlock( + Factory.Code("foreach(foo in Bar) { Html.ExecuteTemplate(foo, ").AsStatement(), + TestTemplate(), + Factory.Code(", ").AsStatement(), + TestTemplate(), + Factory.Code("); }") + .AsStatement() + .Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockProducesErrorButCorrectlyParsesNestedTemplateInStatementWithinCodeBlock() + { + ParseBlockTest("foreach(foo in Bar) { Html.ExecuteTemplate(foo," + TestNestedTemplateCode + "); }", + new StatementBlock( + Factory.Code("foreach(foo in Bar) { Html.ExecuteTemplate(foo, ").AsStatement(), + TestNestedTemplate(), + Factory.Code("); }") + .AsStatement() + .Accepts(AcceptedCharacters.None) + ), + GetNestedTemplateError(74)); + } + + [Fact] + public void ParseBlockHandlesSimpleTemplateInStatementWithinStatementBlock() + { + ParseBlockTest("{ var foo = bar; Html.ExecuteTemplate(foo," + TestTemplateCode + "); }", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code(" var foo = bar; Html.ExecuteTemplate(foo, ").AsStatement(), + TestTemplate(), + Factory.Code("); ").AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockHandlessTwoTemplatesInStatementWithinStatementBlock() + { + ParseBlockTest("{ var foo = bar; Html.ExecuteTemplate(foo," + TestTemplateCode + "," + TestTemplateCode + "); }", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code(" var foo = bar; Html.ExecuteTemplate(foo, ").AsStatement(), + TestTemplate(), + Factory.Code(", ").AsStatement(), + TestTemplate(), + Factory.Code("); ").AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockProducesErrorButCorrectlyParsesNestedTemplateInStatementWithinStatementBlock() + { + ParseBlockTest("{ var foo = bar; Html.ExecuteTemplate(foo," + TestNestedTemplateCode + "); }", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code(" var foo = bar; Html.ExecuteTemplate(foo, ").AsStatement(), + TestNestedTemplate(), + Factory.Code("); ").AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None) + ), + GetNestedTemplateError(69)); + } + + private static RazorError GetNestedTemplateError(int characterIndex) + { + return new RazorError(RazorResources.ParseError_InlineMarkup_Blocks_Cannot_Be_Nested, new SourceLocation(characterIndex, 0, characterIndex)); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpToMarkupSwitchTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpToMarkupSwitchTest.cs new file mode 100644 index 0000000000..ff1022118e --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpToMarkupSwitchTest.cs @@ -0,0 +1,494 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Editor; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.CSharp +{ + public class CSharpToMarkupSwitchTest : CsHtmlCodeParserTestBase + { + [Fact] + public void SingleAngleBracketDoesNotCauseSwitchIfOuterBlockIsTerminated() + { + ParseBlockTest("{ List< }", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code(" List< ").AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockGivesSpacesToCodeOnAtTagTemplateTransitionInDesignTimeMode() + { + ParseBlockTest("Foo( @

Foo

)", + new ExpressionBlock( + Factory.Code("Foo( ") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.Any), + new TemplateBlock( + new MarkupBlock( + Factory.MarkupTransition(), + Factory.Markup("

Foo

").Accepts(AcceptedCharacters.None) + ) + ), + Factory.Code(" )") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace) + ), designTimeParser: true); + } + + [Fact] + public void ParseBlockGivesSpacesToCodeOnAtColonTemplateTransitionInDesignTimeMode() + { + ParseBlockTest("Foo( " + Environment.NewLine + + "@:

Foo

" + Environment.NewLine + + ")", + new ExpressionBlock( + Factory.Code("Foo( \r\n").AsImplicitExpression(CSharpCodeParser.DefaultKeywords), + new TemplateBlock( + new MarkupBlock( + Factory.MarkupTransition(), + Factory.MetaMarkup(":", HtmlSymbolType.Colon), + Factory.Markup("

Foo

\r\n") + .With(new SingleLineMarkupEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString, AcceptedCharacters.None)) + ) + ), + Factory.Code(")") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace) + ), designTimeParser: true); + } + + [Fact] + public void ParseBlockGivesSpacesToCodeOnTagTransitionInDesignTimeMode() + { + ParseBlockTest("{" + Environment.NewLine + + "

Foo

" + Environment.NewLine + + "}", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code("\r\n ").AsStatement(), + new MarkupBlock( + Factory.Markup("

Foo

").Accepts(AcceptedCharacters.None) + ), + Factory.Code(" \r\n").AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None) + ), designTimeParser: true); + } + + [Fact] + public void ParseBlockGivesSpacesToCodeOnInvalidAtTagTransitionInDesignTimeMode() + { + ParseBlockTest("{" + Environment.NewLine + + " @

Foo

" + Environment.NewLine + + "}", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code("\r\n ").AsStatement(), + new MarkupBlock( + Factory.MarkupTransition(), + Factory.Markup("

Foo

").Accepts(AcceptedCharacters.None) + ), + Factory.Code(" \r\n").AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None) + ), true, + new RazorError(RazorResources.ParseError_AtInCode_Must_Be_Followed_By_Colon_Paren_Or_Identifier_Start, 7, 1, 4)); + } + + [Fact] + public void ParseBlockGivesSpacesToCodeOnAtColonTransitionInDesignTimeMode() + { + ParseBlockTest("{" + Environment.NewLine + + " @:

Foo

" + Environment.NewLine + + "}", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code("\r\n ").AsStatement(), + new MarkupBlock( + Factory.MarkupTransition(), + Factory.MetaMarkup(":", HtmlSymbolType.Colon), + Factory.Markup("

Foo

\r\n") + .With(new SingleLineMarkupEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString, AcceptedCharacters.None)) + ), + Factory.EmptyCSharp().AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None) + ), designTimeParser: true); + } + + [Fact] + public void ParseBlockShouldSupportSingleLineMarkupContainingStatementBlock() + { + ParseBlockTest("Repeat(10," + Environment.NewLine + + " @: @{}" + Environment.NewLine + + ")", + new ExpressionBlock( + Factory.Code("Repeat(10,\r\n ") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords), + new TemplateBlock( + new MarkupBlock( + Factory.MarkupTransition(), + Factory.MetaMarkup(":", HtmlSymbolType.Colon), + Factory.Markup(" ") + .With(new SingleLineMarkupEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString)), + new StatementBlock( + Factory.CodeTransition(), + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.EmptyCSharp().AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None) + ), + Factory.Markup("\r\n") + .With(new SingleLineMarkupEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString, AcceptedCharacters.None)) + ) + ), + Factory.Code(")") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace) + )); + } + + [Fact] + public void ParseBlockShouldSupportMarkupWithoutPreceedingWhitespace() + { + ParseBlockTest("foreach(var file in files){" + Environment.NewLine + + Environment.NewLine + + Environment.NewLine + + "@:Baz" + Environment.NewLine + + "
" + Environment.NewLine + + "Foo" + Environment.NewLine + + "@:Bar" + Environment.NewLine + + "}", + new StatementBlock( + Factory.Code("foreach(var file in files){\r\n\r\n\r\n").AsStatement(), + new MarkupBlock( + Factory.MarkupTransition(), + Factory.MetaMarkup(":", HtmlSymbolType.Colon), + Factory.Markup("Baz\r\n") + .With(new SingleLineMarkupEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString, AcceptedCharacters.None)) + ), + new MarkupBlock( + Factory.Markup("
\r\n") + .Accepts(AcceptedCharacters.None) + ), + new MarkupBlock( + Factory.Markup("Foo\r\n") + .Accepts(AcceptedCharacters.None) + ), + new MarkupBlock( + Factory.MarkupTransition(), + Factory.MetaMarkup(":", HtmlSymbolType.Colon), + Factory.Markup("Bar\r\n") + .With(new SingleLineMarkupEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString, AcceptedCharacters.None)) + ), + Factory.Code("}").AsStatement().Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockGivesAllWhitespaceOnSameLineExcludingPreceedingNewlineButIncludingTrailingNewLineToMarkup() + { + ParseBlockTest("if(foo) {" + Environment.NewLine + + " var foo = \"After this statement there are 10 spaces\"; " + Environment.NewLine + + "

" + Environment.NewLine + + " Foo" + Environment.NewLine + + " @bar" + Environment.NewLine + + "

" + Environment.NewLine + + " @:Hello!" + Environment.NewLine + + " var biz = boz;" + Environment.NewLine + + "}", + new StatementBlock( + Factory.Code("if(foo) {\r\n var foo = \"After this statement there are 10 spaces\"; \r\n").AsStatement(), + new MarkupBlock( + Factory.Markup("

\r\n Foo\r\n"), + new ExpressionBlock( + Factory.Code(" ").AsStatement(), + Factory.CodeTransition(), + Factory.Code("bar").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharacters.NonWhiteSpace) + ), + Factory.Markup("\r\n

\r\n").Accepts(AcceptedCharacters.None) + ), + new MarkupBlock( + Factory.Markup(" "), + Factory.MarkupTransition(), + Factory.MetaMarkup(":", HtmlSymbolType.Colon), + Factory.Markup("Hello!\r\n").With(new SingleLineMarkupEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString, AcceptedCharacters.None)) + ), + Factory.Code(" var biz = boz;\r\n}").AsStatement())); + } + + [Fact] + public void ParseBlockAllowsMarkupInIfBodyWithBraces() + { + ParseBlockTest("if(foo) {

Bar

} else if(bar) {

Baz

} else {

Boz

}", + new StatementBlock( + Factory.Code("if(foo) {").AsStatement(), + new MarkupBlock( + Factory.Markup("

Bar

").Accepts(AcceptedCharacters.None) + ), + Factory.Code("} else if(bar) {").AsStatement(), + new MarkupBlock( + Factory.Markup("

Baz

").Accepts(AcceptedCharacters.None) + ), + Factory.Code("} else {").AsStatement(), + new MarkupBlock( + Factory.Markup("

Boz

").Accepts(AcceptedCharacters.None) + ), + Factory.Code("}").AsStatement().Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockAllowsMarkupInIfBodyWithBracesWithinCodeBlock() + { + ParseBlockTest("{ if(foo) {

Bar

} else if(bar) {

Baz

} else {

Boz

} }", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code(" if(foo) {").AsStatement(), + new MarkupBlock( + Factory.Markup("

Bar

").Accepts(AcceptedCharacters.None) + ), + Factory.Code("} else if(bar) {").AsStatement(), + new MarkupBlock( + Factory.Markup("

Baz

").Accepts(AcceptedCharacters.None) + ), + Factory.Code("} else {").AsStatement(), + new MarkupBlock( + Factory.Markup("

Boz

").Accepts(AcceptedCharacters.None) + ), + Factory.Code("} ").AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockSupportsMarkupInCaseAndDefaultBranchesOfSwitch() + { + // Arrange + ParseBlockTest("switch(foo) {" + Environment.NewLine + + " case 0:" + Environment.NewLine + + "

Foo

" + Environment.NewLine + + " break;" + Environment.NewLine + + " case 1:" + Environment.NewLine + + "

Bar

" + Environment.NewLine + + " return;" + Environment.NewLine + + " case 2:" + Environment.NewLine + + " {" + Environment.NewLine + + "

Baz

" + Environment.NewLine + + "

Boz

" + Environment.NewLine + + " }" + Environment.NewLine + + " default:" + Environment.NewLine + + "

Biz

" + Environment.NewLine + + "}", + new StatementBlock( + Factory.Code("switch(foo) {\r\n case 0:\r\n").AsStatement(), + new MarkupBlock( + Factory.Markup("

Foo

\r\n").Accepts(AcceptedCharacters.None) + ), + Factory.Code(" break;\r\n case 1:\r\n").AsStatement(), + new MarkupBlock( + Factory.Markup("

Bar

\r\n").Accepts(AcceptedCharacters.None) + ), + Factory.Code(" return;\r\n case 2:\r\n {\r\n").AsStatement(), + new MarkupBlock( + Factory.Markup("

Baz

\r\n").Accepts(AcceptedCharacters.None) + ), + new MarkupBlock( + Factory.Markup("

Boz

\r\n").Accepts(AcceptedCharacters.None) + ), + Factory.Code(" }\r\n default:\r\n").AsStatement(), + new MarkupBlock( + Factory.Markup("

Biz

\r\n").Accepts(AcceptedCharacters.None) + ), + Factory.Code("}").AsStatement().Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockSupportsMarkupInCaseAndDefaultBranchesOfSwitchInCodeBlock() + { + // Arrange + ParseBlockTest("{ switch(foo) {" + Environment.NewLine + + " case 0:" + Environment.NewLine + + "

Foo

" + Environment.NewLine + + " break;" + Environment.NewLine + + " case 1:" + Environment.NewLine + + "

Bar

" + Environment.NewLine + + " return;" + Environment.NewLine + + " case 2:" + Environment.NewLine + + " {" + Environment.NewLine + + "

Baz

" + Environment.NewLine + + "

Boz

" + Environment.NewLine + + " }" + Environment.NewLine + + " default:" + Environment.NewLine + + "

Biz

" + Environment.NewLine + + "} }", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code(" switch(foo) {\r\n case 0:\r\n").AsStatement(), + new MarkupBlock( + Factory.Markup("

Foo

\r\n").Accepts(AcceptedCharacters.None) + ), + Factory.Code(" break;\r\n case 1:\r\n").AsStatement(), + new MarkupBlock( + Factory.Markup("

Bar

\r\n").Accepts(AcceptedCharacters.None) + ), + Factory.Code(" return;\r\n case 2:\r\n {\r\n").AsStatement(), + new MarkupBlock( + Factory.Markup("

Baz

\r\n").Accepts(AcceptedCharacters.None) + ), + new MarkupBlock( + Factory.Markup("

Boz

\r\n").Accepts(AcceptedCharacters.None) + ), + Factory.Code(" }\r\n default:\r\n").AsStatement(), + new MarkupBlock( + Factory.Markup("

Biz

\r\n").Accepts(AcceptedCharacters.None) + ), + Factory.Code("} ").AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockParsesMarkupStatementOnOpenAngleBracket() + { + ParseBlockTest("for(int i = 0; i < 10; i++) {

Foo

}", + new StatementBlock( + Factory.Code("for(int i = 0; i < 10; i++) {").AsStatement(), + new MarkupBlock( + Factory.Markup("

Foo

").Accepts(AcceptedCharacters.None) + ), + Factory.Code("}").AsStatement().Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockParsesMarkupStatementOnOpenAngleBracketInCodeBlock() + { + ParseBlockTest("{ for(int i = 0; i < 10; i++) {

Foo

} }", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code(" for(int i = 0; i < 10; i++) {").AsStatement(), + new MarkupBlock( + Factory.Markup("

Foo

").Accepts(AcceptedCharacters.None) + ), + Factory.Code("} ").AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockParsesMarkupStatementOnSwitchCharacterFollowedByColon() + { + // Arrange + ParseBlockTest("if(foo) { @:Bar" + Environment.NewLine + + "} zoop", + new StatementBlock( + Factory.Code("if(foo) {").AsStatement(), + new MarkupBlock( + Factory.Markup(" "), + Factory.MarkupTransition(), + Factory.MetaMarkup(":", HtmlSymbolType.Colon), + Factory.Markup("Bar\r\n").With(new SingleLineMarkupEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString, AcceptedCharacters.None)) + ), + Factory.Code("}").AsStatement())); + } + + [Fact] + public void ParseBlockParsesMarkupStatementOnSwitchCharacterFollowedByColonInCodeBlock() + { + // Arrange + ParseBlockTest("{ if(foo) { @:Bar" + Environment.NewLine + + "} } zoop", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code(" if(foo) {").AsStatement(), + new MarkupBlock( + Factory.Markup(" "), + Factory.MarkupTransition(), + Factory.MetaMarkup(":", HtmlSymbolType.Colon), + Factory.Markup("Bar\r\n").Accepts(AcceptedCharacters.None) + ), + Factory.Code("} ").AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockCorrectlyReturnsFromMarkupBlockWithPseudoTag() + { + ParseBlockTest("if (i > 0) { ; }", + new StatementBlock( + Factory.Code("if (i > 0) {").AsStatement(), + new MarkupBlock( + Factory.Markup(" "), + Factory.MarkupTransition("").Accepts(AcceptedCharacters.None), + Factory.Markup(";"), + Factory.MarkupTransition("").Accepts(AcceptedCharacters.None), + Factory.Markup(" ").Accepts(AcceptedCharacters.None) + ), + Factory.Code("}").AsStatement())); + } + + [Fact] + public void ParseBlockCorrectlyReturnsFromMarkupBlockWithPseudoTagInCodeBlock() + { + ParseBlockTest("{ if (i > 0) { ; } }", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code(" if (i > 0) {").AsStatement(), + new MarkupBlock( + Factory.Markup(" "), + Factory.MarkupTransition("").Accepts(AcceptedCharacters.None), + Factory.Markup(";"), + Factory.MarkupTransition("").Accepts(AcceptedCharacters.None), + Factory.Markup(" ").Accepts(AcceptedCharacters.None) + ), + Factory.Code("} ").AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockSupportsAllKindsOfImplicitMarkupInCodeBlock() + { + ParseBlockTest("{" + Environment.NewLine + + " if(true) {" + Environment.NewLine + + " @:Single Line Markup" + Environment.NewLine + + " }" + Environment.NewLine + + " foreach (var p in Enumerable.Range(1, 10)) {" + Environment.NewLine + + " The number is @p" + Environment.NewLine + + " }" + Environment.NewLine + + " if(!false) {" + Environment.NewLine + + "

A real tag!

" + Environment.NewLine + + " }" + Environment.NewLine + + "}", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code("\r\n if(true) {\r\n").AsStatement(), + new MarkupBlock( + Factory.Markup(" "), + Factory.MarkupTransition(), + Factory.MetaMarkup(":", HtmlSymbolType.Colon), + Factory.Markup("Single Line Markup\r\n").With(new SingleLineMarkupEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString, AcceptedCharacters.None)) + ), + Factory.Code(" }\r\n foreach (var p in Enumerable.Range(1, 10)) {\r\n").AsStatement(), + new MarkupBlock( + Factory.Markup(" "), + Factory.MarkupTransition("").Accepts(AcceptedCharacters.None), + Factory.Markup("The number is "), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("p").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharacters.NonWhiteSpace) + ), + Factory.MarkupTransition("").Accepts(AcceptedCharacters.None), + Factory.Markup("\r\n").Accepts(AcceptedCharacters.None) + ), + Factory.Code(" }\r\n if(!false) {\r\n").AsStatement(), + new MarkupBlock( + Factory.Markup("

A real tag!

\r\n").Accepts(AcceptedCharacters.None) + ), + Factory.Code(" }\r\n").AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None))); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpVerbatimBlockTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpVerbatimBlockTest.cs new file mode 100644 index 0000000000..749e420b29 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpVerbatimBlockTest.cs @@ -0,0 +1,121 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.CSharp +{ + public class CSharpVerbatimBlockTest : CsHtmlCodeParserTestBase + { + private const string TestExtraKeyword = "model"; + + [Fact] + public void VerbatimBlock() + { + ParseBlockTest("@{ foo(); }", + new StatementBlock( + Factory.CodeTransition(), + Factory.MetaCode("{") + .Accepts(AcceptedCharacters.None), + Factory.Code(" foo(); ") + .AsStatement(), + Factory.MetaCode("}") + .Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void InnerImplicitExpressionWithOnlySingleAtOutputsZeroLengthCodeSpan() + { + ParseBlockTest("{@}", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.EmptyCSharp().AsStatement(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.EmptyCSharp().AsImplicitExpression(KeywordSet, acceptTrailingDot: true).Accepts(AcceptedCharacters.NonWhiteSpace) + ), + Factory.EmptyCSharp().AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + designTimeParser: true, + expectedErrors: new[] + { + new RazorError(String.Format(RazorResources.ParseError_Unexpected_Character_At_Start_Of_CodeBlock_CS, "}"), new SourceLocation(2, 0, 2)) + }); + } + + [Fact] + public void InnerImplicitExpressionDoesNotAcceptDotAfterAt() + { + ParseBlockTest("{@.}", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.EmptyCSharp().AsStatement(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.EmptyCSharp().AsImplicitExpression(KeywordSet, acceptTrailingDot: true).Accepts(AcceptedCharacters.NonWhiteSpace) + ), + Factory.Code(".").AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + designTimeParser: true, + expectedErrors: new[] + { + new RazorError(String.Format(RazorResources.ParseError_Unexpected_Character_At_Start_Of_CodeBlock_CS, "."), new SourceLocation(2, 0, 2)) + }); + } + + [Fact] + public void InnerImplicitExpressionWithOnlySingleAtAcceptsSingleSpaceOrNewlineAtDesignTime() + { + ParseBlockTest("{" + Environment.NewLine + + " @" + Environment.NewLine + + "}", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code("\r\n ").AsStatement(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.EmptyCSharp().AsImplicitExpression(KeywordSet, acceptTrailingDot: true).Accepts(AcceptedCharacters.NonWhiteSpace) + ), + Factory.Code("\r\n").AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + /* designTimeParser */ true, + new RazorError(RazorResources.ParseError_Unexpected_WhiteSpace_At_Start_Of_CodeBlock_CS, 8, 1, 5)); + } + + [Fact] + public void InnerImplicitExpressionDoesNotAcceptTrailingNewlineInRunTimeMode() + { + ParseBlockTest("{@foo." + Environment.NewLine + + "}", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.EmptyCSharp().AsStatement(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("foo.").AsImplicitExpression(KeywordSet, acceptTrailingDot: true).Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Code("\r\n").AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void InnerImplicitExpressionAcceptsTrailingNewlineInDesignTimeMode() + { + ParseBlockTest("{@foo." + Environment.NewLine + + "}", + new StatementBlock( + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.EmptyCSharp().AsStatement(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("foo.").AsImplicitExpression(KeywordSet, acceptTrailingDot: true).Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Code("\r\n").AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + designTimeParser: true); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpWhitespaceHandlingTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpWhitespaceHandlingTest.cs new file mode 100644 index 0000000000..57dc02a190 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpWhitespaceHandlingTest.cs @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.CSharp +{ + public class CSharpWhitespaceHandlingTest : CsHtmlMarkupParserTestBase + { + [Fact] + public void StatementBlockDoesNotAcceptTrailingNewlineIfNewlinesAreSignificantToAncestor() + { + ParseBlockTest("@: @if (true) { }" + Environment.NewLine + + "}", + new MarkupBlock( + Factory.MarkupTransition() + .Accepts(AcceptedCharacters.None), + Factory.MetaMarkup(":", HtmlSymbolType.Colon), + Factory.Markup(" "), + new StatementBlock( + Factory.CodeTransition() + .Accepts(AcceptedCharacters.None), + Factory.Code("if (true) { }") + .AsStatement() + ), + Factory.Markup("\r\n") + .Accepts(AcceptedCharacters.None))); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CsHtmlDocumentTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CsHtmlDocumentTest.cs new file mode 100644 index 0000000000..99542f0dce --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CsHtmlDocumentTest.cs @@ -0,0 +1,289 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.CSharp +{ + public class CsHtmlDocumentTest : CsHtmlMarkupParserTestBase + { + [Fact] + public void UnterminatedBlockCommentCausesRazorError() + { + ParseDocumentTest("@* Foo Bar", + new MarkupBlock( + Factory.EmptyHtml(), + new CommentBlock( + Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition), + Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.Comment(" Foo Bar", HtmlSymbolType.RazorComment) + ) + ), + new RazorError(RazorResources.ParseError_RazorComment_Not_Terminated, SourceLocation.Zero)); + } + + [Fact] + public void BlockCommentInMarkupDocumentIsHandledCorrectly() + { + ParseDocumentTest("
    " + Environment.NewLine + + " @* This is a block comment
*@ foo", + new MarkupBlock( + Factory.Markup("
    \r\n "), + new CommentBlock( + Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition), + Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.Comment(" This is a block comment
", HtmlSymbolType.RazorComment), + Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition) + ), + Factory.Markup(" foo") + )); + } + + [Fact] + public void BlockCommentInMarkupBlockIsHandledCorrectly() + { + ParseBlockTest("
    " + Environment.NewLine + + " @* This is a block comment
*@ foo ", + new MarkupBlock( + Factory.Markup("
    \r\n "), + new CommentBlock( + Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition), + Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.Comment(" This is a block comment
", HtmlSymbolType.RazorComment), + Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition) + ), + Factory.Markup(" foo ").Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void BlockCommentAtStatementStartInCodeBlockIsHandledCorrectly() + { + ParseDocumentTest("@if(Request.IsAuthenticated) {" + Environment.NewLine + + " @* User is logged in! } *@" + Environment.NewLine + + " Write(\"Hello friend!\");" + Environment.NewLine + + "}", + new MarkupBlock( + Factory.EmptyHtml(), + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("if(Request.IsAuthenticated) {\r\n ").AsStatement(), + new CommentBlock( + Factory.CodeTransition(CSharpSymbolType.RazorCommentTransition), + Factory.MetaCode("*", CSharpSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.Comment(" User is logged in! } ", CSharpSymbolType.RazorComment), + Factory.MetaCode("*", CSharpSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.CodeTransition(CSharpSymbolType.RazorCommentTransition) + ), + Factory.Code("\r\n Write(\"Hello friend!\");\r\n}").AsStatement()))); + } + + [Fact] + public void BlockCommentInStatementInCodeBlockIsHandledCorrectly() + { + ParseDocumentTest("@if(Request.IsAuthenticated) {" + Environment.NewLine + + " var foo = @* User is logged in! ; *@;" + Environment.NewLine + + " Write(\"Hello friend!\");" + Environment.NewLine + + "}", + new MarkupBlock( + Factory.EmptyHtml(), + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("if(Request.IsAuthenticated) {\r\n var foo = ").AsStatement(), + new CommentBlock( + Factory.CodeTransition(CSharpSymbolType.RazorCommentTransition), + Factory.MetaCode("*", CSharpSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.Comment(" User is logged in! ; ", CSharpSymbolType.RazorComment), + Factory.MetaCode("*", CSharpSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.CodeTransition(CSharpSymbolType.RazorCommentTransition) + ), + Factory.Code(";\r\n Write(\"Hello friend!\");\r\n}").AsStatement()))); + } + + [Fact] + public void BlockCommentInStringIsIgnored() + { + ParseDocumentTest("@if(Request.IsAuthenticated) {" + Environment.NewLine + + " var foo = \"@* User is logged in! ; *\";" + Environment.NewLine + + " Write(\"Hello friend!\");" + Environment.NewLine + + "}", + new MarkupBlock( + Factory.EmptyHtml(), + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("if(Request.IsAuthenticated) {" + Environment.NewLine + + " var foo = \"@* User is logged in! ; *\";" + Environment.NewLine + + " Write(\"Hello friend!\");" + Environment.NewLine + + "}").AsStatement()))); + } + + [Fact] + public void BlockCommentInCSharpBlockCommentIsIgnored() + { + ParseDocumentTest("@if(Request.IsAuthenticated) {" + Environment.NewLine + + " var foo = /*@* User is logged in! */ *@ */;" + Environment.NewLine + + " Write(\"Hello friend!\");" + Environment.NewLine + + "}", + new MarkupBlock( + Factory.EmptyHtml(), + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("if(Request.IsAuthenticated) {" + Environment.NewLine + + " var foo = /*@* User is logged in! */ *@ */;" + Environment.NewLine + + " Write(\"Hello friend!\");" + Environment.NewLine + + "}").AsStatement()))); + } + + [Fact] + public void BlockCommentInCSharpLineCommentIsIgnored() + { + ParseDocumentTest("@if(Request.IsAuthenticated) {" + Environment.NewLine + + " var foo = //@* User is logged in! */ *@;" + Environment.NewLine + + " Write(\"Hello friend!\");" + Environment.NewLine + + "}", + new MarkupBlock( + Factory.EmptyHtml(), + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("if(Request.IsAuthenticated) {" + Environment.NewLine + + " var foo = //@* User is logged in! */ *@;" + Environment.NewLine + + " Write(\"Hello friend!\");" + Environment.NewLine + + "}").AsStatement()))); + } + + [Fact] + public void BlockCommentInImplicitExpressionIsHandledCorrectly() + { + ParseDocumentTest("@Html.Foo@*bar*@", + new MarkupBlock( + Factory.EmptyHtml(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("Html.Foo").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharacters.NonWhiteSpace) + ), + Factory.EmptyHtml(), + new CommentBlock( + Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition), + Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.Comment("bar", HtmlSymbolType.RazorComment), + Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition) + ), + Factory.EmptyHtml())); + } + + [Fact] + public void BlockCommentAfterDotOfImplicitExpressionIsHandledCorrectly() + { + ParseDocumentTest("@Html.@*bar*@", + new MarkupBlock( + Factory.EmptyHtml(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("Html").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharacters.NonWhiteSpace) + ), + Factory.Markup("."), + new CommentBlock( + Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition), + Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.Comment("bar", HtmlSymbolType.RazorComment), + Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition) + ), + Factory.EmptyHtml())); + } + + [Fact] + public void BlockCommentInParensOfImplicitExpressionIsHandledCorrectly() + { + ParseDocumentTest("@Html.Foo(@*bar*@ 4)", + new MarkupBlock( + Factory.EmptyHtml(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("Html.Foo(").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharacters.Any), + new CommentBlock( + Factory.CodeTransition(CSharpSymbolType.RazorCommentTransition), + Factory.MetaCode("*", CSharpSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.Comment("bar", CSharpSymbolType.RazorComment), + Factory.MetaCode("*", CSharpSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.CodeTransition(CSharpSymbolType.RazorCommentTransition) + ), + Factory.Code(" 4)").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharacters.NonWhiteSpace) + ), + Factory.EmptyHtml())); + } + + [Fact] + public void BlockCommentInBracketsOfImplicitExpressionIsHandledCorrectly() + { + ParseDocumentTest("@Html.Foo[@*bar*@ 4]", + new MarkupBlock( + Factory.EmptyHtml(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("Html.Foo[").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharacters.Any), + new CommentBlock( + Factory.CodeTransition(CSharpSymbolType.RazorCommentTransition), + Factory.MetaCode("*", CSharpSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.Comment("bar", CSharpSymbolType.RazorComment), + Factory.MetaCode("*", CSharpSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.CodeTransition(CSharpSymbolType.RazorCommentTransition) + ), + Factory.Code(" 4]").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharacters.NonWhiteSpace) + ), + Factory.EmptyHtml())); + } + + [Fact] + public void BlockCommentInParensOfConditionIsHandledCorrectly() + { + ParseDocumentTest("@if(@*bar*@) {}", + new MarkupBlock( + Factory.EmptyHtml(), + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("if(").AsStatement(), + new CommentBlock( + Factory.CodeTransition(CSharpSymbolType.RazorCommentTransition), + Factory.MetaCode("*", CSharpSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.Comment("bar", CSharpSymbolType.RazorComment), + Factory.MetaCode("*", CSharpSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.CodeTransition(CSharpSymbolType.RazorCommentTransition) + ), + Factory.Code(") {}").AsStatement() + ))); + } + + [Fact] + public void BlockCommentInExplicitExpressionIsHandledCorrectly() + { + ParseDocumentTest("@(1 + @*bar*@ 1)", + new MarkupBlock( + Factory.EmptyHtml(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.MetaCode("(").Accepts(AcceptedCharacters.None), + Factory.Code("1 + ").AsExpression(), + new CommentBlock( + Factory.CodeTransition(CSharpSymbolType.RazorCommentTransition), + Factory.MetaCode("*", CSharpSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.Comment("bar", CSharpSymbolType.RazorComment), + Factory.MetaCode("*", CSharpSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.CodeTransition(CSharpSymbolType.RazorCommentTransition) + ), + Factory.Code(" 1").AsExpression(), + Factory.MetaCode(")").Accepts(AcceptedCharacters.None) + ), + Factory.EmptyHtml())); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CallbackParserListenerTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CallbackParserListenerTest.cs new file mode 100644 index 0000000000..1f179252a2 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CallbackParserListenerTest.cs @@ -0,0 +1,165 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Threading; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using Microsoft.TestCommon; +using Moq; + +namespace Microsoft.AspNet.Razor.Test.Parser +{ + public class CallbackParserListenerTest + { + [Fact] + public void ListenerConstructedWithSpanCallbackCallsCallbackOnEndSpan() + { + RunOnEndSpanTest(callback => new CallbackVisitor(callback)); + } + + [Fact] + public void ListenerConstructedWithSpanCallbackDoesNotThrowOnStartBlockEndBlockOrError() + { + // Arrange + Action spanCallback = _ => { }; + CallbackVisitor listener = new CallbackVisitor(spanCallback); + + // Act/Assert + listener.VisitStartBlock(new FunctionsBlock()); + listener.VisitError(new RazorError("Error", SourceLocation.Zero)); + listener.VisitEndBlock(new FunctionsBlock()); + } + + [Fact] + public void ListenerConstructedWithSpanAndErrorCallbackCallsCallbackOnEndSpan() + { + RunOnEndSpanTest(spanCallback => new CallbackVisitor(spanCallback, _ => { })); + } + + [Fact] + public void ListenerConstructedWithSpanAndErrorCallbackCallsCallbackOnError() + { + RunOnErrorTest(errorCallback => new CallbackVisitor(_ => { }, errorCallback)); + } + + [Fact] + public void ListenerConstructedWithAllCallbacksCallsCallbackOnEndSpan() + { + RunOnEndSpanTest(spanCallback => new CallbackVisitor(spanCallback, _ => { }, _ => { }, _ => { })); + } + + [Fact] + public void ListenerConstructedWithAllCallbacksCallsCallbackOnError() + { + RunOnErrorTest(errorCallback => new CallbackVisitor(_ => { }, errorCallback, _ => { }, _ => { })); + } + + [Fact] + public void ListenerConstructedWithAllCallbacksCallsCallbackOnStartBlock() + { + RunOnStartBlockTest(startBlockCallback => new CallbackVisitor(_ => { }, _ => { }, startBlockCallback, _ => { })); + } + + [Fact] + public void ListenerConstructedWithAllCallbacksCallsCallbackOnEndBlock() + { + RunOnEndBlockTest(endBlockCallback => new CallbackVisitor(_ => { }, _ => { }, _ => { }, endBlockCallback)); + } + + [Fact] + public void ListenerCallsOnEndSpanCallbackUsingSynchronizationContextIfSpecified() + { + RunSyncContextTest(new SpanBuilder().Build(), + spanCallback => new CallbackVisitor(spanCallback, _ => { }, _ => { }, _ => { }), + (listener, expected) => listener.VisitSpan(expected)); + } + + [Fact] + public void ListenerCallsOnStartBlockCallbackUsingSynchronizationContextIfSpecified() + { + RunSyncContextTest(BlockType.Template, + startBlockCallback => new CallbackVisitor(_ => { }, _ => { }, startBlockCallback, _ => { }), + (listener, expected) => listener.VisitStartBlock(new BlockBuilder() { Type = expected }.Build())); + } + + [Fact] + public void ListenerCallsOnEndBlockCallbackUsingSynchronizationContextIfSpecified() + { + RunSyncContextTest(BlockType.Template, + endBlockCallback => new CallbackVisitor(_ => { }, _ => { }, _ => { }, endBlockCallback), + (listener, expected) => listener.VisitEndBlock(new BlockBuilder() { Type = expected }.Build())); + } + + [Fact] + public void ListenerCallsOnErrorCallbackUsingSynchronizationContextIfSpecified() + { + RunSyncContextTest(new RazorError("Bar", 42, 42, 42), + errorCallback => new CallbackVisitor(_ => { }, errorCallback, _ => { }, _ => { }), + (listener, expected) => listener.VisitError(expected)); + } + + private static void RunSyncContextTest(T expected, Func, CallbackVisitor> ctor, Action call) + { + // Arrange + Mock mockContext = new Mock(); + mockContext.Setup(c => c.Post(It.IsAny(), It.IsAny())) + .Callback((callback, state) => { callback(expected); }); + + // Act/Assert + RunCallbackTest(default(T), callback => + { + CallbackVisitor listener = ctor(callback); + listener.SynchronizationContext = mockContext.Object; + return listener; + }, call, (original, actual) => + { + Assert.NotEqual(original, actual); + Assert.Equal(expected, actual); + }); + } + + private static void RunOnStartBlockTest(Func, CallbackVisitor> ctor, Action verifyResults = null) + { + RunCallbackTest(BlockType.Markup, ctor, (listener, expected) => listener.VisitStartBlock(new BlockBuilder() { Type = expected }.Build()), verifyResults); + } + + private static void RunOnEndBlockTest(Func, CallbackVisitor> ctor, Action verifyResults = null) + { + RunCallbackTest(BlockType.Markup, ctor, (listener, expected) => listener.VisitEndBlock(new BlockBuilder() { Type = expected }.Build()), verifyResults); + } + + private static void RunOnErrorTest(Func, CallbackVisitor> ctor, Action verifyResults = null) + { + RunCallbackTest(new RazorError("Foo", SourceLocation.Zero), ctor, (listener, expected) => listener.VisitError(expected), verifyResults); + } + + private static void RunOnEndSpanTest(Func, CallbackVisitor> ctor, Action verifyResults = null) + { + RunCallbackTest(new SpanBuilder().Build(), ctor, (listener, expected) => listener.VisitSpan(expected), verifyResults); + } + + private static void RunCallbackTest(T expected, Func, CallbackVisitor> ctor, Action call, Action verifyResults = null) + { + // Arrange + object actual = null; + Action callback = t => actual = t; + + CallbackVisitor listener = ctor(callback); + + // Act + call(listener, expected); + + // Assert + if (verifyResults == null) + { + Assert.Equal(expected, actual); + } + else + { + verifyResults(expected, (T)actual); + } + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlAttributeTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlAttributeTest.cs new file mode 100644 index 0000000000..e765f524b6 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlAttributeTest.cs @@ -0,0 +1,303 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Editor; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.Html +{ + public class HtmlAttributeTest : CsHtmlMarkupParserTestBase + { + [Fact] + public void SimpleLiteralAttribute() + { + ParseBlockTest("", + new MarkupBlock( + Factory.Markup("(" href='", 2, 0, 2), suffix: new LocationTagged("'", 12, 0, 12)), + Factory.Markup(" href='").With(SpanCodeGenerator.Null), + Factory.Markup("Foo").With(new LiteralAttributeCodeGenerator(prefix: new LocationTagged(String.Empty, 9, 0, 9), value: new LocationTagged("Foo", 9, 0, 9))), + Factory.Markup("'").With(SpanCodeGenerator.Null)), + Factory.Markup(" />").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void MultiPartLiteralAttribute() + { + ParseBlockTest("", + new MarkupBlock( + Factory.Markup("(" href='", 2, 0, 2), suffix: new LocationTagged("'", 20, 0, 20)), + Factory.Markup(" href='").With(SpanCodeGenerator.Null), + Factory.Markup("Foo").With(new LiteralAttributeCodeGenerator(prefix: new LocationTagged(String.Empty, 9, 0, 9), value: new LocationTagged("Foo", 9, 0, 9))), + Factory.Markup(" Bar").With(new LiteralAttributeCodeGenerator(prefix: new LocationTagged(" ", 12, 0, 12), value: new LocationTagged("Bar", 13, 0, 13))), + Factory.Markup(" Baz").With(new LiteralAttributeCodeGenerator(prefix: new LocationTagged(" ", 16, 0, 16), value: new LocationTagged("Baz", 17, 0, 17))), + Factory.Markup("'").With(SpanCodeGenerator.Null)), + Factory.Markup(" />").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void DoubleQuotedLiteralAttribute() + { + ParseBlockTest("", + new MarkupBlock( + Factory.Markup("(" href=\"", 2, 0, 2), suffix: new LocationTagged("\"", 20, 0, 20)), + Factory.Markup(" href=\"").With(SpanCodeGenerator.Null), + Factory.Markup("Foo").With(new LiteralAttributeCodeGenerator(prefix: new LocationTagged(String.Empty, 9, 0, 9), value: new LocationTagged("Foo", 9, 0, 9))), + Factory.Markup(" Bar").With(new LiteralAttributeCodeGenerator(prefix: new LocationTagged(" ", 12, 0, 12), value: new LocationTagged("Bar", 13, 0, 13))), + Factory.Markup(" Baz").With(new LiteralAttributeCodeGenerator(prefix: new LocationTagged(" ", 16, 0, 16), value: new LocationTagged("Baz", 17, 0, 17))), + Factory.Markup("\"").With(SpanCodeGenerator.Null)), + Factory.Markup(" />").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void UnquotedLiteralAttribute() + { + ParseBlockTest("", + new MarkupBlock( + Factory.Markup("(" href=", 2, 0, 2), suffix: new LocationTagged(String.Empty, 11, 0, 11)), + Factory.Markup(" href=").With(SpanCodeGenerator.Null), + Factory.Markup("Foo").With(new LiteralAttributeCodeGenerator(prefix: new LocationTagged(String.Empty, 8, 0, 8), value: new LocationTagged("Foo", 8, 0, 8)))), + Factory.Markup(" Bar Baz />").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void SimpleExpressionAttribute() + { + ParseBlockTest("", + new MarkupBlock( + Factory.Markup("(" href='", 2, 0, 2), suffix: new LocationTagged("'", 13, 0, 13)), + Factory.Markup(" href='").With(SpanCodeGenerator.Null), + new MarkupBlock(new DynamicAttributeBlockCodeGenerator(new LocationTagged(String.Empty, 9, 0, 9), 9, 0, 9), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("foo") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace))), + Factory.Markup("'").With(SpanCodeGenerator.Null)), + Factory.Markup(" />").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void MultiValueExpressionAttribute() + { + ParseBlockTest("", + new MarkupBlock( + Factory.Markup("(" href='", 2, 0, 2), suffix: new LocationTagged("'", 22, 0, 22)), + Factory.Markup(" href='").With(SpanCodeGenerator.Null), + new MarkupBlock(new DynamicAttributeBlockCodeGenerator(new LocationTagged(String.Empty, 9, 0, 9), 9, 0, 9), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("foo") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace))), + Factory.Markup(" bar").With(new LiteralAttributeCodeGenerator(new LocationTagged(" ", 13, 0, 13), new LocationTagged("bar", 14, 0, 14))), + new MarkupBlock(new DynamicAttributeBlockCodeGenerator(new LocationTagged(" ", 17, 0, 17), 18, 0, 18), + Factory.Markup(" ").With(SpanCodeGenerator.Null), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("baz") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace))), + Factory.Markup("'").With(SpanCodeGenerator.Null)), + Factory.Markup(" />").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void VirtualPathAttributesWorkWithConditionalAttributes() + { + ParseBlockTest("", + new MarkupBlock( + Factory.Markup("(" href='", 2, 0, 2), suffix: new LocationTagged("'", 23, 0, 23)), + Factory.Markup(" href='").With(SpanCodeGenerator.Null), + new MarkupBlock(new DynamicAttributeBlockCodeGenerator(new LocationTagged(String.Empty, 9, 0, 9), 9, 0, 9), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("foo") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace))), + Factory.Markup(" ~/Foo/Bar") + .WithEditorHints(EditorHints.VirtualPath) + .With(new LiteralAttributeCodeGenerator( + new LocationTagged(" ", 13, 0, 13), + new LocationTagged(new ResolveUrlCodeGenerator(), 14, 0, 14))), + Factory.Markup("'").With(SpanCodeGenerator.Null)), + Factory.Markup(" />").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void UnquotedAttributeWithCodeWithSpacesInBlock() + { + ParseBlockTest("", + new MarkupBlock( + Factory.Markup("(" value=", 6, 0, 6), suffix: new LocationTagged(String.Empty, 17, 0, 17)), + Factory.Markup(" value=").With(SpanCodeGenerator.Null), + new MarkupBlock(new DynamicAttributeBlockCodeGenerator(new LocationTagged(String.Empty, 13, 0, 13), 13, 0, 13), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("foo") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)))), + Factory.Markup(" />").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void UnquotedAttributeWithCodeWithSpacesInDocument() + { + ParseDocumentTest("", + new MarkupBlock( + Factory.Markup("(" value=", 6, 0, 6), suffix: new LocationTagged(String.Empty, 17, 0, 17)), + Factory.Markup(" value=").With(SpanCodeGenerator.Null), + new MarkupBlock(new DynamicAttributeBlockCodeGenerator(new LocationTagged(String.Empty, 13, 0, 13), 13, 0, 13), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("foo") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)))), + Factory.Markup(" />"))); + } + + [Fact] + public void ConditionalAttributeCollapserDoesNotRemoveUrlAttributeValues() + { + // Act + ParserResults results = ParseDocument(""); + Block rewritten = new ConditionalAttributeCollapser(new HtmlMarkupParser().BuildSpan).Rewrite(results.Document); + rewritten = new MarkupCollapser(new HtmlMarkupParser().BuildSpan).Rewrite(rewritten); + + // Assert + Assert.Equal(0, results.ParserErrors.Count); + EvaluateParseTree(rewritten, + new MarkupBlock( + Factory.Markup("(" href='", 2, 0, 2), suffix: new LocationTagged("'", 18, 0, 18)), + Factory.Markup(" href='").With(SpanCodeGenerator.Null), + Factory.Markup("~/Foo/Bar") + .WithEditorHints(EditorHints.VirtualPath) + .With(new LiteralAttributeCodeGenerator( + new LocationTagged(String.Empty, 9, 0, 9), + new LocationTagged(new ResolveUrlCodeGenerator(), 9, 0, 9))), + Factory.Markup("'").With(SpanCodeGenerator.Null)), + Factory.Markup(" />"))); + } + + [Fact] + public void ConditionalAttributesDoNotCreateExtraDataForEntirelyLiteralAttribute() + { + // Arrange + const string code = + @"
+

Title

+

+ As the author, you can edit + or remove this photo. +

+
+
Description
+
+ The uploader did not provide a description for this photo. +
+
Uploaded by
+
user.DisplayName
+
Upload date
+
photo.UploadDate
+
Gallery
+
gallery.Name
+
Tags
+
+
    +
  • This photo has no tags.
  • +
+ edit tags +
+
+ +

+ Download full photo ((photo.FileSize / 1024) KB) +

+
+
+ +

Nobody has commented on this photo

+
    +
  1. +

    + comment.DisplayName commented at comment.CommentDate: +

    +

    comment.CommentText

    +
  2. +
+ +
+
+ Post new comment +
    +
  1. + + +
  2. +
+

+ +

+
+
+
"; + + // Act + ParserResults results = ParseDocument(code); + Block rewritten = new ConditionalAttributeCollapser(new HtmlMarkupParser().BuildSpan).Rewrite(results.Document); + rewritten = new MarkupCollapser(new HtmlMarkupParser().BuildSpan).Rewrite(rewritten); + + // Assert + Assert.Equal(0, results.ParserErrors.Count); + EvaluateParseTree(rewritten, new MarkupBlock(Factory.Markup(code))); + } + + [Fact] + public void ConditionalAttributesAreDisabledForDataAttributesInBlock() + { + ParseBlockTest("", + new MarkupBlock( + Factory.Markup("").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ConditionalAttributesAreDisabledForDataAttributesInDocument() + { + ParseDocumentTest("", + new MarkupBlock( + Factory.Markup(""))); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlBlockTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlBlockTest.cs new file mode 100644 index 0000000000..6ec65ea8fb --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlBlockTest.cs @@ -0,0 +1,393 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Editor; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.Html +{ + public class HtmlBlockTest : CsHtmlMarkupParserTestBase + { + [Fact] + public void ParseBlockMethodThrowsArgNullExceptionOnNullContext() + { + // Arrange + HtmlMarkupParser parser = new HtmlMarkupParser(); + + // Act and Assert + Assert.Throws(() => parser.ParseBlock(), RazorResources.Parser_Context_Not_Set); + } + + [Fact] + public void ParseBlockHandlesOpenAngleAtEof() + { + ParseDocumentTest("@{" + Environment.NewLine + + "<", + new MarkupBlock( + Factory.EmptyHtml(), + new StatementBlock( + Factory.CodeTransition(), + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code("\r\n").AsStatement(), + new MarkupBlock( + Factory.Markup("<")))), + new RazorError( + String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, RazorResources.BlockName_Code, "}", "{"), + 1, 0, 1)); + } + + [Fact] + public void ParseBlockHandlesOpenAngleWithProperTagFollowingIt() + { + ParseDocumentTest("@{" + Environment.NewLine + + "<" + Environment.NewLine + + "", + new MarkupBlock( + Factory.EmptyHtml(), + new StatementBlock( + Factory.CodeTransition(), + Factory.MetaCode("{").Accepts(AcceptedCharacters.None), + Factory.Code("\r\n").AsStatement(), + new MarkupBlock( + Factory.Markup("<\r\n") + ), + new MarkupBlock( + Factory.Markup(@"").Accepts(AcceptedCharacters.None) + ), + Factory.EmptyCSharp().AsStatement() + ) + ), + designTimeParser: true, + expectedErrors: new[] + { + new RazorError(String.Format(RazorResources.ParseError_UnexpectedEndTag, "html"), 7, 2, 0), + new RazorError(String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, "code", "}", "{"), 1, 0, 1) + }); + } + + [Fact] + public void TagWithoutCloseAngleDoesNotTerminateBlock() + { + ParseBlockTest("< " + Environment.NewLine + + " ", + new MarkupBlock( + Factory.Markup("< \r\n ")), + designTimeParser: true, + expectedErrors: new RazorError(String.Format(RazorResources.ParseError_UnfinishedTag, String.Empty), 0, 0, 0)); + } + + [Fact] + public void ParseBlockAllowsStartAndEndTagsToDifferInCase() + { + SingleSpanBlockTest("
  • Foo

  • ", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockReadsToEndOfLineIfFirstCharacterAfterTransitionIsColon() + { + ParseBlockTest("@:
  • Foo Bar Baz" + Environment.NewLine + + "bork", + new MarkupBlock( + Factory.MarkupTransition(), + Factory.MetaMarkup(":", HtmlSymbolType.Colon), + Factory.Markup("
  • Foo Bar Baz\r\n") + .With(new SingleLineMarkupEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString, AcceptedCharacters.None)) + )); + } + + [Fact] + public void ParseBlockStopsParsingSingleLineBlockAtEOFIfNoEOLReached() + { + ParseBlockTest("@:foo bar", + new MarkupBlock( + Factory.MarkupTransition(), + Factory.MetaMarkup(":", HtmlSymbolType.Colon), + Factory.Markup(@"foo bar") + .With(new SingleLineMarkupEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString)) + )); + } + + [Fact] + public void ParseBlockStopsAtMatchingCloseTagToStartTag() + { + SingleSpanBlockTest("", "", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockParsesUntilMatchingEndTagIfFirstNonWhitespaceCharacterIsStartTag() + { + SingleSpanBlockTest("", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockAllowsUnclosedTagsAsLongAsItCanRecoverToAnExpectedEndTag() + { + SingleSpanBlockTest("", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockWithSelfClosingTagJustEmitsTag() + { + SingleSpanBlockTest("", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockCanHandleSelfClosingTagsWithinBlock() + { + SingleSpanBlockTest("", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockSupportsTagsWithAttributes() + { + ParseBlockTest("", + new MarkupBlock( + Factory.Markup("(" bar=\"", 4, 0, 4), new LocationTagged("\"", 13, 0, 13)), + Factory.Markup(" bar=\"").With(SpanCodeGenerator.Null), + Factory.Markup("baz").With(new LiteralAttributeCodeGenerator(new LocationTagged(String.Empty, 10, 0, 10), new LocationTagged("baz", 10, 0, 10))), + Factory.Markup("\"").With(SpanCodeGenerator.Null)), + Factory.Markup(">(" zoop=", 24, 0, 24), new LocationTagged(String.Empty, 34, 0, 34)), + Factory.Markup(" zoop=").With(SpanCodeGenerator.Null), + Factory.Markup("zork").With(new LiteralAttributeCodeGenerator(new LocationTagged(String.Empty, 30, 0, 30), new LocationTagged("zork", 30, 0, 30)))), + Factory.Markup("/>").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockAllowsCloseAngleBracketInAttributeValueIfDoubleQuoted() + { + ParseBlockTest("\" />", + new MarkupBlock( + Factory.Markup("(" baz=\"", 9, 0, 9), new LocationTagged("\"", 16, 0, 16)), + Factory.Markup(" baz=\"").With(SpanCodeGenerator.Null), + Factory.Markup(">").With(new LiteralAttributeCodeGenerator(new LocationTagged(String.Empty, 15, 0, 15), new LocationTagged(">", 15, 0, 15))), + Factory.Markup("\"").With(SpanCodeGenerator.Null)), + Factory.Markup(" />").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockAllowsCloseAngleBracketInAttributeValueIfSingleQuoted() + { + ParseBlockTest("\' />", + new MarkupBlock( + Factory.Markup("(" baz='", 9, 0, 9), new LocationTagged("'", 16, 0, 16)), + Factory.Markup(" baz='").With(SpanCodeGenerator.Null), + Factory.Markup(">").With(new LiteralAttributeCodeGenerator(new LocationTagged(String.Empty, 15, 0, 15), new LocationTagged(">", 15, 0, 15))), + Factory.Markup("'").With(SpanCodeGenerator.Null)), + Factory.Markup(" />").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockAllowsSlashInAttributeValueIfDoubleQuoted() + { + ParseBlockTest("", + new MarkupBlock( + Factory.Markup("(" baz=\"", 9, 0, 9), new LocationTagged("\"", 16, 0, 16)), + Factory.Markup(" baz=\"").With(SpanCodeGenerator.Null), + Factory.Markup("/").With(new LiteralAttributeCodeGenerator(new LocationTagged(String.Empty, 15, 0, 15), new LocationTagged("/", 15, 0, 15))), + Factory.Markup("\"").With(SpanCodeGenerator.Null)), + Factory.Markup(">").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockAllowsSlashInAttributeValueIfSingleQuoted() + { + ParseBlockTest("", + new MarkupBlock( + Factory.Markup("(" baz='", 9, 0, 9), new LocationTagged("'", 16, 0, 16)), + Factory.Markup(" baz='").With(SpanCodeGenerator.Null), + Factory.Markup("/").With(new LiteralAttributeCodeGenerator(new LocationTagged(String.Empty, 15, 0, 15), new LocationTagged("/", 15, 0, 15))), + Factory.Markup("'").With(SpanCodeGenerator.Null)), + Factory.Markup(">").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockTerminatesAtEOF() + { + SingleSpanBlockTest("", "", BlockType.Markup, SpanKind.Markup, + new RazorError(String.Format(RazorResources.ParseError_MissingEndTag, "foo"), new SourceLocation(0, 0, 0))); + } + + [Fact] + public void ParseBlockSupportsCommentAsBlock() + { + SingleSpanBlockTest("", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockSupportsCommentWithinBlock() + { + SingleSpanBlockTest("barbaz", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockProperlyBalancesCommentStartAndEndTags() + { + SingleSpanBlockTest("", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockTerminatesAtEOFWhenParsingComment() + { + SingleSpanBlockTest("", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockTerminatesCommentAtFirstOccurrenceOfEndSequence() + { + SingleSpanBlockTest("-->", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockTreatsMalformedTagsAsContent() + { + SingleSpanBlockTest( + "", + "", + BlockType.Markup, + SpanKind.Markup, + AcceptedCharacters.None, + new RazorError(String.Format(RazorResources.ParseError_MissingEndTag, "foo"), 0, 0, 0)); + } + + + [Fact] + public void ParseBlockParsesSGMLDeclarationAsEmptyTag() + { + SingleSpanBlockTest("", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockTerminatesSGMLDeclarationAtFirstCloseAngle() + { + SingleSpanBlockTest(" baz>", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockParsesXMLProcessingInstructionAsEmptyTag() + { + SingleSpanBlockTest("", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockTerminatesXMLProcessingInstructionAtQuestionMarkCloseAnglePair() + { + SingleSpanBlockTest(" baz", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockDoesNotTerminateXMLProcessingInstructionAtCloseAngleUnlessPreceededByQuestionMark() + { + SingleSpanBlockTest(" baz?>", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockSupportsScriptTagsWithLessThanSignsInThem() + { + SingleSpanBlockTest(@"", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockSupportsScriptTagsWithSpacedLessThanSignsInThem() + { + SingleSpanBlockTest(@"", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockAcceptsEmptyTextTag() + { + ParseBlockTest("", + new MarkupBlock( + Factory.MarkupTransition("") + )); + } + + [Fact] + public void ParseBlockAcceptsTextTagAsOuterTagButDoesNotRender() + { + ParseBlockTest("Foo Bar Baz zoop", + new MarkupBlock( + Factory.MarkupTransition(""), + Factory.Markup("Foo Bar Baz"), + Factory.MarkupTransition(""), + Factory.Markup(" ").Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockRendersLiteralTextTagIfDoubled() + { + ParseBlockTest("Foo Bar Baz zoop", + new MarkupBlock( + Factory.MarkupTransition(""), + Factory.Markup("Foo Bar Baz"), + Factory.MarkupTransition(""), + Factory.Markup(" ").Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockDoesNotConsiderPsuedoTagWithinMarkupBlock() + { + ParseBlockTest("", + new MarkupBlock( + Factory.Markup("").Accepts(AcceptedCharacters.None) + )); + } + + [Fact] + public void ParseBlockStopsParsingMidEmptyTagIfEOFReached() + { + ParseBlockTest("
    Foo @if(true) {} Bar", + new MarkupBlock( + Factory.Markup("
    Foo "), + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("if(true) {}").AsStatement()), + Factory.Markup(" Bar
    ").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockIgnoresTagsInContentsOfScriptTag() + { + ParseBlockTest(@"", + new MarkupBlock( + Factory.Markup("") + .Accepts(AcceptedCharacters.None))); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlDocumentTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlDocumentTest.cs new file mode 100644 index 0000000000..fdb83e9ecd --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlDocumentTest.cs @@ -0,0 +1,239 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using System.Web.WebPages.TestUtils; +using Microsoft.TestCommon; +using System; + +namespace Microsoft.AspNet.Razor.Test.Parser.Html +{ + public class HtmlDocumentTest : CsHtmlMarkupParserTestBase + { + private static readonly TestFile Nested1000 = TestFile.Create("nested-1000.html"); + + [Fact] + public void ParseDocumentMethodThrowsArgNullExceptionOnNullContext() + { + // Arrange + HtmlMarkupParser parser = new HtmlMarkupParser(); + + // Act and Assert + Assert.Throws(() => parser.ParseDocument(), RazorResources.Parser_Context_Not_Set); + } + + [Fact] + public void ParseSectionMethodThrowsArgNullExceptionOnNullContext() + { + // Arrange + HtmlMarkupParser parser = new HtmlMarkupParser(); + + // Act and Assert + Assert.Throws(() => parser.ParseSection(null, true), RazorResources.Parser_Context_Not_Set); + } + + [Fact] + public void ParseDocumentOutputsEmptyBlockWithEmptyMarkupSpanIfContentIsEmptyString() + { + ParseDocumentTest(String.Empty, new MarkupBlock(Factory.EmptyHtml())); + } + + [Fact] + public void ParseDocumentOutputsWhitespaceOnlyContentAsSingleWhitespaceMarkupSpan() + { + SingleSpanDocumentTest(" ", BlockType.Markup, SpanKind.Markup); + } + + [Fact] + public void ParseDocumentAcceptsSwapTokenAtEndOfFileAndOutputsZeroLengthCodeSpan() + { + ParseDocumentTest("@", + new MarkupBlock( + Factory.EmptyHtml(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.EmptyCSharp() + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.EmptyHtml()), + new RazorError(RazorResources.ParseError_Unexpected_EndOfFile_At_Start_Of_CodeBlock, 1, 0, 1)); + } + + [Fact] + public void ParseDocumentCorrectlyHandlesSingleLineOfMarkupWithEmbeddedStatement() + { + ParseDocumentTest("
    Foo @if(true) {} Bar
    ", + new MarkupBlock( + Factory.Markup("
    Foo "), + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("if(true) {}").AsStatement()), + Factory.Markup(" Bar
    "))); + } + + [Fact] + public void ParseDocumentWithinSectionDoesNotCreateDocumentLevelSpan() + { + ParseDocumentTest("@section Foo {" + Environment.NewLine + + " " + Environment.NewLine + + "}", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("Foo"), + Factory.CodeTransition(), + Factory.MetaCode("section Foo {") + .AutoCompleteWith(null, atEndOfSpan: true), + new MarkupBlock( + Factory.Markup("\r\n \r\n")), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void ParseDocumentParsesWholeContentAsOneSpanIfNoSwapCharacterEncountered() + { + SingleSpanDocumentTest("foo baz", BlockType.Markup, SpanKind.Markup); + } + + [Fact] + public void ParseDocumentHandsParsingOverToCodeParserWhenAtSignEncounteredAndEmitsOutput() + { + ParseDocumentTest("foo @bar baz", + new MarkupBlock( + Factory.Markup("foo "), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("bar") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Markup(" baz"))); + } + + [Fact] + public void ParseDocumentEmitsAtSignAsMarkupIfAtEndOfFile() + { + ParseDocumentTest("foo @", + new MarkupBlock( + Factory.Markup("foo "), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.EmptyCSharp() + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.EmptyHtml()), + new RazorError(RazorResources.ParseError_Unexpected_EndOfFile_At_Start_Of_CodeBlock, 5, 0, 5)); + } + + [Fact] + public void ParseDocumentEmitsCodeBlockIfFirstCharacterIsSwapCharacter() + { + ParseDocumentTest("@bar", + new MarkupBlock( + Factory.EmptyHtml(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("bar") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.EmptyHtml())); + } + + [Fact] + public void ParseDocumentDoesNotSwitchToCodeOnEmailAddressInText() + { + SingleSpanDocumentTest("anurse@microsoft.com", BlockType.Markup, SpanKind.Markup); + } + + [Fact] + public void ParseDocumentDoesNotSwitchToCodeOnEmailAddressInAttribute() + { + ParseDocumentTest("Email me", + new MarkupBlock( + Factory.Markup("(" href=\"", 2, 0, 2), new LocationTagged("\"", 36, 0, 36)), + Factory.Markup(" href=\"").With(SpanCodeGenerator.Null), + Factory.Markup("mailto:anurse@microsoft.com") + .With(new LiteralAttributeCodeGenerator(new LocationTagged(String.Empty, 9, 0, 9), new LocationTagged("mailto:anurse@microsoft.com", 9, 0, 9))), + Factory.Markup("\"").With(SpanCodeGenerator.Null)), + Factory.Markup(">Email me"))); + } + + [Fact] + public void ParseDocumentDoesNotReturnErrorOnMismatchedTags() + { + SingleSpanDocumentTest("Foo

    Baz", BlockType.Markup, SpanKind.Markup); + } + + [Fact] + public void ParseDocumentReturnsOneMarkupSegmentIfNoCodeBlocksEncountered() + { + SingleSpanDocumentTest("Foo

    BazBar Qux", BlockType.Markup, SpanKind.Markup); + } + + [Fact] + public void ParseDocumentRendersTextPseudoTagAsMarkup() + { + SingleSpanDocumentTest("Foo Foo", BlockType.Markup, SpanKind.Markup); + } + + [Fact] + public void ParseDocumentAcceptsEndTagWithNoMatchingStartTag() + { + SingleSpanDocumentTest("Foo

    Bar", BlockType.Markup, SpanKind.Markup); + } + + [Fact] + public void ParseDocumentNoLongerSupportsDollarOpenBraceCombination() + { + ParseDocumentTest("${bar}", + new MarkupBlock( + Factory.Markup("${bar}"))); + } + + [Fact] + public void ParseDocumentIgnoresTagsInContentsOfScriptTag() + { + ParseDocumentTest(@"", + new MarkupBlock( + Factory.Markup(""))); + } + + [Fact] + public void ParseSectionIgnoresTagsInContentsOfScriptTag() + { + ParseDocumentTest(@"@section Foo { }", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("Foo"), + Factory.CodeTransition(), + Factory.MetaCode("section Foo {"), + new MarkupBlock( + Factory.Markup(" ")), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void ParseBlockCanParse1000NestedElements() + { + string content = Nested1000.ReadAllText(); + SingleSpanDocumentTest(content, BlockType.Markup, SpanKind.Markup); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlErrorTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlErrorTest.cs new file mode 100644 index 0000000000..2fe1764508 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlErrorTest.cs @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.Html +{ + public class HtmlErrorTest : CsHtmlMarkupParserTestBase + { + [Fact] + public void ParseBlockAllowsInvalidTagNamesAsLongAsParserCanIdentifyEndTag() + { + SingleSpanBlockTest("<1-foo+bar>foo", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockThrowsErrorIfStartTextTagContainsTextAfterName() + { + ParseBlockTest("", + new MarkupBlock( + Factory.MarkupTransition(""), + Factory.MarkupTransition("
    ")), + new RazorError(RazorResources.ParseError_TextTagCannotContainAttributes, SourceLocation.Zero)); + } + + [Fact] + public void ParseBlockThrowsErrorIfEndTextTagContainsTextAfterName() + { + ParseBlockTest("", + new MarkupBlock( + Factory.MarkupTransition(""), + Factory.MarkupTransition("", + new MarkupBlock(), + new RazorError(RazorResources.ParseError_MarkupBlock_Must_Start_With_Tag, SourceLocation.Zero)); + } + + [Fact] + public void ParseBlockStartingWithEndTagProducesRazorErrorThenOutputsMarkupSegmentAndEndsBlock() + { + ParseBlockTest("
    bar baz", + new MarkupBlock( + Factory.Markup("
    ").Accepts(AcceptedCharacters.None)), + new RazorError(String.Format(RazorResources.ParseError_UnexpectedEndTag, "foo"), SourceLocation.Zero)); + } + + [Fact] + public void ParseBlockWithUnclosedTopLevelTagThrowsMissingEndTagParserExceptionOnOutermostUnclosedTag() + { + ParseBlockTest("

    ", + new MarkupBlock( + Factory.Markup("

    ").Accepts(AcceptedCharacters.None)), + new RazorError(String.Format(RazorResources.ParseError_MissingEndTag, "p"), new SourceLocation(0, 0, 0))); + } + + [Fact] + public void ParseBlockWithUnclosedTagAtEOFThrowsMissingEndTagException() + { + ParseBlockTest("blah blah blah blah blah", + new MarkupBlock( + Factory.Markup("blah blah blah blah blah")), + new RazorError(String.Format(RazorResources.ParseError_MissingEndTag, "foo"), new SourceLocation(0, 0, 0))); + } + + [Fact] + public void ParseBlockWithUnfinishedTagAtEOFThrowsIncompleteTagException() + { + ParseBlockTest("(" bar=", 4, 0, 4), new LocationTagged(String.Empty, 12, 0, 12)), + Factory.Markup(" bar=").With(SpanCodeGenerator.Null), + Factory.Markup("baz").With(new LiteralAttributeCodeGenerator(new LocationTagged(String.Empty, 9, 0, 9), new LocationTagged("baz", 9, 0, 9))))), + new RazorError(String.Format(RazorResources.ParseError_UnfinishedTag, "foo"), new SourceLocation(0, 0, 0))); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlParserTestUtils.cs b/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlParserTestUtils.cs new file mode 100644 index 0000000000..00f1f5475d --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlParserTestUtils.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Test.Framework; + +namespace Microsoft.AspNet.Razor.Test.Parser.Html +{ + internal class HtmlParserTestUtils + { + public static void RunSingleAtEscapeTest(Action testMethod, AcceptedCharacters lastSpanAcceptedCharacters = AcceptedCharacters.None) + { + var factory = SpanFactory.CreateCsHtml(); + testMethod("@@bar", + new MarkupBlock( + factory.Markup(""), + factory.Markup("@").Hidden(), + factory.Markup("@bar").Accepts(lastSpanAcceptedCharacters))); + } + + public static void RunMultiAtEscapeTest(Action testMethod, AcceptedCharacters lastSpanAcceptedCharacters = AcceptedCharacters.None) + { + var factory = SpanFactory.CreateCsHtml(); + testMethod("@@@@@bar", + new MarkupBlock( + factory.Markup(""), + factory.Markup("@").Hidden(), + factory.Markup("@"), + factory.Markup("@").Hidden(), + factory.Markup("@"), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("bar") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Markup("").Accepts(lastSpanAcceptedCharacters))); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlTagsTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlTagsTest.cs new file mode 100644 index 0000000000..7e64116b8b --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlTagsTest.cs @@ -0,0 +1,152 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.Html +{ + public class HtmlTagsTest : CsHtmlMarkupParserTestBase + { + public static IEnumerable VoidElementNames + { + get + { + yield return new[] { "area" }; + yield return new[] { "base" }; + yield return new[] { "br" }; + yield return new[] { "col" }; + yield return new[] { "command" }; + yield return new[] { "embed" }; + yield return new[] { "hr" }; + yield return new[] { "img" }; + yield return new[] { "input" }; + yield return new[] { "keygen" }; + yield return new[] { "link" }; + yield return new[] { "meta" }; + yield return new[] { "param" }; + yield return new[] { "source" }; + yield return new[] { "track" }; + yield return new[] { "wbr" }; + } + } + + [Fact] + public void EmptyTagNestsLikeNormalTag() + { + ParseBlockTest("

    Bar", + new MarkupBlock( + Factory.Markup("

    ").Accepts(AcceptedCharacters.None)), + new RazorError(String.Format(RazorResources.ParseError_MissingEndTag, "p"), 0, 0, 0)); + } + + [Fact] + public void EmptyTag() + { + ParseBlockTest("<> Bar", + new MarkupBlock( + Factory.Markup("<> ").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void CommentTag() + { + ParseBlockTest(" Bar", + new MarkupBlock( + Factory.Markup(" ").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void DocTypeTag() + { + ParseBlockTest(" foo", + new MarkupBlock( + Factory.Markup(" ").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ProcessingInstructionTag() + { + ParseBlockTest(" foo", + new MarkupBlock( + Factory.Markup(" ").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ElementTags() + { + ParseBlockTest("

    Foo

    Bar", + new MarkupBlock( + Factory.Markup("

    Foo

    ").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void TextTags() + { + ParseBlockTest("Foo}", + new MarkupBlock( + Factory.MarkupTransition(""), + Factory.Markup("Foo"), + Factory.MarkupTransition(""))); + } + + [Fact] + public void CDataTag() + { + ParseBlockTest(" Bar", + new MarkupBlock( + Factory.Markup(" ").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ScriptTag() + { + ParseDocumentTest("", + new MarkupBlock( + Factory.Markup(""))); + } + + [Theory] + [PropertyData("VoidElementNames")] + public void VoidElementFollowedByContent(string tagName) + { + ParseBlockTest("<" + tagName + ">foo", + new MarkupBlock( + Factory.Markup("<" + tagName + ">") + .Accepts(AcceptedCharacters.None))); + } + + [Theory] + [PropertyData("VoidElementNames")] + public void VoidElementFollowedByOtherTag(string tagName) + { + ParseBlockTest("<" + tagName + ">foo", + new MarkupBlock( + Factory.Markup("<" + tagName + ">") + .Accepts(AcceptedCharacters.None))); + } + + [Theory] + [PropertyData("VoidElementNames")] + public void VoidElementFollowedByCloseTag(string tagName) + { + ParseBlockTest("<" + tagName + "> foo", + new MarkupBlock( + Factory.Markup("<" + tagName + "> ") + .Accepts(AcceptedCharacters.None))); + } + + [Theory] + [PropertyData("VoidElementNames")] + public void IncompleteVoidElementEndTag(string tagName) + { + ParseBlockTest("<" + tagName + ">foo#@i

    ", + new MarkupBlock( + Factory.Markup("

    foo#"), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("i").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Markup("

    ").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockSwitchesToCodeWhenSwapCharacterEncounteredMidTag() + { + ParseBlockTest("", + new MarkupBlock( + Factory.Markup("").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockSwitchesToCodeWhenSwapCharacterEncounteredInAttributeValue() + { + ParseBlockTest("", + new MarkupBlock( + Factory.Markup("(" bar=\"", 4, 0, 4), new LocationTagged("\"", 14, 0, 14)), + Factory.Markup(" bar=\"").With(SpanCodeGenerator.Null), + new MarkupBlock(new DynamicAttributeBlockCodeGenerator(new LocationTagged(String.Empty, 10, 0, 10), 10, 0, 10), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("baz") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace))), + Factory.Markup("\"").With(SpanCodeGenerator.Null)), + Factory.Markup(" />").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockSwitchesToCodeWhenSwapCharacterEncounteredInTagContent() + { + ParseBlockTest("@bar@boz", + new MarkupBlock( + Factory.Markup(""), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("bar") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Markup(""), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("boz") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Markup("").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockParsesCodeWithinSingleLineMarkup() + { + ParseBlockTest("@:
  • Foo @Bar Baz" + Environment.NewLine + + "bork", + new MarkupBlock( + Factory.MarkupTransition(), + Factory.MetaMarkup(":", HtmlSymbolType.Colon), + Factory.Markup("
  • Foo ").With(new SingleLineMarkupEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString)), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("Bar") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Markup(" Baz\r\n") + .With(new SingleLineMarkupEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString, AcceptedCharacters.None)))); + } + + [Fact] + public void ParseBlockSupportsCodeWithinComment() + { + ParseBlockTest("", + new MarkupBlock( + Factory.Markup("").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockSupportsCodeWithinSGMLDeclaration() + { + ParseBlockTest("", + new MarkupBlock( + Factory.Markup("").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockSupportsCodeWithinCDataDeclaration() + { + ParseBlockTest("", + new MarkupBlock( + Factory.Markup("").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockSupportsCodeWithinXMLProcessingInstruction() + { + ParseBlockTest("", + new MarkupBlock( + Factory.Markup("").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockDoesNotSwitchToCodeOnEmailAddressInText() + { + SingleSpanBlockTest("anurse@microsoft.com", BlockType.Markup, SpanKind.Markup, acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockDoesNotSwitchToCodeOnEmailAddressInAttribute() + { + ParseBlockTest("Email me", + new MarkupBlock( + Factory.Markup("(" href=\"", 2, 0, 2), new LocationTagged("\"", 36, 0, 36)), + Factory.Markup(" href=\"").With(SpanCodeGenerator.Null), + Factory.Markup("mailto:anurse@microsoft.com") + .With(new LiteralAttributeCodeGenerator(new LocationTagged(String.Empty, 9, 0, 9), new LocationTagged("mailto:anurse@microsoft.com", 9, 0, 9))), + Factory.Markup("\"").With(SpanCodeGenerator.Null)), + Factory.Markup(">Email me").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockGivesWhitespacePreceedingAtToCodeIfThereIsNoMarkupOnThatLine() + { + ParseBlockTest("
      " + Environment.NewLine + + " @foreach(var p in Products) {" + Environment.NewLine + + "
    • Product: @p.Name
    • " + Environment.NewLine + + " }" + Environment.NewLine + + "
    ", + new MarkupBlock( + Factory.Markup("
      \r\n"), + new StatementBlock( + Factory.Code(" ").AsStatement(), + Factory.CodeTransition(), + Factory.Code("foreach(var p in Products) {\r\n").AsStatement(), + new MarkupBlock( + Factory.Markup("
    • Product: "), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("p.Name") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Markup("
    • \r\n").Accepts(AcceptedCharacters.None)), + Factory.Code(" }\r\n").AsStatement().Accepts(AcceptedCharacters.None)), + Factory.Markup("
    ").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseDocumentGivesWhitespacePreceedingAtToCodeIfThereIsNoMarkupOnThatLine() + { + ParseDocumentTest("
      " + Environment.NewLine + + " @foreach(var p in Products) {" + Environment.NewLine + + "
    • Product: @p.Name
    • " + Environment.NewLine + + " }" + Environment.NewLine + + "
    ", + new MarkupBlock( + Factory.Markup("
      \r\n"), + new StatementBlock( + Factory.Code(" ").AsStatement(), + Factory.CodeTransition(), + Factory.Code("foreach(var p in Products) {\r\n").AsStatement(), + new MarkupBlock( + Factory.Markup("
    • Product: "), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("p.Name") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Markup("
    • \r\n").Accepts(AcceptedCharacters.None)), + Factory.Code(" }\r\n").AsStatement().Accepts(AcceptedCharacters.None)), + Factory.Markup("
    "))); + } + + [Fact] + public void SectionContextGivesWhitespacePreceedingAtToCodeIfThereIsNoMarkupOnThatLine() + { + ParseDocumentTest("@section foo {" + Environment.NewLine + + "
      " + Environment.NewLine + + " @foreach(var p in Products) {" + Environment.NewLine + + "
    • Product: @p.Name
    • " + Environment.NewLine + + " }" + Environment.NewLine + + "
    " + Environment.NewLine + + "}", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("foo"), + Factory.CodeTransition(), + Factory.MetaCode("section foo {").AutoCompleteWith(null, atEndOfSpan: true), + new MarkupBlock( + Factory.Markup("\r\n
      \r\n"), + new StatementBlock( + Factory.Code(" ").AsStatement(), + Factory.CodeTransition(), + Factory.Code("foreach(var p in Products) {\r\n").AsStatement(), + new MarkupBlock( + Factory.Markup("
    • Product: "), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("p.Name") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Markup("
    • \r\n").Accepts(AcceptedCharacters.None)), + Factory.Code(" }\r\n").AsStatement().Accepts(AcceptedCharacters.None)), + Factory.Markup("
    \r\n")), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void CSharpCodeParserDoesNotAcceptLeadingOrTrailingWhitespaceInDesignMode() + { + ParseBlockTest("
      " + Environment.NewLine + + " @foreach(var p in Products) {" + Environment.NewLine + + "
    • Product: @p.Name
    • " + Environment.NewLine + + " }" + Environment.NewLine + + "
    ", + new MarkupBlock( + Factory.Markup("
      \r\n "), + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("foreach(var p in Products) {\r\n ").AsStatement(), + new MarkupBlock( + Factory.Markup("
    • Product: "), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("p.Name").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Markup("
    • ").Accepts(AcceptedCharacters.None)), + Factory.Code("\r\n }").AsStatement().Accepts(AcceptedCharacters.None)), + Factory.Markup("\r\n
    ").Accepts(AcceptedCharacters.None)), + designTimeParser: true); + } + + // Tests for "@@" escape sequence: + [Fact] + public void ParseBlockTreatsTwoAtSignsAsEscapeSequence() + { + HtmlParserTestUtils.RunSingleAtEscapeTest(ParseBlockTest); + } + + [Fact] + public void ParseBlockTreatsPairsOfAtSignsAsEscapeSequence() + { + HtmlParserTestUtils.RunMultiAtEscapeTest(ParseBlockTest); + } + + [Fact] + public void ParseDocumentTreatsTwoAtSignsAsEscapeSequence() + { + HtmlParserTestUtils.RunSingleAtEscapeTest(ParseDocumentTest, lastSpanAcceptedCharacters: AcceptedCharacters.Any); + } + + [Fact] + public void ParseDocumentTreatsPairsOfAtSignsAsEscapeSequence() + { + HtmlParserTestUtils.RunMultiAtEscapeTest(ParseDocumentTest, lastSpanAcceptedCharacters: AcceptedCharacters.Any); + } + + [Fact] + public void SectionBodyTreatsTwoAtSignsAsEscapeSequence() + { + ParseDocumentTest("@section Foo { @@bar }", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("Foo"), + Factory.CodeTransition(), + Factory.MetaCode("section Foo {").AutoCompleteWith(null, atEndOfSpan: true), + new MarkupBlock( + Factory.Markup(" "), + Factory.Markup("@").Hidden(), + Factory.Markup("@bar ")), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void SectionBodyTreatsPairsOfAtSignsAsEscapeSequence() + { + ParseDocumentTest("@section Foo { @@@@@bar }", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("Foo"), + Factory.CodeTransition(), + Factory.MetaCode("section Foo {").AutoCompleteWith(null, atEndOfSpan: true), + new MarkupBlock( + Factory.Markup(" "), + Factory.Markup("@").Hidden(), + Factory.Markup("@"), + Factory.Markup("@").Hidden(), + Factory.Markup("@"), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("bar") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Markup(" ")), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlUrlAttributeTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlUrlAttributeTest.cs new file mode 100644 index 0000000000..4b033769b2 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlUrlAttributeTest.cs @@ -0,0 +1,273 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Editor; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.Html +{ + public class HtmlUrlAttributeTest : CsHtmlMarkupParserTestBase + { + [Fact] + public void SimpleUrlInAttributeInMarkupBlock() + { + ParseBlockTest("", + new MarkupBlock( + Factory.Markup("(" href='", 2, 0, 2), new LocationTagged("'", 22, 0, 22)), + Factory.Markup(" href='").With(SpanCodeGenerator.Null), + Factory.Markup("~/Foo/Bar/Baz") + .WithEditorHints(EditorHints.VirtualPath) + .With(new LiteralAttributeCodeGenerator( + new LocationTagged(String.Empty, 9, 0, 9), + new LocationTagged(new ResolveUrlCodeGenerator(), 9, 0, 9))), + Factory.Markup("'").With(SpanCodeGenerator.Null)), + Factory.Markup(" />").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void SimpleUrlInAttributeInMarkupDocument() + { + ParseDocumentTest("", + new MarkupBlock( + Factory.Markup("(" href='", 2, 0, 2), new LocationTagged("'", 22, 0, 22)), + Factory.Markup(" href='").With(SpanCodeGenerator.Null), + Factory.Markup("~/Foo/Bar/Baz") + .WithEditorHints(EditorHints.VirtualPath) + .With(new LiteralAttributeCodeGenerator( + new LocationTagged(String.Empty, 9, 0, 9), + new LocationTagged(new ResolveUrlCodeGenerator(), 9, 0, 9))), + Factory.Markup("'").With(SpanCodeGenerator.Null)), + Factory.Markup(" />"))); + } + + [Fact] + public void SimpleUrlInAttributeInMarkupSection() + { + ParseDocumentTest("@section Foo { }", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("Foo"), + Factory.CodeTransition(), + Factory.MetaCode("section Foo {") + .AutoCompleteWith(null, atEndOfSpan: true) + .Accepts(AcceptedCharacters.Any), + new MarkupBlock( + Factory.Markup(" (" href='", 17, 0, 17), new LocationTagged("'", 37, 0, 37)), + Factory.Markup(" href='").With(SpanCodeGenerator.Null), + Factory.Markup("~/Foo/Bar/Baz") + .WithEditorHints(EditorHints.VirtualPath) + .With(new LiteralAttributeCodeGenerator( + new LocationTagged(String.Empty, 24, 0, 24), + new LocationTagged(new ResolveUrlCodeGenerator(), 24, 0, 24))), + Factory.Markup("'").With(SpanCodeGenerator.Null)), + Factory.Markup(" /> ")), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void UrlWithExpressionsInAttributeInMarkupBlock() + { + ParseBlockTest("", + new MarkupBlock( + Factory.Markup("(" href='", 2, 0, 2), new LocationTagged("'", 22, 0, 22)), + Factory.Markup(" href='").With(SpanCodeGenerator.Null), + Factory.Markup("~/Foo/") + .WithEditorHints(EditorHints.VirtualPath) + .With(new LiteralAttributeCodeGenerator( + new LocationTagged(String.Empty, 9, 0, 9), + new LocationTagged(new ResolveUrlCodeGenerator(), 9, 0, 9))), + new MarkupBlock(new DynamicAttributeBlockCodeGenerator(new LocationTagged(String.Empty, 15, 0, 15), 15, 0, 15), + new ExpressionBlock( + Factory.CodeTransition().Accepts(AcceptedCharacters.None), + Factory.Code("id") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace))), + Factory.Markup("/Baz") + .With(new LiteralAttributeCodeGenerator(new LocationTagged(String.Empty, 18, 0, 18), new LocationTagged("/Baz", 18, 0, 18))), + Factory.Markup("'").With(SpanCodeGenerator.Null)), + Factory.Markup(" />").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void UrlWithExpressionsInAttributeInMarkupDocument() + { + ParseDocumentTest("", + new MarkupBlock( + Factory.Markup("(" href='", 2, 0, 2), new LocationTagged("'", 22, 0, 22)), + Factory.Markup(" href='").With(SpanCodeGenerator.Null), + Factory.Markup("~/Foo/") + .WithEditorHints(EditorHints.VirtualPath) + .With(new LiteralAttributeCodeGenerator( + new LocationTagged(String.Empty, 9, 0, 9), + new LocationTagged(new ResolveUrlCodeGenerator(), 9, 0, 9))), + new MarkupBlock(new DynamicAttributeBlockCodeGenerator(new LocationTagged(String.Empty, 15, 0, 15), 15, 0, 15), + new ExpressionBlock( + Factory.CodeTransition().Accepts(AcceptedCharacters.None), + Factory.Code("id") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace))), + Factory.Markup("/Baz") + .With(new LiteralAttributeCodeGenerator(new LocationTagged(String.Empty, 18, 0, 18), new LocationTagged("/Baz", 18, 0, 18))), + Factory.Markup("'").With(SpanCodeGenerator.Null)), + Factory.Markup(" />"))); + } + + [Fact] + public void UrlWithExpressionsInAttributeInMarkupSection() + { + ParseDocumentTest("@section Foo { }", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("Foo"), + Factory.CodeTransition(), + Factory.MetaCode("section Foo {") + .AutoCompleteWith(null, atEndOfSpan: true), + new MarkupBlock( + Factory.Markup(" (" href='", 17, 0, 17), new LocationTagged("'", 37, 0, 37)), + Factory.Markup(" href='").With(SpanCodeGenerator.Null), + Factory.Markup("~/Foo/") + .WithEditorHints(EditorHints.VirtualPath) + .With(new LiteralAttributeCodeGenerator( + new LocationTagged(String.Empty, 24, 0, 24), + new LocationTagged(new ResolveUrlCodeGenerator(), 24, 0, 24))), + new MarkupBlock(new DynamicAttributeBlockCodeGenerator(new LocationTagged(String.Empty, 30, 0, 30), 30, 0, 30), + new ExpressionBlock( + Factory.CodeTransition().Accepts(AcceptedCharacters.None), + Factory.Code("id") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace))), + Factory.Markup("/Baz") + .With(new LiteralAttributeCodeGenerator(new LocationTagged(String.Empty, 33, 0, 33), new LocationTagged("/Baz", 33, 0, 33))), + Factory.Markup("'").With(SpanCodeGenerator.Null)), + Factory.Markup(" /> ")), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void UrlWithComplexCharactersInAttributeInMarkupBlock() + { + ParseBlockTest("", + new MarkupBlock( + Factory.Markup("(" href='", 2, 0, 2), new LocationTagged("'", 31, 0, 31)), + Factory.Markup(" href='").With(SpanCodeGenerator.Null), + Factory.Markup("~/Foo+Bar:Baz(Biz),Boz") + .WithEditorHints(EditorHints.VirtualPath) + .With(new LiteralAttributeCodeGenerator( + new LocationTagged(String.Empty, 9, 0, 9), + new LocationTagged(new ResolveUrlCodeGenerator(), 9, 0, 9))), + Factory.Markup("'").With(SpanCodeGenerator.Null)), + Factory.Markup(" />").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void UrlWithComplexCharactersInAttributeInMarkupDocument() + { + ParseDocumentTest("", + new MarkupBlock( + Factory.Markup("(" href='", 2, 0, 2), new LocationTagged("'", 31, 0, 31)), + Factory.Markup(" href='").With(SpanCodeGenerator.Null), + Factory.Markup("~/Foo+Bar:Baz(Biz),Boz") + .WithEditorHints(EditorHints.VirtualPath) + .With(new LiteralAttributeCodeGenerator( + new LocationTagged(String.Empty, 9, 0, 9), + new LocationTagged(new ResolveUrlCodeGenerator(), 9, 0, 9))), + Factory.Markup("'").With(SpanCodeGenerator.Null)), + Factory.Markup(" />"))); + } + + [Fact] + public void UrlInUnquotedAttributeValueInMarkupBlock() + { + ParseBlockTest("", + new MarkupBlock( + Factory.Markup("(" href=", 2, 0, 2), new LocationTagged(String.Empty, 38, 0, 38)), + Factory.Markup(" href=").With(SpanCodeGenerator.Null), + Factory.Markup("~/Foo+Bar:Baz(Biz),Boz/") + .WithEditorHints(EditorHints.VirtualPath) + .With(new LiteralAttributeCodeGenerator( + new LocationTagged(String.Empty, 8, 0, 8), + new LocationTagged(new ResolveUrlCodeGenerator(), 8, 0, 8))), + new MarkupBlock(new DynamicAttributeBlockCodeGenerator(new LocationTagged(String.Empty, 31, 0, 31), 31, 0, 31), + new ExpressionBlock( + Factory.CodeTransition() + .Accepts(AcceptedCharacters.None), + Factory.Code("id") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace))), + Factory.Markup("/Boz").With(new LiteralAttributeCodeGenerator(new LocationTagged(String.Empty, 34, 0, 34), new LocationTagged("/Boz", 34, 0, 34)))), + Factory.Markup(" />").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void UrlInUnquotedAttributeValueInMarkupDocument() + { + ParseDocumentTest("", + new MarkupBlock( + Factory.Markup("(" href=", 2, 0, 2), new LocationTagged(String.Empty, 38, 0, 38)), + Factory.Markup(" href=").With(SpanCodeGenerator.Null), + Factory.Markup("~/Foo+Bar:Baz(Biz),Boz/") + .WithEditorHints(EditorHints.VirtualPath) + .With(new LiteralAttributeCodeGenerator( + new LocationTagged(String.Empty, 8, 0, 8), + new LocationTagged(new ResolveUrlCodeGenerator(), 8, 0, 8))), + new MarkupBlock(new DynamicAttributeBlockCodeGenerator(new LocationTagged(String.Empty, 31, 0, 31), 31, 0, 31), + new ExpressionBlock( + Factory.CodeTransition() + .Accepts(AcceptedCharacters.None), + Factory.Code("id") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace))), + Factory.Markup("/Boz").With(new LiteralAttributeCodeGenerator(new LocationTagged(String.Empty, 34, 0, 34), new LocationTagged("/Boz", 34, 0, 34)))), + Factory.Markup(" />"))); + } + + [Fact] + public void UrlInUnquotedAttributeValueInMarkupSection() + { + ParseDocumentTest("@section Foo { }", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("Foo"), + Factory.CodeTransition(), + Factory.MetaCode("section Foo {") + .AutoCompleteWith(null, atEndOfSpan: true), + new MarkupBlock( + Factory.Markup(" (" href=", 17, 0, 17), new LocationTagged(String.Empty, 53, 0, 53)), + Factory.Markup(" href=").With(SpanCodeGenerator.Null), + Factory.Markup("~/Foo+Bar:Baz(Biz),Boz/") + .WithEditorHints(EditorHints.VirtualPath) + .With(new LiteralAttributeCodeGenerator( + new LocationTagged(String.Empty, 23, 0, 23), + new LocationTagged(new ResolveUrlCodeGenerator(), 23, 0, 23))), + new MarkupBlock(new DynamicAttributeBlockCodeGenerator(new LocationTagged(String.Empty, 46, 0, 46), 46, 0, 46), + new ExpressionBlock( + Factory.CodeTransition() + .Accepts(AcceptedCharacters.None), + Factory.Code("id") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace))), + Factory.Markup("/Boz").With(new LiteralAttributeCodeGenerator(new LocationTagged(String.Empty, 49, 0, 49), new LocationTagged("/Boz", 49, 0, 49)))), + Factory.Markup(" /> ")), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/ParserContextTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/ParserContextTest.cs new file mode 100644 index 0000000000..ef024268a2 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/ParserContextTest.cs @@ -0,0 +1,242 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.IO; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.TestCommon; +using Moq; + +namespace Microsoft.AspNet.Razor.Test.Parser +{ + public class ParserContextTest + { + [Fact] + public void ConstructorRequiresNonNullSource() + { + var codeParser = new CSharpCodeParser(); + Assert.ThrowsArgumentNull(() => new ParserContext(null, codeParser, new HtmlMarkupParser(), codeParser), "source"); + } + + [Fact] + public void ConstructorRequiresNonNullCodeParser() + { + var codeParser = new CSharpCodeParser(); + Assert.ThrowsArgumentNull(() => new ParserContext(new SeekableTextReader(TextReader.Null), null, new HtmlMarkupParser(), codeParser), "codeParser"); + } + + [Fact] + public void ConstructorRequiresNonNullMarkupParser() + { + var codeParser = new CSharpCodeParser(); + Assert.ThrowsArgumentNull(() => new ParserContext(new SeekableTextReader(TextReader.Null), codeParser, null, codeParser), "markupParser"); + } + + [Fact] + public void ConstructorRequiresNonNullActiveParser() + { + Assert.ThrowsArgumentNull(() => new ParserContext(new SeekableTextReader(TextReader.Null), new CSharpCodeParser(), new HtmlMarkupParser(), null), "activeParser"); + } + + [Fact] + public void ConstructorThrowsIfActiveParserIsNotCodeOrMarkupParser() + { + Assert.ThrowsArgument(() => new ParserContext(new SeekableTextReader(TextReader.Null), new CSharpCodeParser(), new HtmlMarkupParser(), new CSharpCodeParser()), + "activeParser", + RazorResources.ActiveParser_Must_Be_Code_Or_Markup_Parser); + } + + [Fact] + public void ConstructorAcceptsActiveParserIfIsSameAsEitherCodeOrMarkupParser() + { + var codeParser = new CSharpCodeParser(); + var markupParser = new HtmlMarkupParser(); + new ParserContext(new SeekableTextReader(TextReader.Null), codeParser, markupParser, codeParser); + new ParserContext(new SeekableTextReader(TextReader.Null), codeParser, markupParser, markupParser); + } + + [Fact] + public void ConstructorInitializesProperties() + { + // Arrange + SeekableTextReader expectedBuffer = new SeekableTextReader(TextReader.Null); + CSharpCodeParser expectedCodeParser = new CSharpCodeParser(); + HtmlMarkupParser expectedMarkupParser = new HtmlMarkupParser(); + + // Act + ParserContext context = new ParserContext(expectedBuffer, expectedCodeParser, expectedMarkupParser, expectedCodeParser); + + // Assert + Assert.NotNull(context.Source); + Assert.Same(expectedCodeParser, context.CodeParser); + Assert.Same(expectedMarkupParser, context.MarkupParser); + Assert.Same(expectedCodeParser, context.ActiveParser); + } + + [Fact] + public void CurrentCharacterReturnsCurrentCharacterInTextBuffer() + { + // Arrange + ParserContext context = SetupTestContext("bar", b => b.Read()); + + // Act + char actual = context.CurrentCharacter; + + // Assert + Assert.Equal('a', actual); + } + + [Fact] + public void CurrentCharacterReturnsNulCharacterIfTextBufferAtEOF() + { + // Arrange + ParserContext context = SetupTestContext("bar", b => b.ReadToEnd()); + + // Act + char actual = context.CurrentCharacter; + + // Assert + Assert.Equal('\0', actual); + } + + [Fact] + public void EndOfFileReturnsFalseIfTextBufferNotAtEOF() + { + // Arrange + ParserContext context = SetupTestContext("bar"); + + // Act/Assert + Assert.False(context.EndOfFile); + } + + [Fact] + public void EndOfFileReturnsTrueIfTextBufferAtEOF() + { + // Arrange + ParserContext context = SetupTestContext("bar", b => b.ReadToEnd()); + + // Act/Assert + Assert.True(context.EndOfFile); + } + + [Fact] + public void StartBlockCreatesNewBlock() + { + // Arrange + ParserContext context = SetupTestContext("phoo"); + + // Act + context.StartBlock(BlockType.Expression); + + // Assert + Assert.Equal(1, context.BlockStack.Count); + Assert.Equal(BlockType.Expression, context.BlockStack.Peek().Type); + } + + [Fact] + public void EndBlockAddsCurrentBlockToParentBlock() + { + // Arrange + Mock mockListener = new Mock(); + ParserContext context = SetupTestContext("phoo"); + + // Act + context.StartBlock(BlockType.Expression); + context.StartBlock(BlockType.Statement); + context.EndBlock(); + + // Assert + Assert.Equal(1, context.BlockStack.Count); + Assert.Equal(BlockType.Expression, context.BlockStack.Peek().Type); + Assert.Equal(1, context.BlockStack.Peek().Children.Count); + Assert.Equal(BlockType.Statement, ((Block)context.BlockStack.Peek().Children[0]).Type); + } + + [Fact] + public void AddSpanAddsSpanToCurrentBlockBuilder() + { + // Arrange + var factory = SpanFactory.CreateCsHtml(); + Mock mockListener = new Mock(); + ParserContext context = SetupTestContext("phoo"); + + SpanBuilder builder = new SpanBuilder() + { + Kind = SpanKind.Code + }; + builder.Accept(new CSharpSymbol(1, 0, 1, "foo", CSharpSymbolType.Identifier)); + Span added = builder.Build(); + + using (context.StartBlock(BlockType.Functions)) + { + context.AddSpan(added); + } + + BlockBuilder expected = new BlockBuilder() + { + Type = BlockType.Functions, + }; + expected.Children.Add(added); + + // Assert + ParserTestBase.EvaluateResults(context.CompleteParse(), expected.Build()); + } + + [Fact] + public void SwitchActiveParserSetsMarkupParserAsActiveIfCodeParserCurrentlyActive() + { + // Arrange + var codeParser = new CSharpCodeParser(); + var markupParser = new HtmlMarkupParser(); + ParserContext context = SetupTestContext("barbazbiz", b => b.Read(), codeParser, markupParser, codeParser); + Assert.Same(codeParser, context.ActiveParser); + + // Act + context.SwitchActiveParser(); + + // Assert + Assert.Same(markupParser, context.ActiveParser); + } + + [Fact] + public void SwitchActiveParserSetsCodeParserAsActiveIfMarkupParserCurrentlyActive() + { + // Arrange + var codeParser = new CSharpCodeParser(); + var markupParser = new HtmlMarkupParser(); + ParserContext context = SetupTestContext("barbazbiz", b => b.Read(), codeParser, markupParser, markupParser); + Assert.Same(markupParser, context.ActiveParser); + + // Act + context.SwitchActiveParser(); + + // Assert + Assert.Same(codeParser, context.ActiveParser); + } + + private ParserContext SetupTestContext(string document) + { + var codeParser = new CSharpCodeParser(); + var markupParser = new HtmlMarkupParser(); + return SetupTestContext(document, b => { }, codeParser, markupParser, codeParser); + } + + private ParserContext SetupTestContext(string document, Action positioningAction) + { + var codeParser = new CSharpCodeParser(); + var markupParser = new HtmlMarkupParser(); + return SetupTestContext(document, positioningAction, codeParser, markupParser, codeParser); + } + + private ParserContext SetupTestContext(string document, Action positioningAction, ParserBase codeParser, ParserBase markupParser, ParserBase activeParser) + { + ParserContext context = new ParserContext(new SeekableTextReader(new StringReader(document)), codeParser, markupParser, activeParser); + positioningAction(context.Source); + return context; + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/ParserVisitorExtensionsTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/ParserVisitorExtensionsTest.cs new file mode 100644 index 0000000000..611227ec2b --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/ParserVisitorExtensionsTest.cs @@ -0,0 +1,83 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.Collections.Generic; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.TestCommon; +using Moq; + +namespace Microsoft.AspNet.Razor.Test.Parser +{ + public class ParserVisitorExtensionsTest + { + [Fact] + public void VisitThrowsOnNullVisitor() + { + ParserVisitor target = null; + ParserResults results = new ParserResults(new BlockBuilder() { Type = BlockType.Comment }.Build(), new List()); + + Assert.ThrowsArgumentNull(() => target.Visit(results), "self"); + } + + [Fact] + public void VisitThrowsOnNullResults() + { + ParserVisitor target = new Mock().Object; + Assert.ThrowsArgumentNull(() => target.Visit(null), "result"); + } + + [Fact] + public void VisitSendsDocumentToVisitor() + { + // Arrange + Mock targetMock = new Mock(); + Block root = new BlockBuilder() { Type = BlockType.Comment }.Build(); + ParserResults results = new ParserResults(root, new List()); + + // Act + targetMock.Object.Visit(results); + + // Assert + targetMock.Verify(v => v.VisitBlock(root)); + } + + [Fact] + public void VisitSendsErrorsToVisitor() + { + // Arrange + Mock targetMock = new Mock(); + Block root = new BlockBuilder() { Type = BlockType.Comment }.Build(); + List errors = new List() { + new RazorError("Foo", 1, 0, 1), + new RazorError("Bar", 2, 0, 2) + }; + ParserResults results = new ParserResults(root, errors); + + // Act + targetMock.Object.Visit(results); + + // Assert + targetMock.Verify(v => v.VisitError(errors[0])); + targetMock.Verify(v => v.VisitError(errors[1])); + } + + [Fact] + public void VisitCallsOnCompleteWhenAllNodesHaveBeenVisited() + { + // Arrange + Mock targetMock = new Mock(); + Block root = new BlockBuilder() { Type = BlockType.Comment }.Build(); + List errors = new List() { + new RazorError("Foo", 1, 0, 1), + new RazorError("Bar", 2, 0, 2) + }; + ParserResults results = new ParserResults(root, errors); + + // Act + targetMock.Object.Visit(results); + + // Assert + targetMock.Verify(v => v.OnComplete()); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/PartialParsing/CSharpPartialParsingTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/PartialParsing/CSharpPartialParsingTest.cs new file mode 100644 index 0000000000..506aa2619e --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/PartialParsing/CSharpPartialParsingTest.cs @@ -0,0 +1,638 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using System.Web.WebPages.TestUtils; +using Microsoft.TestCommon; +using System; + +namespace Microsoft.AspNet.Razor.Test.Parser.PartialParsing +{ + public class CSharpPartialParsingTest : PartialParsingTestBase + { + [Fact] + public void ImplicitExpressionAcceptsInnerInsertionsInStatementBlock() + { + // Arrange + SpanFactory factory = SpanFactory.CreateCsHtml(); + StringTextBuffer changed = new StringTextBuffer("@{" + Environment.NewLine + + " @DateTime..Now" + Environment.NewLine + + "}"); + StringTextBuffer old = new StringTextBuffer("@{" + Environment.NewLine + + " @DateTime.Now" + Environment.NewLine + + "}"); + + // Act and Assert + RunPartialParseTest(new TextChange(17, 0, old, 1, changed), + new MarkupBlock( + factory.EmptyHtml(), + new StatementBlock( + factory.CodeTransition(), + factory.MetaCode("{").Accepts(AcceptedCharacters.None), + factory.Code("\r\n ").AsStatement(), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("DateTime..Now") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Code("\r\n").AsStatement(), + factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + factory.EmptyHtml())); + } + + [Fact] + public void ImplicitExpressionAcceptsInnerInsertions() + { + // Arrange + SpanFactory factory = SpanFactory.CreateCsHtml(); + StringTextBuffer changed = new StringTextBuffer("foo @DateTime..Now baz"); + StringTextBuffer old = new StringTextBuffer("foo @DateTime.Now baz"); + + // Act and Assert + RunPartialParseTest(new TextChange(13, 0, old, 1, changed), + new MarkupBlock( + factory.Markup("foo "), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("DateTime..Now").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Markup(" baz")), additionalFlags: PartialParseResult.Provisional); + } + + + [Fact] + public void ImplicitExpressionAcceptsDotlessCommitInsertionsInStatementBlockAfterIdentifiers() + { + SpanFactory factory = SpanFactory.CreateCsHtml(); + StringTextBuffer changed = new StringTextBuffer("@{" + Environment.NewLine + + " @DateTime." + Environment.NewLine + + "}"); + StringTextBuffer old = new StringTextBuffer("@{" + Environment.NewLine + + " @DateTime" + Environment.NewLine + + "}"); + + var textChange = new TextChange(17, 0, old, 1, changed); + TestParserManager manager = CreateParserManager(); + Action applyAndVerifyPartialChange = (changeToApply, expectedResult, expectedCode) => + { + PartialParseResult result = manager.CheckForStructureChangesAndWait(textChange); + + // Assert + Assert.Equal(expectedResult, result); + Assert.Equal(1, manager.ParseCount); + ParserTestBase.EvaluateParseTree(manager.Parser.CurrentParseTree, new MarkupBlock( + factory.EmptyHtml(), + new StatementBlock( + factory.CodeTransition(), + factory.MetaCode("{").Accepts(AcceptedCharacters.None), + factory.Code("\r\n ").AsStatement(), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code(expectedCode) + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Code("\r\n").AsStatement(), + factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + factory.EmptyHtml())); + }; + + manager.InitializeWithDocument(textChange.OldBuffer); + + // This is the process of a dotless commit when doing "." insertions to commit intellisense changes. + applyAndVerifyPartialChange(textChange, PartialParseResult.Accepted, "DateTime."); + + old = changed; + changed = new StringTextBuffer("@{" + Environment.NewLine + + " @DateTime.." + Environment.NewLine + + "}"); + textChange = new TextChange(18, 0, old, 1, changed); + + applyAndVerifyPartialChange(textChange, PartialParseResult.Accepted, "DateTime.."); + + old = changed; + changed = new StringTextBuffer("@{" + Environment.NewLine + + " @DateTime.Now." + Environment.NewLine + + "}"); + textChange = new TextChange(18, 0, old, 3, changed); + + applyAndVerifyPartialChange(textChange, PartialParseResult.Accepted, "DateTime.Now."); + } + + [Fact] + public void ImplicitExpressionAcceptsDotlessCommitInsertionsInStatementBlock() + { + SpanFactory factory = SpanFactory.CreateCsHtml(); + StringTextBuffer changed = new StringTextBuffer("@{" + Environment.NewLine + + " @DateT." + Environment.NewLine + + "}"); + StringTextBuffer old = new StringTextBuffer("@{" + Environment.NewLine + + " @DateT" + Environment.NewLine + + "}"); + + var textChange = new TextChange(14, 0, old, 1, changed); + TestParserManager manager = CreateParserManager(); + Action applyAndVerifyPartialChange = (changeToApply, expectedResult, expectedCode) => + { + PartialParseResult result = manager.CheckForStructureChangesAndWait(textChange); + + // Assert + Assert.Equal(expectedResult, result); + Assert.Equal(1, manager.ParseCount); + ParserTestBase.EvaluateParseTree(manager.Parser.CurrentParseTree, new MarkupBlock( + factory.EmptyHtml(), + new StatementBlock( + factory.CodeTransition(), + factory.MetaCode("{").Accepts(AcceptedCharacters.None), + factory.Code("\r\n ").AsStatement(), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code(expectedCode) + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Code("\r\n").AsStatement(), + factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + factory.EmptyHtml())); + }; + + manager.InitializeWithDocument(textChange.OldBuffer); + + // This is the process of a dotless commit when doing "." insertions to commit intellisense changes. + applyAndVerifyPartialChange(textChange, PartialParseResult.Accepted, "DateT."); + + old = changed; + changed = new StringTextBuffer("@{" + Environment.NewLine + + " @DateTime." + Environment.NewLine + + "}"); + textChange = new TextChange(14, 0, old, 3, changed); + + applyAndVerifyPartialChange(textChange, PartialParseResult.Accepted, "DateTime."); + } + + [Fact] + public void ImplicitExpressionProvisionallyAcceptsDotlessCommitInsertions() + { + SpanFactory factory = SpanFactory.CreateCsHtml(); + var changed = new StringTextBuffer("foo @DateT. baz"); + var old = new StringTextBuffer("foo @DateT baz"); + var textChange = new TextChange(10, 0, old, 1, changed); + TestParserManager manager = CreateParserManager(); + Action applyAndVerifyPartialChange = (changeToApply, expectedResult, expectedCode) => + { + PartialParseResult result = manager.CheckForStructureChangesAndWait(textChange); + + // Assert + Assert.Equal(expectedResult, result); + Assert.Equal(1, manager.ParseCount); + + ParserTestBase.EvaluateParseTree(manager.Parser.CurrentParseTree, new MarkupBlock( + factory.Markup("foo "), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code(expectedCode).AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Markup(" baz"))); + }; + + manager.InitializeWithDocument(textChange.OldBuffer); + + // This is the process of a dotless commit when doing "." insertions to commit intellisense changes. + applyAndVerifyPartialChange(textChange, PartialParseResult.Accepted | PartialParseResult.Provisional, "DateT."); + + old = changed; + changed = new StringTextBuffer("foo @DateTime. baz"); + textChange = new TextChange(10, 0, old, 3, changed); + + applyAndVerifyPartialChange(textChange, PartialParseResult.Accepted | PartialParseResult.Provisional, "DateTime."); + } + + [Fact] + public void ImplicitExpressionProvisionallyAcceptsDotlessCommitInsertionsAfterIdentifiers() + { + SpanFactory factory = SpanFactory.CreateCsHtml(); + var changed = new StringTextBuffer("foo @DateTime. baz"); + var old = new StringTextBuffer("foo @DateTime baz"); + var textChange = new TextChange(13, 0, old, 1, changed); + TestParserManager manager = CreateParserManager(); + Action applyAndVerifyPartialChange = (changeToApply, expectedResult, expectedCode) => + { + PartialParseResult result = manager.CheckForStructureChangesAndWait(textChange); + + // Assert + Assert.Equal(expectedResult, result); + Assert.Equal(1, manager.ParseCount); + + ParserTestBase.EvaluateParseTree(manager.Parser.CurrentParseTree, new MarkupBlock( + factory.Markup("foo "), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code(expectedCode).AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Markup(" baz"))); + }; + + manager.InitializeWithDocument(textChange.OldBuffer); + + // This is the process of a dotless commit when doing "." insertions to commit intellisense changes. + applyAndVerifyPartialChange(textChange, PartialParseResult.Accepted | PartialParseResult.Provisional, "DateTime."); + + old = changed; + changed = new StringTextBuffer("foo @DateTime.. baz"); + textChange = new TextChange(14, 0, old, 1, changed); + + applyAndVerifyPartialChange(textChange, PartialParseResult.Accepted | PartialParseResult.Provisional, "DateTime.."); + + old = changed; + changed = new StringTextBuffer("foo @DateTime.Now. baz"); + textChange = new TextChange(14, 0, old, 3, changed); + + applyAndVerifyPartialChange(textChange, PartialParseResult.Accepted | PartialParseResult.Provisional, "DateTime.Now."); + } + + [Fact] + public void ImplicitExpressionProvisionallyAcceptsDeleteOfIdentifierPartsIfDotRemains() + { + SpanFactory factory = SpanFactory.CreateCsHtml(); + StringTextBuffer changed = new StringTextBuffer("foo @User. baz"); + StringTextBuffer old = new StringTextBuffer("foo @User.Name baz"); + RunPartialParseTest(new TextChange(10, 4, old, 0, changed), + new MarkupBlock( + factory.Markup("foo "), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("User.").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Markup(" baz")), + additionalFlags: PartialParseResult.Provisional); + } + + [Fact] + public void ImplicitExpressionAcceptsDeleteOfIdentifierPartsIfSomeOfIdentifierRemains() + { + var factory = SpanFactory.CreateCsHtml(); + StringTextBuffer changed = new StringTextBuffer("foo @Us baz"); + StringTextBuffer old = new StringTextBuffer("foo @User baz"); + RunPartialParseTest(new TextChange(7, 2, old, 0, changed), + new MarkupBlock( + factory.Markup("foo "), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("Us").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Markup(" baz"))); + } + + [Fact] + public void ImplicitExpressionProvisionallyAcceptsMultipleInsertionIfItCausesIdentifierExpansionAndTrailingDot() + { + var factory = SpanFactory.CreateCsHtml(); + StringTextBuffer changed = new StringTextBuffer("foo @User. baz"); + StringTextBuffer old = new StringTextBuffer("foo @U baz"); + RunPartialParseTest(new TextChange(6, 0, old, 4, changed), + new MarkupBlock( + factory.Markup("foo "), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("User.").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Markup(" baz")), + additionalFlags: PartialParseResult.Provisional); + } + + [Fact] + public void ImplicitExpressionAcceptsMultipleInsertionIfItOnlyCausesIdentifierExpansion() + { + var factory = SpanFactory.CreateCsHtml(); + StringTextBuffer changed = new StringTextBuffer("foo @barbiz baz"); + StringTextBuffer old = new StringTextBuffer("foo @bar baz"); + RunPartialParseTest(new TextChange(8, 0, old, 3, changed), + new MarkupBlock( + factory.Markup("foo "), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("barbiz").AsImplicitExpression(CSharpCodeParser.DefaultKeywords).Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Markup(" baz"))); + } + + [Fact] + public void ImplicitExpressionAcceptsIdentifierExpansionAtEndOfNonWhitespaceCharacters() + { + var factory = SpanFactory.CreateCsHtml(); + StringTextBuffer changed = new StringTextBuffer("@{" + Environment.NewLine + + " @food" + Environment.NewLine + + "}"); + StringTextBuffer old = new StringTextBuffer("@{" + Environment.NewLine + + " @foo" + Environment.NewLine + + "}"); + RunPartialParseTest(new TextChange(12, 0, old, 1, changed), + new MarkupBlock( + factory.EmptyHtml(), + new StatementBlock( + factory.CodeTransition(), + factory.MetaCode("{").Accepts(AcceptedCharacters.None), + factory.Code("\r\n ").AsStatement(), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("food") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Code("\r\n").AsStatement(), + factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + factory.EmptyHtml())); + } + + [Fact] + public void ImplicitExpressionAcceptsIdentifierAfterDotAtEndOfNonWhitespaceCharacters() + { + var factory = SpanFactory.CreateCsHtml(); + StringTextBuffer changed = new StringTextBuffer("@{" + Environment.NewLine + + " @foo.d" + Environment.NewLine + + "}"); + StringTextBuffer old = new StringTextBuffer("@{" + Environment.NewLine + + " @foo." + Environment.NewLine + + "}"); + RunPartialParseTest(new TextChange(13, 0, old, 1, changed), + new MarkupBlock( + factory.EmptyHtml(), + new StatementBlock( + factory.CodeTransition(), + factory.MetaCode("{").Accepts(AcceptedCharacters.None), + factory.Code("\r\n ").AsStatement(), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("foo.d") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Code("\r\n").AsStatement(), + factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + factory.EmptyHtml())); + } + + [Fact] + public void ImplicitExpressionAcceptsDotAtEndOfNonWhitespaceCharacters() + { + var factory = SpanFactory.CreateCsHtml(); + StringTextBuffer changed = new StringTextBuffer("@{" + Environment.NewLine + + " @foo." + Environment.NewLine + + "}"); + StringTextBuffer old = new StringTextBuffer("@{" + Environment.NewLine + + " @foo" + Environment.NewLine + + "}"); + RunPartialParseTest(new TextChange(12, 0, old, 1, changed), + new MarkupBlock( + factory.EmptyHtml(), + new StatementBlock( + factory.CodeTransition(), + factory.MetaCode("{").Accepts(AcceptedCharacters.None), + factory.Code("\r\n ").AsStatement(), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code(@"foo.") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Code("\r\n").AsStatement(), + factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + factory.EmptyHtml())); + } + + [Fact] + public void ImplicitExpressionRejectsChangeWhichWouldHaveBeenAcceptedIfLastChangeWasProvisionallyAcceptedOnDifferentSpan() + { + var factory = SpanFactory.CreateCsHtml(); + + // Arrange + TextChange dotTyped = new TextChange(8, 0, new StringTextBuffer("foo @foo @bar"), 1, new StringTextBuffer("foo @foo. @bar")); + TextChange charTyped = new TextChange(14, 0, new StringTextBuffer("foo @foo. @bar"), 1, new StringTextBuffer("foo @foo. @barb")); + TestParserManager manager = CreateParserManager(); + manager.InitializeWithDocument(dotTyped.OldBuffer); + + // Apply the dot change + Assert.Equal(PartialParseResult.Provisional | PartialParseResult.Accepted, manager.CheckForStructureChangesAndWait(dotTyped)); + + // Act (apply the identifier start char change) + PartialParseResult result = manager.CheckForStructureChangesAndWait(charTyped); + + // Assert + Assert.Equal(PartialParseResult.Rejected, result); + Assert.False(manager.Parser.LastResultProvisional, "LastResultProvisional flag should have been cleared but it was not"); + ParserTestBase.EvaluateParseTree(manager.Parser.CurrentParseTree, + new MarkupBlock( + factory.Markup("foo "), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("foo") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Markup(". "), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("barb") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.EmptyHtml())); + } + + [Fact] + public void ImplicitExpressionAcceptsIdentifierTypedAfterDotIfLastChangeWasProvisionalAcceptanceOfDot() + { + var factory = SpanFactory.CreateCsHtml(); + + // Arrange + TextChange dotTyped = new TextChange(8, 0, new StringTextBuffer("foo @foo bar"), 1, new StringTextBuffer("foo @foo. bar")); + TextChange charTyped = new TextChange(9, 0, new StringTextBuffer("foo @foo. bar"), 1, new StringTextBuffer("foo @foo.b bar")); + TestParserManager manager = CreateParserManager(); + manager.InitializeWithDocument(dotTyped.OldBuffer); + + // Apply the dot change + Assert.Equal(PartialParseResult.Provisional | PartialParseResult.Accepted, manager.CheckForStructureChangesAndWait(dotTyped)); + + // Act (apply the identifier start char change) + PartialParseResult result = manager.CheckForStructureChangesAndWait(charTyped); + + // Assert + Assert.Equal(PartialParseResult.Accepted, result); + Assert.False(manager.Parser.LastResultProvisional, "LastResultProvisional flag should have been cleared but it was not"); + ParserTestBase.EvaluateParseTree(manager.Parser.CurrentParseTree, + new MarkupBlock( + factory.Markup("foo "), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("foo.b") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Markup(" bar"))); + } + + [Fact] + public void ImplicitExpressionProvisionallyAcceptsDotAfterIdentifierInMarkup() + { + var factory = SpanFactory.CreateCsHtml(); + StringTextBuffer changed = new StringTextBuffer("foo @foo. bar"); + StringTextBuffer old = new StringTextBuffer("foo @foo bar"); + RunPartialParseTest(new TextChange(8, 0, old, 1, changed), + new MarkupBlock( + factory.Markup("foo "), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("foo.") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Markup(" bar")), + additionalFlags: PartialParseResult.Provisional); + } + + [Fact] + public void ImplicitExpressionAcceptsAdditionalIdentifierCharactersIfEndOfSpanIsIdentifier() + { + var factory = SpanFactory.CreateCsHtml(); + StringTextBuffer changed = new StringTextBuffer("foo @foob bar"); + StringTextBuffer old = new StringTextBuffer("foo @foo bar"); + RunPartialParseTest(new TextChange(8, 0, old, 1, changed), + new MarkupBlock( + factory.Markup("foo "), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("foob") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Markup(" bar"))); + } + + [Fact] + public void ImplicitExpressionAcceptsAdditionalIdentifierStartCharactersIfEndOfSpanIsDot() + { + var factory = SpanFactory.CreateCsHtml(); + StringTextBuffer changed = new StringTextBuffer("@{@foo.b}"); + StringTextBuffer old = new StringTextBuffer("@{@foo.}"); + RunPartialParseTest(new TextChange(7, 0, old, 1, changed), + new MarkupBlock( + factory.EmptyHtml(), + new StatementBlock( + factory.CodeTransition(), + factory.MetaCode("{").Accepts(AcceptedCharacters.None), + factory.EmptyCSharp().AsStatement(), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("foo.b") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.EmptyCSharp().AsStatement(), + factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + factory.EmptyHtml())); + } + + [Fact] + public void ImplicitExpressionAcceptsDotIfTrailingDotsAreAllowed() + { + var factory = SpanFactory.CreateCsHtml(); + StringTextBuffer changed = new StringTextBuffer("@{@foo.}"); + StringTextBuffer old = new StringTextBuffer("@{@foo}"); + RunPartialParseTest(new TextChange(6, 0, old, 1, changed), + new MarkupBlock( + factory.EmptyHtml(), + new StatementBlock( + factory.CodeTransition(), + factory.MetaCode("{").Accepts(AcceptedCharacters.None), + factory.EmptyCSharp().AsStatement(), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("foo.") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.EmptyCSharp().AsStatement(), + factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + factory.EmptyHtml())); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfIfKeywordTyped() + { + RunTypeKeywordTest("if"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfDoKeywordTyped() + { + RunTypeKeywordTest("do"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfTryKeywordTyped() + { + RunTypeKeywordTest("try"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfForKeywordTyped() + { + RunTypeKeywordTest("for"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfForEachKeywordTyped() + { + RunTypeKeywordTest("foreach"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfWhileKeywordTyped() + { + RunTypeKeywordTest("while"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfSwitchKeywordTyped() + { + RunTypeKeywordTest("switch"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfLockKeywordTyped() + { + RunTypeKeywordTest("lock"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfUsingKeywordTyped() + { + RunTypeKeywordTest("using"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfSectionKeywordTyped() + { + RunTypeKeywordTest("section"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfInheritsKeywordTyped() + { + RunTypeKeywordTest("inherits"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfHelperKeywordTyped() + { + RunTypeKeywordTest("helper"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfFunctionsKeywordTyped() + { + RunTypeKeywordTest("functions"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfNamespaceKeywordTyped() + { + RunTypeKeywordTest("namespace"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfClassKeywordTyped() + { + RunTypeKeywordTest("class"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfLayoutKeywordTyped() + { + RunTypeKeywordTest("layout"); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/PartialParsing/PartialParsingTestBase.cs b/test/Microsoft.AspNet.Razor.Test/Parser/PartialParsing/PartialParsingTestBase.cs new file mode 100644 index 0000000000..732837fe49 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/PartialParsing/PartialParsingTestBase.cs @@ -0,0 +1,114 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.Threading; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Test.Utils; +using Microsoft.AspNet.Razor.Text; +using System.Web.WebPages.TestUtils; +using Microsoft.TestCommon; +using System; + +namespace Microsoft.AspNet.Razor.Test.Parser.PartialParsing +{ + public abstract class PartialParsingTestBase + where TLanguage : RazorCodeLanguage, new() + { + private const string TestLinePragmaFileName = "C:\\This\\Path\\Is\\Just\\For\\Line\\Pragmas.cshtml"; + + protected static void RunFullReparseTest(TextChange change, PartialParseResult additionalFlags = (PartialParseResult)0) + { + // Arrange + TestParserManager manager = CreateParserManager(); + manager.InitializeWithDocument(change.OldBuffer); + + // Act + PartialParseResult result = manager.CheckForStructureChangesAndWait(change); + + // Assert + Assert.Equal(PartialParseResult.Rejected | additionalFlags, result); + Assert.Equal(2, manager.ParseCount); + } + + protected static void RunPartialParseTest(TextChange change, Block newTreeRoot, PartialParseResult additionalFlags = (PartialParseResult)0) + { + // Arrange + TestParserManager manager = CreateParserManager(); + manager.InitializeWithDocument(change.OldBuffer); + + // Act + PartialParseResult result = manager.CheckForStructureChangesAndWait(change); + + // Assert + Assert.Equal(PartialParseResult.Accepted | additionalFlags, result); + Assert.Equal(1, manager.ParseCount); + ParserTestBase.EvaluateParseTree(manager.Parser.CurrentParseTree, newTreeRoot); + } + + protected static TestParserManager CreateParserManager() + { + RazorEngineHost host = CreateHost(); + RazorEditorParser parser = new RazorEditorParser(host, TestLinePragmaFileName); + return new TestParserManager(parser); + } + + protected static RazorEngineHost CreateHost() + { + return new RazorEngineHost(new TLanguage()) + { + GeneratedClassContext = new GeneratedClassContext("Execute", "Write", "WriteLiteral", "WriteTo", "WriteLiteralTo", "Template", "DefineSection"), + DesignTimeMode = true + }; + } + + protected static void RunTypeKeywordTest(string keyword) + { + string before = "@" + keyword.Substring(0, keyword.Length - 1); + string after = "@" + keyword; + StringTextBuffer changed = new StringTextBuffer(after); + StringTextBuffer old = new StringTextBuffer(before); + RunFullReparseTest(new TextChange(keyword.Length, 0, old, 1, changed), additionalFlags: PartialParseResult.SpanContextChanged); + } + + protected class TestParserManager + { + public RazorEditorParser Parser; + public ManualResetEventSlim ParserComplete; + public int ParseCount; + + public TestParserManager(RazorEditorParser parser) + { + ParserComplete = new ManualResetEventSlim(); + ParseCount = 0; + Parser = parser; + parser.DocumentParseComplete += (sender, args) => + { + Interlocked.Increment(ref ParseCount); + ParserComplete.Set(); + }; + } + + public void InitializeWithDocument(ITextBuffer startDocument) + { + CheckForStructureChangesAndWait(new TextChange(0, 0, new StringTextBuffer(String.Empty), startDocument.Length, startDocument)); + } + + public PartialParseResult CheckForStructureChangesAndWait(TextChange change) + { + PartialParseResult result = Parser.CheckForStructureChanges(change); + if (result.HasFlag(PartialParseResult.Rejected)) + { + WaitForParse(); + } + return result; + } + + public void WaitForParse() + { + MiscUtils.DoWithTimeoutIfNotDebugging(ParserComplete.Wait); // Wait for the parse to finish + ParserComplete.Reset(); + } + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/PartialParsing/VBPartialParsingTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/PartialParsing/VBPartialParsingTest.cs new file mode 100644 index 0000000000..64e2552a74 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/PartialParsing/VBPartialParsingTest.cs @@ -0,0 +1,375 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using System.Web.WebPages.TestUtils; +using Microsoft.TestCommon; +using System; + +namespace Microsoft.AspNet.Razor.Test.Parser.PartialParsing +{ + public class VBPartialParsingTest : PartialParsingTestBase + { + [Fact] + public void ImplicitExpressionProvisionallyAcceptsDeleteOfIdentifierPartsIfDotRemains() + { + var factory = SpanFactory.CreateVbHtml(); + StringTextBuffer changed = new StringTextBuffer("foo @User. baz"); + StringTextBuffer old = new StringTextBuffer("foo @User.Name baz"); + RunPartialParseTest(new TextChange(10, 4, old, 0, changed), + new MarkupBlock( + factory.Markup("foo "), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("User.") + .AsImplicitExpression(VBCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Markup(" baz")), + additionalFlags: PartialParseResult.Provisional); + } + + [Fact] + public void ImplicitExpressionAcceptsDeleteOfIdentifierPartsIfSomeOfIdentifierRemains() + { + var factory = SpanFactory.CreateVbHtml(); + StringTextBuffer changed = new StringTextBuffer("foo @Us baz"); + StringTextBuffer old = new StringTextBuffer("foo @User baz"); + RunPartialParseTest(new TextChange(7, 2, old, 0, changed), + new MarkupBlock( + factory.Markup("foo "), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("Us") + .AsImplicitExpression(VBCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Markup(" baz"))); + } + + [Fact] + public void ImplicitExpressionProvisionallyAcceptsMultipleInsertionIfItCausesIdentifierExpansionAndTrailingDot() + { + var factory = SpanFactory.CreateVbHtml(); + StringTextBuffer changed = new StringTextBuffer("foo @User. baz"); + StringTextBuffer old = new StringTextBuffer("foo @U baz"); + RunPartialParseTest(new TextChange(6, 0, old, 4, changed), + new MarkupBlock( + factory.Markup("foo "), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("User.") + .AsImplicitExpression(VBCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Markup(" baz")), + additionalFlags: PartialParseResult.Provisional); + } + + [Fact] + public void ImplicitExpressionAcceptsMultipleInsertionIfItOnlyCausesIdentifierExpansion() + { + var factory = SpanFactory.CreateVbHtml(); + StringTextBuffer changed = new StringTextBuffer("foo @barbiz baz"); + StringTextBuffer old = new StringTextBuffer("foo @bar baz"); + RunPartialParseTest(new TextChange(8, 0, old, 3, changed), + new MarkupBlock( + factory.Markup("foo "), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("barbiz") + .AsImplicitExpression(VBCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Markup(" baz"))); + } + + [Fact] + public void ImplicitExpressionRejectsChangeWhichWouldHaveBeenAcceptedIfLastChangeWasProvisionallyAcceptedOnDifferentSpan() + { + var factory = SpanFactory.CreateVbHtml(); + + // Arrange + TextChange dotTyped = new TextChange(8, 0, new StringTextBuffer("foo @foo @bar"), 1, new StringTextBuffer("foo @foo. @bar")); + TextChange charTyped = new TextChange(14, 0, new StringTextBuffer("foo @foo. @barb"), 1, new StringTextBuffer("foo @foo. @barb")); + TestParserManager manager = CreateParserManager(); + manager.InitializeWithDocument(dotTyped.OldBuffer); + + // Apply the dot change + Assert.Equal(PartialParseResult.Provisional | PartialParseResult.Accepted, manager.CheckForStructureChangesAndWait(dotTyped)); + + // Act (apply the identifier start char change) + PartialParseResult result = manager.CheckForStructureChangesAndWait(charTyped); + + // Assert + Assert.Equal(PartialParseResult.Rejected, result); + Assert.False(manager.Parser.LastResultProvisional, "LastResultProvisional flag should have been cleared but it was not"); + ParserTestBase.EvaluateParseTree(manager.Parser.CurrentParseTree, + new MarkupBlock( + factory.Markup("foo "), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("foo") + .AsImplicitExpression(VBCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Markup(". "), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("barb") + .AsImplicitExpression(VBCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.EmptyHtml())); + } + + [Fact] + public void ImplicitExpressionAcceptsIdentifierTypedAfterDotIfLastChangeWasProvisionalAcceptanceOfDot() + { + var factory = SpanFactory.CreateVbHtml(); + + // Arrange + TextChange dotTyped = new TextChange(8, 0, new StringTextBuffer("foo @foo bar"), 1, new StringTextBuffer("foo @foo. bar")); + TextChange charTyped = new TextChange(9, 0, new StringTextBuffer("foo @foo. bar"), 1, new StringTextBuffer("foo @foo.b bar")); + TestParserManager manager = CreateParserManager(); + manager.InitializeWithDocument(dotTyped.OldBuffer); + + // Apply the dot change + Assert.Equal(PartialParseResult.Provisional | PartialParseResult.Accepted, manager.CheckForStructureChangesAndWait(dotTyped)); + + // Act (apply the identifier start char change) + PartialParseResult result = manager.CheckForStructureChangesAndWait(charTyped); + + // Assert + Assert.Equal(PartialParseResult.Accepted, result); + Assert.False(manager.Parser.LastResultProvisional, "LastResultProvisional flag should have been cleared but it was not"); + ParserTestBase.EvaluateParseTree(manager.Parser.CurrentParseTree, + new MarkupBlock( + factory.Markup("foo "), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("foo.b") + .AsImplicitExpression(VBCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Markup(" bar"))); + } + [Fact] + public void ImplicitExpressionAcceptsIdentifierExpansionAtEndOfNonWhitespaceCharacters() + { + var factory = SpanFactory.CreateVbHtml(); + StringTextBuffer changed = new StringTextBuffer("@Code" + Environment.NewLine + + " @food" + Environment.NewLine + + "End Code"); + StringTextBuffer old = new StringTextBuffer("@Code" + Environment.NewLine + + " @foo" + Environment.NewLine + + "End Code"); + RunPartialParseTest(new TextChange(15, 0, old, 1, changed), + new MarkupBlock( + factory.EmptyHtml(), + new StatementBlock( + factory.CodeTransition(), + factory.MetaCode("Code") + .Accepts(AcceptedCharacters.None), + factory.Code("\r\n ").AsStatement(), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("food") + .AsImplicitExpression(VBCodeParser.DefaultKeywords, acceptTrailingDot: true) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Code("\r\n").AsStatement(), + factory.MetaCode("End Code").Accepts(AcceptedCharacters.None)), + factory.EmptyHtml())); + } + + [Fact] + public void ImplicitExpressionProvisionallyAcceptsDotAfterIdentifierInMarkup() + { + var factory = SpanFactory.CreateVbHtml(); + StringTextBuffer changed = new StringTextBuffer("foo @foo. bar"); + StringTextBuffer old = new StringTextBuffer("foo @foo bar"); + RunPartialParseTest(new TextChange(8, 0, old, 1, changed), + new MarkupBlock( + factory.Markup("foo "), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("foo.") + .AsImplicitExpression(VBCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Markup(" bar")), + additionalFlags: PartialParseResult.Provisional); + } + + [Fact] + public void ImplicitExpressionAcceptsAdditionalIdentifierCharactersIfEndOfSpanIsIdentifier() + { + var factory = SpanFactory.CreateVbHtml(); + StringTextBuffer changed = new StringTextBuffer("foo @foob baz"); + StringTextBuffer old = new StringTextBuffer("foo @foo bar"); + RunPartialParseTest(new TextChange(8, 0, old, 1, changed), + new MarkupBlock( + factory.Markup("foo "), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("foob") + .AsImplicitExpression(VBCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Markup(" bar"))); + } + + [Fact] + public void ImplicitExpressionAcceptsAdditionalIdentifierStartCharactersIfEndOfSpanIsDot() + { + var factory = SpanFactory.CreateVbHtml(); + StringTextBuffer changed = new StringTextBuffer("@Code @foo.b End Code"); + StringTextBuffer old = new StringTextBuffer("@Code @foo. End Code"); + RunPartialParseTest(new TextChange(11, 0, old, 1, changed), + new MarkupBlock( + factory.EmptyHtml(), + new StatementBlock( + factory.CodeTransition(), + factory.MetaCode("Code").Accepts(AcceptedCharacters.None), + factory.Code(" ").AsStatement(), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("foo.b") + .AsImplicitExpression(VBCodeParser.DefaultKeywords, acceptTrailingDot: true) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Code(" ").AsStatement(), + factory.MetaCode("End Code").Accepts(AcceptedCharacters.None)), + factory.EmptyHtml())); + } + + [Fact] + public void ImplicitExpressionAcceptsDotIfTrailingDotsAreAllowed() + { + var factory = SpanFactory.CreateVbHtml(); + StringTextBuffer changed = new StringTextBuffer("@Code @foo. End Code"); + StringTextBuffer old = new StringTextBuffer("@Code @foo End Code"); + RunPartialParseTest(new TextChange(10, 0, old, 1, changed), + new MarkupBlock( + factory.EmptyHtml(), + new StatementBlock( + factory.CodeTransition(), + factory.MetaCode("Code").Accepts(AcceptedCharacters.None), + factory.Code(" ").AsStatement(), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("foo.") + .AsImplicitExpression(VBCodeParser.DefaultKeywords, acceptTrailingDot: true) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Code(" ").AsStatement(), + factory.MetaCode("End Code").Accepts(AcceptedCharacters.None)), + factory.EmptyHtml())); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfFunctionsKeywordTyped() + { + RunTypeKeywordTest("functions"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfCodeKeywordTyped() + { + RunTypeKeywordTest("code"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfSectionKeywordTyped() + { + RunTypeKeywordTest("section"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfDoKeywordTyped() + { + RunTypeKeywordTest("do"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfWhileKeywordTyped() + { + RunTypeKeywordTest("while"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfIfKeywordTyped() + { + RunTypeKeywordTest("if"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfSelectKeywordTyped() + { + RunTypeKeywordTest("select"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfForKeywordTyped() + { + RunTypeKeywordTest("for"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfTryKeywordTyped() + { + RunTypeKeywordTest("try"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfWithKeywordTyped() + { + RunTypeKeywordTest("with"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfSyncLockKeywordTyped() + { + RunTypeKeywordTest("synclock"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfUsingKeywordTyped() + { + RunTypeKeywordTest("using"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfImportsKeywordTyped() + { + RunTypeKeywordTest("imports"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfInheritsKeywordTyped() + { + RunTypeKeywordTest("inherits"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfOptionKeywordTyped() + { + RunTypeKeywordTest("option"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfHelperKeywordTyped() + { + RunTypeKeywordTest("helper"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfNamespaceKeywordTyped() + { + RunTypeKeywordTest("namespace"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfClassKeywordTyped() + { + RunTypeKeywordTest("class"); + } + + [Fact] + public void ImplicitExpressionCorrectlyTriggersReparseIfLayoutKeywordTyped() + { + RunTypeKeywordTest("layout"); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/RazorParserTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/RazorParserTest.cs new file mode 100644 index 0000000000..125e5ad479 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/RazorParserTest.cs @@ -0,0 +1,136 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.IO; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser +{ + public class RazorParserTest + { + [Fact] + public void ConstructorRequiresNonNullCodeParser() + { + Assert.ThrowsArgumentNull(() => new RazorParser(null, new HtmlMarkupParser()), "codeParser"); + } + + [Fact] + public void ConstructorRequiresNonNullMarkupParser() + { + Assert.ThrowsArgumentNull(() => new RazorParser(new CSharpCodeParser(), null), "markupParser"); + } + + [Fact] + public void ParseMethodCallsParseDocumentOnMarkupParserAndReturnsResults() + { + var factory = SpanFactory.CreateCsHtml(); + + // Arrange + RazorParser parser = new RazorParser(new CSharpCodeParser(), new HtmlMarkupParser()); + + // Act/Assert + ParserTestBase.EvaluateResults(parser.Parse(new StringReader("foo @bar baz")), + new MarkupBlock( + factory.Markup("foo "), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("bar") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Markup(" baz"))); + } + + [Fact] + public void ParseMethodUsesProvidedParserListenerIfSpecified() + { + var factory = SpanFactory.CreateCsHtml(); + + // Arrange + RazorParser parser = new RazorParser(new CSharpCodeParser(), new HtmlMarkupParser()); + + // Act + ParserResults results = parser.Parse(new StringReader("foo @bar baz")); + + // Assert + ParserTestBase.EvaluateResults(results, + new MarkupBlock( + factory.Markup("foo "), + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("bar") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + factory.Markup(" baz"))); + } + + [Fact] + public void ParseMethodSetsUpRunWithSpecifiedCodeParserMarkupParserAndListenerAndPassesToMarkupParser() + { + RunParseWithListenerTest((parser, reader) => parser.Parse(reader)); + } + + private static void RunParseWithListenerTest(Action parserAction) + { + // Arrange + ParserBase markupParser = new MockMarkupParser(); + ParserBase codeParser = new CSharpCodeParser(); + RazorParser parser = new RazorParser(codeParser, markupParser); + TextReader expectedReader = new StringReader("foo"); + + // Act + parserAction(parser, expectedReader); + + // Assert + ParserContext actualContext = markupParser.Context; + Assert.NotNull(actualContext); + Assert.Same(markupParser, actualContext.MarkupParser); + Assert.Same(markupParser, actualContext.ActiveParser); + Assert.Same(codeParser, actualContext.CodeParser); + } + + private class MockMarkupParser : ParserBase + { + public override bool IsMarkupParser + { + get + { + return true; + } + } + + public override void ParseDocument() + { + using (Context.StartBlock(BlockType.Markup)) + { + } + } + + public override void ParseSection(Tuple nestingSequences, bool caseSensitive = true) + { + using (Context.StartBlock(BlockType.Markup)) + { + } + } + + public override void ParseBlock() + { + using (Context.StartBlock(BlockType.Markup)) + { + } + } + + protected override ParserBase OtherParser + { + get { return Context.CodeParser; } + } + + public override void BuildSpan(SpanBuilder span, Razor.Text.SourceLocation start, string content) + { + throw new NotImplementedException(); + } + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBAutoCompleteTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBAutoCompleteTest.cs new file mode 100644 index 0000000000..d27b6b2255 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBAutoCompleteTest.cs @@ -0,0 +1,156 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.VB +{ + public class VBAutoCompleteTest : VBHtmlCodeParserTestBase + { + [Fact] + public void FunctionsDirective_AutoComplete_At_EOF() + { + ParseBlockTest("@Functions", + new FunctionsBlock( + Factory.CodeTransition("@") + .Accepts(AcceptedCharacters.None), + Factory.MetaCode("Functions") + .Accepts(AcceptedCharacters.None), + Factory.EmptyVB() + .AsFunctionsBody() + .With(new AutoCompleteEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString) + { + AutoCompleteString = SyntaxConstants.VB.EndFunctionsKeyword + })), + new RazorError( + String.Format(RazorResources.ParseError_BlockNotTerminated, "Functions", "End Functions"), + 1, 0, 1)); + } + + [Fact] + public void HelperDirective_AutoComplete_At_EOF() + { + ParseBlockTest("@Helper Strong(value As String)", + new HelperBlock(new HelperCodeGenerator(new LocationTagged("Strong(value As String)", 8, 0, 8), headerComplete: true), + Factory.CodeTransition(), + Factory.MetaCode("Helper ") + .Accepts(AcceptedCharacters.None), + Factory.Code("Strong(value As String)") + .Hidden() + .Accepts(AcceptedCharacters.None) + .With(new AutoCompleteEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString) { AutoCompleteString = SyntaxConstants.VB.EndHelperKeyword }), + new StatementBlock()), + new RazorError( + String.Format(RazorResources.ParseError_BlockNotTerminated, "Helper", "End Helper"), + 1, 0, 1)); + } + + [Fact] + public void SectionDirective_AutoComplete_At_EOF() + { + ParseBlockTest("@Section Header", + new SectionBlock(new SectionCodeGenerator("Header"), + Factory.CodeTransition(), + Factory.MetaCode("Section Header") + .With(new AutoCompleteEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString) { AutoCompleteString = SyntaxConstants.VB.EndSectionKeyword }), + new MarkupBlock()), + new RazorError( + String.Format(RazorResources.ParseError_BlockNotTerminated, "Section", "End Section"), + 1, 0, 1)); + } + + [Fact] + public void VerbatimBlock_AutoComplete_At_EOF() + { + ParseBlockTest("@Code", + new StatementBlock( + Factory.CodeTransition(), + Factory.MetaCode("Code").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, new VBSymbol(5, 0, 5, String.Empty, VBSymbolType.Unknown)) + .With(new StatementCodeGenerator()) + .With(new AutoCompleteEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString) { AutoCompleteString = SyntaxConstants.VB.EndCodeKeyword })), + new RazorError( + String.Format(RazorResources.ParseError_BlockNotTerminated, "Code", "End Code"), + 1, 0, 1)); + } + + [Fact] + public void FunctionsDirective_AutoComplete_At_StartOfFile() + { + ParseBlockTest("@Functions" + Environment.NewLine + + "foo", + new FunctionsBlock( + Factory.CodeTransition("@").Accepts(AcceptedCharacters.None), + Factory.MetaCode("Functions").Accepts(AcceptedCharacters.None), + Factory.Code("\r\nfoo") + .AsFunctionsBody() + .With(new AutoCompleteEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString) + { + AutoCompleteString = SyntaxConstants.VB.EndFunctionsKeyword + })), + new RazorError( + String.Format(RazorResources.ParseError_BlockNotTerminated, "Functions", "End Functions"), + 1, 0, 1)); + } + + [Fact] + public void HelperDirective_AutoComplete_At_StartOfFile() + { + ParseBlockTest("@Helper Strong(value As String)" + Environment.NewLine + + "Foo", + new HelperBlock(new HelperCodeGenerator(new LocationTagged("Strong(value As String)", 8, 0, 8), headerComplete: true), + Factory.CodeTransition(), + Factory.MetaCode("Helper ") + .Accepts(AcceptedCharacters.None), + Factory.Code("Strong(value As String)") + .Hidden() + .Accepts(AcceptedCharacters.None) + .With(new AutoCompleteEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString) { AutoCompleteString = SyntaxConstants.VB.EndHelperKeyword }), + new StatementBlock( + Factory.Code("\r\nFoo").AsStatement())), + new RazorError( + String.Format(RazorResources.ParseError_BlockNotTerminated, "Helper", "End Helper"), + 1, 0, 1)); + } + + [Fact] + public void SectionDirective_AutoComplete_At_StartOfFile() + { + ParseBlockTest("@Section Header" + Environment.NewLine + + "Foo", + new SectionBlock(new SectionCodeGenerator("Header"), + Factory.CodeTransition(), + Factory.MetaCode("Section Header") + .With(new AutoCompleteEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString) { AutoCompleteString = SyntaxConstants.VB.EndSectionKeyword }), + new MarkupBlock( + Factory.Markup("\r\nFoo") + .With(new MarkupCodeGenerator()))), + new RazorError( + String.Format(RazorResources.ParseError_BlockNotTerminated, "Section", "End Section"), + 1, 0, 1)); + } + + [Fact] + public void VerbatimBlock_AutoComplete_At_StartOfFile() + { + ParseBlockTest("@Code" + Environment.NewLine + + "Foo", + new StatementBlock( + Factory.CodeTransition(), + Factory.MetaCode("Code").Accepts(AcceptedCharacters.None), + Factory.Code("\r\nFoo") + .AsStatement() + .With(new AutoCompleteEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString) { AutoCompleteString = SyntaxConstants.VB.EndCodeKeyword })), + new RazorError( + String.Format(RazorResources.ParseError_BlockNotTerminated, "Code", "End Code"), + 1, 0, 1)); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBBlockTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBBlockTest.cs new file mode 100644 index 0000000000..c7b4601f41 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBBlockTest.cs @@ -0,0 +1,367 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.VB +{ + public class VBBlockTest : VBHtmlCodeParserTestBase + { + [Fact] + public void ParseBlockMethodThrowsArgNullExceptionOnNullContext() + { + // Arrange + VBCodeParser parser = new VBCodeParser(); + + // Act and Assert + Assert.Throws(() => parser.ParseBlock(), RazorResources.Parser_Context_Not_Set); + } + + [Fact] + public void ParseBlockAcceptsImplicitExpression() + { + ParseBlockTest("If True Then" + Environment.NewLine + + " @foo" + Environment.NewLine + + "End If", + new StatementBlock( + Factory.Code("If True Then\r\n ").AsStatement(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("foo") + .AsImplicitExpression(VBCodeParser.DefaultKeywords, acceptTrailingDot: true) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Code("\r\nEnd If") + .AsStatement() + .Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockAcceptsIfStatementWithinCodeBlockIfInDesignTimeMode() + { + ParseBlockTest("If True Then" + Environment.NewLine + + " @If True Then" + Environment.NewLine + + " End If" + Environment.NewLine + + "End If", + new StatementBlock( + Factory.Code("If True Then\r\n ").AsStatement(), + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("If True Then\r\n End If\r\n") + .AsStatement() + .Accepts(AcceptedCharacters.None)), + Factory.Code(@"End If") + .AsStatement() + .Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockSupportsSpacesInStrings() + { + ParseBlockTest("for each p in db.Query(\"SELECT * FROM PRODUCTS\")" + Environment.NewLine + + " @

    @p.Name

    " + Environment.NewLine + + "next", + new StatementBlock( + Factory.Code("for each p in db.Query(\"SELECT * FROM PRODUCTS\")\r\n") + .AsStatement(), + new MarkupBlock( + Factory.Markup(" "), + Factory.MarkupTransition(), + Factory.Markup("

    "), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("p.Name") + .AsImplicitExpression(VBCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Markup("

    \r\n").Accepts(AcceptedCharacters.None)), + Factory.Code("next") + .AsStatement() + .Accepts(AcceptedCharacters.WhiteSpace | AcceptedCharacters.NonWhiteSpace))); + } + + [Fact] + public void ParseBlockSupportsSimpleCodeBlock() + { + ParseBlockTest("Code" + Environment.NewLine + + " If foo IsNot Nothing" + Environment.NewLine + + " Bar(foo)" + Environment.NewLine + + " End If" + Environment.NewLine + + "End Code", + new StatementBlock( + Factory.MetaCode("Code").Accepts(AcceptedCharacters.None), + Factory.Code("\r\n If foo IsNot Nothing\r\n Bar(foo)\r\n End If\r\n") + .AsStatement(), + Factory.MetaCode("End Code").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockRejectsNewlineBetweenEndAndCodeIfNotPrefixedWithUnderscore() + { + ParseBlockTest("Code" + Environment.NewLine + + " If foo IsNot Nothing" + Environment.NewLine + + " Bar(foo)" + Environment.NewLine + + " End If" + Environment.NewLine + + "End" + Environment.NewLine + + "Code", + new StatementBlock( + Factory.MetaCode("Code").Accepts(AcceptedCharacters.None), + Factory.Code("\r\n If foo IsNot Nothing\r\n Bar(foo)\r\n End If\r\nEnd\r\nCode") + .AsStatement()), + new RazorError( + String.Format(RazorResources.ParseError_BlockNotTerminated, "Code", "End Code"), + SourceLocation.Zero)); + } + + [Fact] + public void ParseBlockAcceptsNewlineBetweenEndAndCodeIfPrefixedWithUnderscore() + { + ParseBlockTest("Code" + Environment.NewLine + + " If foo IsNot Nothing" + Environment.NewLine + + " Bar(foo)" + Environment.NewLine + + " End If" + Environment.NewLine + + "End _" + Environment.NewLine + + "_" + Environment.NewLine + + " _" + Environment.NewLine + + "Code", + new StatementBlock( + Factory.MetaCode("Code").Accepts(AcceptedCharacters.None), + Factory.Code("\r\n If foo IsNot Nothing\r\n Bar(foo)\r\n End If\r\n") + .AsStatement(), + Factory.MetaCode("End _\r\n_\r\n _\r\nCode").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockSupportsSimpleFunctionsBlock() + { + ParseBlockTest("Functions" + Environment.NewLine + + " Public Sub Foo()" + Environment.NewLine + + " Bar()" + Environment.NewLine + + " End Sub" + Environment.NewLine + + Environment.NewLine + + " Private Function Bar() As Object" + Environment.NewLine + + " Return Nothing" + Environment.NewLine + + " End Function" + Environment.NewLine + + "End Functions", + new FunctionsBlock( + Factory.MetaCode("Functions").Accepts(AcceptedCharacters.None), + Factory.Code("\r\n Public Sub Foo()\r\n Bar()\r\n End Sub\r\n\r\n Private Function Bar() As Object\r\n Return Nothing\r\n End Function\r\n") + .AsFunctionsBody(), + Factory.MetaCode("End Functions").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockRejectsNewlineBetweenEndAndFunctionsIfNotPrefixedWithUnderscore() + { + ParseBlockTest("Functions" + Environment.NewLine + + " If foo IsNot Nothing" + Environment.NewLine + + " Bar(foo)" + Environment.NewLine + + " End If" + Environment.NewLine + + "End" + Environment.NewLine + + "Functions", + new FunctionsBlock( + Factory.MetaCode("Functions").Accepts(AcceptedCharacters.None), + Factory.Code("\r\n If foo IsNot Nothing\r\n Bar(foo)\r\n End If\r\nEnd\r\nFunctions") + .AsFunctionsBody()), + new RazorError( + String.Format(RazorResources.ParseError_BlockNotTerminated, "Functions", "End Functions"), + SourceLocation.Zero)); + } + + [Fact] + public void ParseBlockAcceptsNewlineBetweenEndAndFunctionsIfPrefixedWithUnderscore() + { + ParseBlockTest("Functions" + Environment.NewLine + + " If foo IsNot Nothing" + Environment.NewLine + + " Bar(foo)" + Environment.NewLine + + " End If" + Environment.NewLine + + "End _" + Environment.NewLine + + "_" + Environment.NewLine + + " _" + Environment.NewLine + + "Functions", + new FunctionsBlock( + Factory.MetaCode("Functions").Accepts(AcceptedCharacters.None), + Factory.Code("\r\n If foo IsNot Nothing\r\n Bar(foo)\r\n End If\r\n") + .AsFunctionsBody(), + Factory.MetaCode("End _\r\n_\r\n _\r\nFunctions").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockCorrectlyHandlesExtraEndsInEndCode() + { + ParseBlockTest("Code" + Environment.NewLine + + " Bar End" + Environment.NewLine + + "End Code", + new StatementBlock( + Factory.MetaCode("Code").Accepts(AcceptedCharacters.None), + Factory.Code("\r\n Bar End\r\n").AsStatement(), + Factory.MetaCode("End Code").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockCorrectlyHandlesExtraEndsInEndFunctions() + { + ParseBlockTest("Functions" + Environment.NewLine + + " Bar End" + Environment.NewLine + + "End Functions", + new FunctionsBlock( + Factory.MetaCode("Functions").Accepts(AcceptedCharacters.None), + Factory.Code("\r\n Bar End\r\n").AsFunctionsBody().AutoCompleteWith(null, atEndOfSpan: false), + Factory.MetaCode("End Functions").Accepts(AcceptedCharacters.None))); + } + + [Theory] + [InlineData("If", "End", "If")] + [InlineData("Try", "End", "Try")] + [InlineData("While", "End", "While")] + [InlineData("Using", "End", "Using")] + [InlineData("With", "End", "With")] + public void KeywordAllowsNewlinesIfPrefixedByUnderscore(string startKeyword, string endKeyword1, string endKeyword2) + { + string code = startKeyword + Environment.NewLine + + " ' In the block" + Environment.NewLine + + endKeyword1 + " _" + Environment.NewLine + + "_" + Environment.NewLine + + "_" + Environment.NewLine + + "_" + Environment.NewLine + + "_" + Environment.NewLine + + "_" + Environment.NewLine + + " " + endKeyword2 + Environment.NewLine; + ParseBlockTest(code + "foo bar baz", + new StatementBlock( + Factory.Code(code) + .AsStatement() + .Accepts(AcceptedCharacters.None))); + } + + [Theory] + [InlineData("While", "EndWhile", "End While")] + [InlineData("If", "EndIf", "End If")] + [InlineData("Select", "EndSelect", "End Select")] + [InlineData("Try", "EndTry", "End Try")] + [InlineData("With", "EndWith", "End With")] + [InlineData("Using", "EndUsing", "End Using")] + public void EndTerminatedKeywordRequiresSpaceBetweenEndAndKeyword(string startKeyword, string wrongEndKeyword, string endKeyword) + { + string code = startKeyword + Environment.NewLine + + " ' This should not end the code" + Environment.NewLine + + " " + wrongEndKeyword + Environment.NewLine + + " ' But this should" + Environment.NewLine + + endKeyword; + ParseBlockTest(code, + new StatementBlock( + Factory.Code(code) + .AsStatement() + .Accepts(AcceptedCharacters.None))); + } + + [Theory] + [InlineData("While", "End While", false)] + [InlineData("Do", "Loop", true)] + [InlineData("If", "End If", false)] + [InlineData("Select", "End Select", false)] + [InlineData("For", "Next", true)] + [InlineData("Try", "End Try", false)] + [InlineData("With", "End With", false)] + [InlineData("Using", "End Using", false)] + public void EndSequenceInString(string keyword, string endSequence, bool acceptToEndOfLine) + { + string code = keyword + Environment.NewLine + + " \"" + endSequence + "\"" + Environment.NewLine + + endSequence + (acceptToEndOfLine ? " foo bar baz" : "") + Environment.NewLine; + ParseBlockTest(code + "biz boz", + new StatementBlock( + Factory.Code(code).AsStatement().Accepts(GetAcceptedCharacters(acceptToEndOfLine)))); + } + + [Theory] + [InlineData("While", "End While", false)] + [InlineData("Do", "Loop", true)] + [InlineData("If", "End If", false)] + [InlineData("Select", "End Select", false)] + [InlineData("For", "Next", true)] + [InlineData("Try", "End Try", false)] + [InlineData("With", "End With", false)] + [InlineData("Using", "End Using", false)] + private void CommentedEndSequence(string keyword, string endSequence, bool acceptToEndOfLine) + { + string code = keyword + Environment.NewLine + + " '" + endSequence + Environment.NewLine + + endSequence + (acceptToEndOfLine ? @" foo bar baz" : "") + Environment.NewLine; + ParseBlockTest(code + "biz boz", + new StatementBlock( + Factory.Code(code).AsStatement().Accepts(GetAcceptedCharacters(acceptToEndOfLine)))); + } + + [Theory] + [InlineData("While", "End While", false)] + [InlineData("Do", "Loop", true)] + [InlineData("If", "End If", false)] + [InlineData("Select", "End Select", false)] + [InlineData("For", "Next", true)] + [InlineData("Try", "End Try", false)] + [InlineData("With", "End With", false)] + [InlineData("SyncLock", "End SyncLock", false)] + [InlineData("Using", "End Using", false)] + private void NestedKeywordBlock(string keyword, string endSequence, bool acceptToEndOfLine) + { + string code = keyword + Environment.NewLine + + " " + keyword + Environment.NewLine + + " Bar(foo)" + Environment.NewLine + + " " + endSequence + Environment.NewLine + + endSequence + (acceptToEndOfLine ? " foo bar baz" : "") + Environment.NewLine; + ParseBlockTest(code + "biz boz", + new StatementBlock( + Factory.Code(code).AsStatement().Accepts(GetAcceptedCharacters(acceptToEndOfLine)))); + } + + [Theory] + [InlineData("While True", "End While", false)] + [InlineData("Do", "Loop", true)] + [InlineData("If foo IsNot Nothing", "End If", false)] + [InlineData("Select Case foo", "End Select", false)] + [InlineData("For Each p in Products", "Next", true)] + [InlineData("Try", "End Try", false)] + [InlineData("With", "End With", false)] + [InlineData("SyncLock", "End SyncLock", false)] + [InlineData("Using", "End Using", false)] + private void SimpleKeywordBlock(string keyword, string endSequence, bool acceptToEndOfLine) + { + string code = keyword + Environment.NewLine + + " Bar(foo)" + Environment.NewLine + + endSequence + (acceptToEndOfLine ? " foo bar baz" : "") + Environment.NewLine; + ParseBlockTest(code + "biz boz", + new StatementBlock( + Factory.Code(code).AsStatement().Accepts(GetAcceptedCharacters(acceptToEndOfLine)))); + } + + [Theory] + [InlineData("While True", "Exit While", "End While", false)] + [InlineData("Do", "Exit Do", "Loop", true)] + [InlineData("For Each p in Products", "Exit For", "Next", true)] + [InlineData("While True", "Continue While", "End While", false)] + [InlineData("Do", "Continue Do", "Loop", true)] + [InlineData("For Each p in Products", "Continue For", "Next", true)] + private void KeywordWithExitOrContinue(string startKeyword, string exitKeyword, string endKeyword, bool acceptToEndOfLine) + { + string code = startKeyword + Environment.NewLine + + " ' This is before the exit" + Environment.NewLine + + " " + exitKeyword + Environment.NewLine + + " ' This is after the exit" + Environment.NewLine + + endKeyword + Environment.NewLine; + ParseBlockTest(code + "foo bar baz", + new StatementBlock( + Factory.Code(code).AsStatement().Accepts(GetAcceptedCharacters(acceptToEndOfLine)))); + } + + private AcceptedCharacters GetAcceptedCharacters(bool acceptToEndOfLine) + { + return acceptToEndOfLine ? + AcceptedCharacters.WhiteSpace | AcceptedCharacters.NonWhiteSpace : + AcceptedCharacters.None; + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBContinueStatementTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBContinueStatementTest.cs new file mode 100644 index 0000000000..1f5fec480e --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBContinueStatementTest.cs @@ -0,0 +1,59 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.VB +{ + // VB Continue Statement: http://msdn.microsoft.com/en-us/library/801hyx6f.aspx + public class VBContinueStatementTest : VBHtmlCodeParserTestBase + { + [Fact] + public void VB_Do_Statement_With_Continue() + { + ParseBlockTest("@Do While True" + Environment.NewLine + + " Continue Do" + Environment.NewLine + + "Loop" + Environment.NewLine + + "' Not in the block!", + new StatementBlock( + Factory.CodeTransition(SyntaxConstants.TransitionString) + .Accepts(AcceptedCharacters.None), + Factory.Code("Do While True\r\n Continue Do\r\nLoop\r\n") + .AsStatement() + .Accepts(AcceptedCharacters.AnyExceptNewline))); + } + + [Fact] + public void VB_For_Statement_With_Continue() + { + ParseBlockTest("@For i = 1 To 12" + Environment.NewLine + + " Continue For" + Environment.NewLine + + "Next i" + Environment.NewLine + + "' Not in the block!", + new StatementBlock( + Factory.CodeTransition(SyntaxConstants.TransitionString) + .Accepts(AcceptedCharacters.None), + Factory.Code("For i = 1 To 12\r\n Continue For\r\nNext i\r\n") + .AsStatement() + .Accepts(AcceptedCharacters.AnyExceptNewline))); + } + + [Fact] + public void VB_While_Statement_With_Continue() + { + ParseBlockTest("@While True" + Environment.NewLine + + " Continue While" + Environment.NewLine + + "End While" + Environment.NewLine + + "' Not in the block!", + new StatementBlock( + Factory.CodeTransition(SyntaxConstants.TransitionString) + .Accepts(AcceptedCharacters.None), + Factory.Code("While True\r\n Continue While\r\nEnd While\r\n") + .AsStatement() + .Accepts(AcceptedCharacters.None))); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBDirectiveTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBDirectiveTest.cs new file mode 100644 index 0000000000..c5d8d126f4 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBDirectiveTest.cs @@ -0,0 +1,128 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.VB +{ + public class VBDirectiveTest : VBHtmlCodeParserTestBase + { + [Fact] + public void VB_Code_Directive() + { + ParseBlockTest("@Code" + Environment.NewLine + + " foo()" + Environment.NewLine + + "End Code" + Environment.NewLine + + "' Not part of the block", + new StatementBlock( + Factory.CodeTransition(SyntaxConstants.TransitionString) + .Accepts(AcceptedCharacters.None), + Factory.MetaCode("Code") + .Accepts(AcceptedCharacters.None), + Factory.Code("\r\n foo()\r\n") + .AsStatement() + .With(new AutoCompleteEditHandler(VBLanguageCharacteristics.Instance.TokenizeString)), + Factory.MetaCode("End Code") + .Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void VB_Functions_Directive() + { + ParseBlockTest("@Functions" + Environment.NewLine + + " Public Function Foo() As String" + Environment.NewLine + + " Return \"Foo\"" + Environment.NewLine + + " End Function" + Environment.NewLine + + Environment.NewLine + + " Public Sub Bar()" + Environment.NewLine + + " End Sub" + Environment.NewLine + + "End Functions" + Environment.NewLine + + "' Not part of the block", + new FunctionsBlock( + Factory.CodeTransition(SyntaxConstants.TransitionString) + .Accepts(AcceptedCharacters.None), + Factory.MetaCode("Functions") + .Accepts(AcceptedCharacters.None), + Factory.Code("\r\n Public Function Foo() As String\r\n Return \"Foo\"\r\n End Function\r\n\r\n Public Sub Bar()\r\n End Sub\r\n") + .AsFunctionsBody(), + Factory.MetaCode("End Functions") + .Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void VB_Section_Directive() + { + ParseBlockTest("@Section Header" + Environment.NewLine + + "

    Foo

    " + Environment.NewLine + + "End Section", + new SectionBlock(new SectionCodeGenerator("Header"), + Factory.CodeTransition(SyntaxConstants.TransitionString), + Factory.MetaCode("Section Header"), + new MarkupBlock( + Factory.Markup("\r\n

    Foo

    \r\n")), + Factory.MetaCode("End Section") + .Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void SessionStateDirectiveWorks() + { + ParseBlockTest("@SessionState InProc" + Environment.NewLine, + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode("SessionState ") + .Accepts(AcceptedCharacters.None), + Factory.MetaCode("InProc\r\n") + .Accepts(AcceptedCharacters.None) + .With(new RazorDirectiveAttributeCodeGenerator("SessionState", "InProc")) + ) + ); + } + + [Fact] + public void SessionStateDirectiveIsCaseInsensitive() + { + ParseBlockTest("@sessionstate disabled" + Environment.NewLine, + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode("sessionstate ") + .Accepts(AcceptedCharacters.None), + Factory.MetaCode("disabled\r\n") + .Accepts(AcceptedCharacters.None) + .With(new RazorDirectiveAttributeCodeGenerator("SessionState", "disabled")) + ) + ); + } + + [Fact] + public void VB_Helper_Directive() + { + ParseBlockTest("@Helper Strong(s as String)" + Environment.NewLine + + " s = s.ToUpperCase()" + Environment.NewLine + + " @s" + Environment.NewLine + + "End Helper", + new HelperBlock(new HelperCodeGenerator(new LocationTagged("Strong(s as String)", 8, 0, 8), headerComplete: true), + Factory.CodeTransition(SyntaxConstants.TransitionString), + Factory.MetaCode("Helper ") + .Accepts(AcceptedCharacters.None), + Factory.Code("Strong(s as String)").Hidden(), + new StatementBlock( + Factory.Code("\r\n s = s.ToUpperCase()\r\n") + .AsStatement(), + new MarkupBlock( + Factory.Markup(" "), + Factory.MarkupTransition(SyntaxConstants.TransitionString), + Factory.Markup("s\r\n") + .Accepts(AcceptedCharacters.None)), + Factory.EmptyVB() + .AsStatement(), + Factory.MetaCode("End Helper") + .Accepts(AcceptedCharacters.None)))); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBErrorTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBErrorTest.cs new file mode 100644 index 0000000000..2fb00c9676 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBErrorTest.cs @@ -0,0 +1,174 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Editor; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.VB +{ + public class VBErrorTest : VBHtmlCodeParserTestBase + { + [Fact] + public void ParserOutputsErrorAndRecoversToEndOfLineIfExplicitExpressionUnterminated() + { + ParseBlockTest(@"(foo +bar", + new ExpressionBlock( + Factory.MetaCode("(").Accepts(AcceptedCharacters.None), + Factory.Code("foo").AsExpression()), + new RazorError( + String.Format( + RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, + RazorResources.BlockName_ExplicitExpression, + ")", "("), + SourceLocation.Zero)); + } + + [Fact] + public void ParserOutputsZeroLengthCodeSpanIfEofReachedAfterStartOfExplicitExpression() + { + ParseBlockTest("(", + new ExpressionBlock( + Factory.MetaCode("(").Accepts(AcceptedCharacters.None), + Factory.EmptyVB().AsExpression()), + new RazorError( + String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, "explicit expression", ")", "("), + SourceLocation.Zero)); + } + + [Fact] + public void ParserOutputsZeroLengthCodeSpanIfEofReachedAfterAtSign() + { + ParseBlockTest(String.Empty, + new ExpressionBlock( + Factory.EmptyVB().AsImplicitExpression(KeywordSet).Accepts(AcceptedCharacters.NonWhiteSpace)), + new RazorError( + RazorResources.ParseError_Unexpected_EndOfFile_At_Start_Of_CodeBlock, + SourceLocation.Zero)); + } + + [Fact] + public void ParserOutputsZeroLengthCodeSpanIfOnlyWhitespaceFoundAfterAtSign() + { + ParseBlockTest(" ", + new ExpressionBlock( + Factory.EmptyVB().AsImplicitExpression(KeywordSet).Accepts(AcceptedCharacters.NonWhiteSpace)), + new RazorError( + RazorResources.ParseError_Unexpected_WhiteSpace_At_Start_Of_CodeBlock_VB, + SourceLocation.Zero)); + } + + [Fact] + public void ParserOutputsZeroLengthCodeSpanIfInvalidCharacterFoundAfterAtSign() + { + ParseBlockTest("!!!", + new ExpressionBlock( + Factory.EmptyVB().AsImplicitExpression(KeywordSet).Accepts(AcceptedCharacters.NonWhiteSpace)), + new RazorError( + String.Format(RazorResources.ParseError_Unexpected_Character_At_Start_Of_CodeBlock_VB, "!"), + SourceLocation.Zero)); + } + + [Theory] + [InlineData("Code", "End Code", true, true)] + [InlineData("Do", "Loop", false, false)] + [InlineData("While", "End While", false, false)] + [InlineData("If", "End If", false, false)] + [InlineData("Select Case", "End Select", false, false)] + [InlineData("For", "Next", false, false)] + [InlineData("Try", "End Try", false, false)] + [InlineData("With", "End With", false, false)] + [InlineData("Using", "End Using", false, false)] + public void EofBlock(string keyword, string expectedTerminator, bool autoComplete, bool keywordIsMetaCode) + { + EofBlockCore(keyword, expectedTerminator, autoComplete, BlockType.Statement, keywordIsMetaCode, c => c.AsStatement()); + } + + [Fact] + public void EofFunctionsBlock() + { + EofBlockCore("Functions", "End Functions", true, BlockType.Functions, true, c => c.AsFunctionsBody()); + } + + private void EofBlockCore(string keyword, string expectedTerminator, bool autoComplete, BlockType blockType, bool keywordIsMetaCode, Func classifier) + { + BlockBuilder expected = new BlockBuilder(); + expected.Type = blockType; + if (keywordIsMetaCode) + { + expected.Children.Add(Factory.MetaCode(keyword).Accepts(AcceptedCharacters.None)); + expected.Children.Add( + classifier(Factory.EmptyVB()) + .With((SpanEditHandler)( + autoComplete ? + new AutoCompleteEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString) { AutoCompleteString = expectedTerminator } : + SpanEditHandler.CreateDefault()))); + } + else + { + expected.Children.Add( + classifier(Factory.Code(keyword)) + .With((SpanEditHandler)( + autoComplete ? + new AutoCompleteEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString) { AutoCompleteString = expectedTerminator } : + SpanEditHandler.CreateDefault()))); + } + + ParseBlockTest(keyword, + expected.Build(), + new RazorError( + String.Format(RazorResources.ParseError_BlockNotTerminated, keyword, expectedTerminator), + SourceLocation.Zero)); + } + + [Theory] + [InlineData("Code", "End Code", true)] + [InlineData("Do", "Loop", false)] + [InlineData("While", "End While", false)] + [InlineData("If", "End If", false)] + [InlineData("Select Case", "End Select", false)] + [InlineData("For", "Next", false)] + [InlineData("Try", "End Try", false)] + [InlineData("With", "End With", false)] + [InlineData("Using", "End Using", false)] + public void UnterminatedBlock(string keyword, string expectedTerminator, bool keywordIsMetaCode) + { + UnterminatedBlockCore(keyword, expectedTerminator, BlockType.Statement, keywordIsMetaCode, c => c.AsStatement()); + } + + [Fact] + public void UnterminatedFunctionsBlock() + { + UnterminatedBlockCore("Functions", "End Functions", BlockType.Functions, true, c => c.AsFunctionsBody()); + } + + private void UnterminatedBlockCore(string keyword, string expectedTerminator, BlockType blockType, bool keywordIsMetaCode, Func classifier) + { + const string blockBody = @" + ' This block is not correctly terminated!"; + + BlockBuilder expected = new BlockBuilder(); + expected.Type = blockType; + if (keywordIsMetaCode) + { + expected.Children.Add(Factory.MetaCode(keyword).Accepts(AcceptedCharacters.None)); + expected.Children.Add(classifier(Factory.Code(blockBody))); + } + else + { + expected.Children.Add(classifier(Factory.Code(keyword + blockBody))); + } + + ParseBlockTest(keyword + blockBody, + expected.Build(), + new RazorError( + String.Format(RazorResources.ParseError_BlockNotTerminated, keyword, expectedTerminator), + SourceLocation.Zero)); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBExitStatementTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBExitStatementTest.cs new file mode 100644 index 0000000000..9eda18768d --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBExitStatementTest.cs @@ -0,0 +1,97 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.VB +{ + // VB Exit Statement: http://msdn.microsoft.com/en-us/library/t2at9t47.aspx + public class VBExitStatementTest : VBHtmlCodeParserTestBase + { + [Fact] + public void VB_Do_Statement_With_Exit() + { + ParseBlockTest("@Do While True" + Environment.NewLine + + " Exit Do" + Environment.NewLine + + "Loop" + Environment.NewLine + + "' Not in the block!", + new StatementBlock( + Factory.CodeTransition(SyntaxConstants.TransitionString) + .Accepts(AcceptedCharacters.None), + Factory.Code("Do While True\r\n Exit Do\r\nLoop\r\n") + .AsStatement() + .Accepts(AcceptedCharacters.AnyExceptNewline))); + } + + [Fact] + public void VB_For_Statement_With_Exit() + { + ParseBlockTest("@For i = 1 To 12" + Environment.NewLine + + " Exit For" + Environment.NewLine + + "Next i" + Environment.NewLine + + "' Not in the block!", + new StatementBlock( + Factory.CodeTransition(SyntaxConstants.TransitionString) + .Accepts(AcceptedCharacters.None), + Factory.Code("For i = 1 To 12\r\n Exit For\r\nNext i\r\n") + .AsStatement() + .Accepts(AcceptedCharacters.AnyExceptNewline))); + } + + [Fact] + public void VB_Select_Statement_With_Exit() + { + ParseBlockTest("@Select Case Foo" + Environment.NewLine + + " Case 1" + Environment.NewLine + + " Exit Select" + Environment.NewLine + + " Case 2" + Environment.NewLine + + " Exit Select" + Environment.NewLine + + "End Select" + Environment.NewLine + + "' Not in the block!", + new StatementBlock( + Factory.CodeTransition(SyntaxConstants.TransitionString) + .Accepts(AcceptedCharacters.None), + Factory.Code("Select Case Foo\r\n Case 1\r\n Exit Select\r\n Case 2\r\n Exit Select\r\nEnd Select\r\n") + .AsStatement() + .Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void VB_Try_Statement_With_Exit() + { + ParseBlockTest("@Try" + Environment.NewLine + + " Foo()" + Environment.NewLine + + " Exit Try" + Environment.NewLine + + "Catch Bar" + Environment.NewLine + + " Throw Bar" + Environment.NewLine + + "Finally" + Environment.NewLine + + " Baz()" + Environment.NewLine + + "End Try" + Environment.NewLine + + "' Not in the block!", + new StatementBlock( + Factory.CodeTransition(SyntaxConstants.TransitionString) + .Accepts(AcceptedCharacters.None), + Factory.Code("Try\r\n Foo()\r\n Exit Try\r\nCatch Bar\r\n Throw Bar\r\nFinally\r\n Baz()\r\nEnd Try\r\n") + .AsStatement() + .Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void VB_While_Statement_With_Exit() + { + ParseBlockTest("@While True" + Environment.NewLine + + " Exit While" + Environment.NewLine + + "End While" + Environment.NewLine + + "' Not in the block!", + new StatementBlock( + Factory.CodeTransition(SyntaxConstants.TransitionString) + .Accepts(AcceptedCharacters.None), + Factory.Code("While True\r\n Exit While\r\nEnd While\r\n") + .AsStatement() + .Accepts(AcceptedCharacters.None))); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBExplicitExpressionTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBExplicitExpressionTest.cs new file mode 100644 index 0000000000..d7f0fe4424 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBExplicitExpressionTest.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.VB +{ + public class VBExplicitExpressionTest : VBHtmlCodeParserTestBase + { + [Fact] + public void VB_Simple_ExplicitExpression() + { + ParseBlockTest("@(foo)", + new ExpressionBlock( + Factory.CodeTransition(), + Factory.MetaCode("(").Accepts(AcceptedCharacters.None), + Factory.Code("foo").AsExpression(), + Factory.MetaCode(")").Accepts(AcceptedCharacters.None))); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBExpressionTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBExpressionTest.cs new file mode 100644 index 0000000000..07af887998 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBExpressionTest.cs @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.VB +{ + public class VBExpressionTest : VBHtmlCodeParserTestBase + { + [Fact] + public void ParseBlockCorrectlyHandlesCodeBlockInBodyOfExplicitExpressionDueToUnclosedExpression() + { + ParseBlockTest(@"( +@Code + Dim foo = bar +End Code", + new ExpressionBlock( + Factory.MetaCode("(").Accepts(AcceptedCharacters.None), + Factory.EmptyVB().AsExpression()), + new RazorError( + String.Format( + RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, + RazorResources.BlockName_ExplicitExpression, + ")", "("), + SourceLocation.Zero)); + } + + [Fact] + public void ParseBlockAcceptsNonEnglishCharactersThatAreValidIdentifiers() + { + ImplicitExpressionTest("हळूँजद॔.", "हळूँजद॔"); + } + + [Fact] + public void ParseBlockDoesNotTreatXmlAxisPropertyAsTransitionToMarkup() + { + SingleSpanBlockTest( + @"If foo Is Nothing Then + Dim bar As XElement + Dim foo = bar. +End If", + BlockType.Statement, + SpanKind.Code, + acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockDoesNotTreatXmlAttributePropertyAsTransitionToMarkup() + { + SingleSpanBlockTest( + @"If foo Is Nothing Then + Dim bar As XElement + Dim foo = bar.@foo +End If", + BlockType.Statement, + SpanKind.Code, + acceptedCharacters: AcceptedCharacters.None); + } + + [Fact] + public void ParseBlockSupportsSimpleImplicitExpression() + { + ImplicitExpressionTest("Foo"); + } + + [Fact] + public void ParseBlockSupportsImplicitExpressionWithDots() + { + ImplicitExpressionTest("Foo.Bar.Baz"); + } + + [Fact] + public void ParseBlockSupportsImplicitExpressionWithParens() + { + ImplicitExpressionTest("Foo().Bar().Baz()"); + } + + [Fact] + public void ParseBlockSupportsImplicitExpressionWithStuffInParens() + { + ImplicitExpressionTest("Foo().Bar(sdfkhj sdfksdfjs \")\" sjdfkjsdf).Baz()"); + } + + [Fact] + public void ParseBlockSupportsImplicitExpressionWithCommentInParens() + { + ImplicitExpressionTest("Foo().Bar(sdfkhj sdfksdfjs \")\" '))))))))\r\nsjdfkjsdf).Baz()"); + } + + [Theory] + [InlineData("Foo")] + [InlineData("Foo(Of String).Bar(1, 2, 3).Biz")] + [InlineData("Foo(Of String).Bar(\")\").Biz")] + [InlineData("Foo(Of String).Bar(\"Foo\"\"Bar)\"\"Baz\").Biz")] + [InlineData("\"foo\r\nbar")] + [InlineData("Foo.Bar. _\r\nREM )\r\nBaz()\r\n")] + [InlineData("Foo.Bar. _\r\n' )\r\nBaz()\r\n")] + public void ValidExplicitExpressions(string body) + { + ParseBlockTest("(" + body + ")", + new ExpressionBlock( + Factory.MetaCode("(").Accepts(AcceptedCharacters.None), + Factory.Code(body).AsExpression(), + Factory.MetaCode(")").Accepts(AcceptedCharacters.None))); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBExpressionsInCodeTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBExpressionsInCodeTest.cs new file mode 100644 index 0000000000..d6a00d579e --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBExpressionsInCodeTest.cs @@ -0,0 +1,121 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.VB +{ + public class VBExpressionsInCodeTest : VBHtmlCodeParserTestBase + { + [Fact] + public void InnerImplicitExpressionWithOnlySingleAtAcceptsSingleSpaceOrNewlineAtDesignTime() + { + ParseBlockTest("Code" + Environment.NewLine + + " @" + Environment.NewLine + + "End Code", + new StatementBlock( + Factory.MetaCode("Code").Accepts(AcceptedCharacters.None), + Factory.Code("\r\n ").AsStatement(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.EmptyVB() + .AsImplicitExpression(VBCodeParser.DefaultKeywords, acceptTrailingDot: true) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Code("\r\n").AsStatement(), + Factory.MetaCode("End Code").Accepts(AcceptedCharacters.None)), + designTimeParser: true, + expectedErrors: new[] + { + new RazorError(RazorResources.ParseError_Unexpected_WhiteSpace_At_Start_Of_CodeBlock_VB, 11, 1, 5) + }); + } + + [Fact] + public void InnerImplicitExpressionDoesNotAcceptDotAfterAt() + { + ParseBlockTest("Code" + Environment.NewLine + + " @." + Environment.NewLine + + "End Code", + new StatementBlock( + Factory.MetaCode("Code").Accepts(AcceptedCharacters.None), + Factory.Code("\r\n ").AsStatement(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.EmptyVB() + .AsImplicitExpression(VBCodeParser.DefaultKeywords, acceptTrailingDot: true) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Code(".\r\n").AsStatement(), + Factory.MetaCode("End Code").Accepts(AcceptedCharacters.None)), + designTimeParser: true, + expectedErrors: new[] + { + new RazorError( + String.Format(RazorResources.ParseError_Unexpected_Character_At_Start_Of_CodeBlock_VB, "."), + 11, 1, 5) + }); + } + + [Theory] + [InlineData("Foo.Bar.", true)] + [InlineData("Foo", true)] + [InlineData("Foo.Bar.Baz", true)] + [InlineData("Foo().Bar().Baz()", true)] + [InlineData("Foo().Bar(sdfkhj sdfksdfjs \")\" sjdfkjsdf).Baz()", true)] + [InlineData("Foo().Bar(sdfkhj sdfksdfjs \")\" '))))))))\r\nsjdfkjsdf).Baz()", true)] + [InlineData("Foo", false)] + [InlineData("Foo(Of String).Bar(1, 2, 3).Biz", false)] + [InlineData("Foo(Of String).Bar(\")\").Biz", false)] + [InlineData("Foo(Of String).Bar(\"Foo\"\"Bar)\"\"Baz\").Biz", false)] + [InlineData("Foo.Bar. _\r\nREM )\r\nBaz()\r\n", false)] + [InlineData("Foo.Bar. _\r\n' )\r\nBaz()\r\n", false)] + public void ExpressionInCode(string expression, bool isImplicit) + { + ExpressionBlock expressionBlock; + if (isImplicit) + { + expressionBlock = + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code(expression) + .AsImplicitExpression(VBCodeParser.DefaultKeywords, acceptTrailingDot: true) + .Accepts(AcceptedCharacters.NonWhiteSpace)); + } + else + { + expressionBlock = + new ExpressionBlock( + Factory.CodeTransition(), + Factory.MetaCode("(").Accepts(AcceptedCharacters.None), + Factory.Code(expression).AsExpression(), + Factory.MetaCode(")").Accepts(AcceptedCharacters.None)); + } + + string code; + if (isImplicit) + { + code = "If foo IsNot Nothing Then" + Environment.NewLine + + " @" + expression + Environment.NewLine + + "End If"; + } + else + { + code = "If foo IsNot Nothing Then" + Environment.NewLine + + " @(" + expression + ")" + Environment.NewLine + + "End If"; + } + + ParseBlockTest(code, + new StatementBlock( + Factory.Code("If foo IsNot Nothing Then\r\n ") + .AsStatement(), + expressionBlock, + Factory.Code("\r\nEnd If") + .AsStatement() + .Accepts(AcceptedCharacters.None))); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBHelperTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBHelperTest.cs new file mode 100644 index 0000000000..fe5bdbeba3 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBHelperTest.cs @@ -0,0 +1,324 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Text; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.VB +{ + public class VBHelperTest : VBHtmlMarkupParserTestBase + { + [Fact] + public void ParseHelperOutputsErrorButContinuesIfLParenFoundAfterHelperKeyword() + { + ParseDocumentTest("@Helper ()", + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock(new HelperCodeGenerator(new LocationTagged("()", 8, 0, 8), headerComplete: true), + Factory.CodeTransition(), + Factory.MetaCode("Helper ").Accepts(AcceptedCharacters.None), + Factory.Code("()").Hidden().AutoCompleteWith(SyntaxConstants.VB.EndHelperKeyword), + new StatementBlock())), + new RazorError( + String.Format( + RazorResources.ParseError_Unexpected_Character_At_Helper_Name_Start, + String.Format(RazorResources.ErrorComponent_Character, "(")), + 8, 0, 8), + new RazorError( + String.Format(RazorResources.ParseError_BlockNotTerminated, "Helper", "End Helper"), + 1, 0, 1)); + } + + [Fact] + public void ParseHelperStatementOutputsMarkerHelperHeaderSpanOnceKeywordComplete() + { + ParseDocumentTest("@Helper ", + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock(new HelperCodeGenerator(new LocationTagged(String.Empty, 8, 0, 8), headerComplete: false), + Factory.CodeTransition(), + Factory.MetaCode("Helper ").Accepts(AcceptedCharacters.None), + Factory.EmptyVB().Hidden())), + new RazorError( + String.Format(RazorResources.ParseError_Unexpected_Character_At_Helper_Name_Start, RazorResources.ErrorComponent_EndOfFile), + 8, 0, 8)); + } + + [Fact] + public void ParseHelperStatementMarksHelperSpanAsCanGrowIfMissingTrailingSpace() + { + ParseDocumentTest("@Helper", + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock( + Factory.CodeTransition(), + Factory.MetaCode("Helper"))), + new RazorError( + String.Format(RazorResources.ParseError_Unexpected_Character_At_Helper_Name_Start, RazorResources.ErrorComponent_EndOfFile), + 7, 0, 7)); + } + + [Fact] + public void ParseHelperStatementTerminatesEarlyIfHeaderNotComplete() + { + ParseDocumentTest("@Helper" + Environment.NewLine + + "@Helper", + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock( + Factory.CodeTransition(), + Factory.MetaCode("Helper\r\n").Accepts(AcceptedCharacters.None), + Factory.EmptyVB().Hidden()), + new HelperBlock( + Factory.CodeTransition(), + Factory.MetaCode("Helper"))), + designTimeParser: true, + expectedErrors: new[] + { + new RazorError( + String.Format( + RazorResources.ParseError_Unexpected_Character_At_Helper_Name_Start, + String.Format(RazorResources.ErrorComponent_Character, "@")), + 9, 1, 0), + new RazorError( + String.Format( + RazorResources.ParseError_Unexpected_Character_At_Helper_Name_Start, + RazorResources.ErrorComponent_EndOfFile), + 16, 1, 7) + }); + } + + [Fact] + public void ParseHelperStatementTerminatesEarlyIfHeaderNotCompleteWithSpace() + { + ParseDocumentTest(@"@Helper @Helper", + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock(new HelperCodeGenerator(new LocationTagged(String.Empty, 8, 0, 8), headerComplete: false), + Factory.CodeTransition(), + Factory.MetaCode(@"Helper ").Accepts(AcceptedCharacters.None), + Factory.EmptyVB().Hidden()), + new HelperBlock( + Factory.CodeTransition(), + Factory.MetaCode("Helper").Accepts(AcceptedCharacters.Any))), + designTimeParser: true, + expectedErrors: new[] + { + new RazorError( + String.Format( + RazorResources.ParseError_Unexpected_Character_At_Helper_Name_Start, + String.Format(RazorResources.ErrorComponent_Character, "@")), + 8, 0, 8), + new RazorError( + String.Format( + RazorResources.ParseError_Unexpected_Character_At_Helper_Name_Start, + RazorResources.ErrorComponent_EndOfFile), + 15, 0, 15) + }); + } + + [Fact] + public void ParseHelperStatementAllowsDifferentlyCasedEndHelperKeyword() + { + ParseDocumentTest("@Helper Foo()" + Environment.NewLine + + "end helper", + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock(new HelperCodeGenerator(new LocationTagged("Foo()", 8, 0, 8), headerComplete: true), + Factory.CodeTransition(), + Factory.MetaCode("Helper ").Accepts(AcceptedCharacters.None), + Factory.Code("Foo()").Hidden(), + new StatementBlock( + Factory.Code("\r\n").AsStatement(), + Factory.MetaCode("end helper").Accepts(AcceptedCharacters.None))), + Factory.EmptyHtml())); + } + + [Fact] + public void ParseHelperStatementCapturesWhitespaceToEndOfLineIfHelperStatementMissingName() + { + ParseDocumentTest("@Helper " + Environment.NewLine + + " ", + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock(new HelperCodeGenerator(new LocationTagged(" ", 8, 0, 8), headerComplete: false), + Factory.CodeTransition(), + Factory.MetaCode("Helper ").Accepts(AcceptedCharacters.None), + Factory.Code(" ").Hidden()), + Factory.Markup("\r\n ")), + new RazorError( + String.Format( + RazorResources.ParseError_Unexpected_Character_At_Helper_Name_Start, + RazorResources.ErrorComponent_Newline), + 30, 0, 30)); + } + + [Fact] + public void ParseHelperStatementCapturesWhitespaceToEndOfLineIfHelperStatementMissingOpenParen() + { + ParseDocumentTest("@Helper Foo " + Environment.NewLine + + " ", + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock(new HelperCodeGenerator(new LocationTagged("Foo ", 8, 0, 8), headerComplete: false), + Factory.CodeTransition(), + Factory.MetaCode("Helper ").Accepts(AcceptedCharacters.None), + Factory.Code("Foo ").Hidden()), + Factory.Markup("\r\n ")), + new RazorError( + String.Format(RazorResources.ParseError_MissingCharAfterHelperName, "("), + 15, 0, 15)); + } + + [Fact] + public void ParseHelperStatementCapturesAllContentToEndOfFileIfHelperStatementMissingCloseParenInParameterList() + { + ParseDocumentTest("@Helper Foo(Foo Bar" + Environment.NewLine + + "Biz" + Environment.NewLine + + "Boz", + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock(new HelperCodeGenerator(new LocationTagged("Foo(Foo Bar\r\nBiz\r\nBoz", 8, 0, 8), headerComplete: false), + Factory.CodeTransition(), + Factory.MetaCode("Helper ").Accepts(AcceptedCharacters.None), + Factory.Code("Foo(Foo Bar\r\nBiz\r\nBoz").Hidden())), + new RazorError(RazorResources.ParseError_UnterminatedHelperParameterList, 11, 0, 11)); + } + + [Fact] + public void ParseHelperStatementCapturesWhitespaceToEndOfLineIfHelperStatementMissingOpenBraceAfterParameterList() + { + ParseDocumentTest("@Helper Foo(foo as String) " + Environment.NewLine, + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock(new HelperCodeGenerator(new LocationTagged("Foo(foo as String)", 8, 0, 8), headerComplete: true), + Factory.CodeTransition(), + Factory.MetaCode("Helper ").Accepts(AcceptedCharacters.None), + Factory.Code("Foo(foo as String)") + .Hidden() + .AutoCompleteWith(SyntaxConstants.VB.EndHelperKeyword), + new StatementBlock( + Factory.Code(" \r\n").AsStatement()))), + new RazorError( + String.Format(RazorResources.ParseError_BlockNotTerminated, "Helper", "End Helper"), + 1, 0, 1)); + } + + [Fact] + public void ParseHelperStatementContinuesParsingHelperUntilEOF() + { + ParseDocumentTest("@Helper Foo(foo as String)" + Environment.NewLine + + " @

    Foo

    ", + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock(new HelperCodeGenerator(new LocationTagged("Foo(foo as String)", 8, 0, 8), headerComplete: true), + Factory.CodeTransition(), + Factory.MetaCode("Helper ").Accepts(AcceptedCharacters.None), + Factory.Code("Foo(foo as String)") + .Hidden() + .AutoCompleteWith(SyntaxConstants.VB.EndHelperKeyword), + new StatementBlock( + Factory.Code("\r\n").AsStatement(), + new MarkupBlock( + Factory.Markup(" "), + Factory.MarkupTransition(), + Factory.Markup("

    Foo

    ").Accepts(AcceptedCharacters.None)), + Factory.EmptyVB().AsStatement()))), + new RazorError( + String.Format(RazorResources.ParseError_BlockNotTerminated, "Helper", "End Helper"), + 1, 0, 1)); + } + + [Fact] + public void ParseHelperStatementCorrectlyParsesHelperWithEmbeddedCode() + { + ParseDocumentTest("@Helper Foo(foo as String, bar as String)" + Environment.NewLine + + " @

    @foo

    " + Environment.NewLine + + "End Helper", + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock(new HelperCodeGenerator(new LocationTagged("Foo(foo as String, bar as String)", 8, 0, 8), headerComplete: true), + Factory.CodeTransition(), + Factory.MetaCode("Helper ").Accepts(AcceptedCharacters.None), + Factory.Code("Foo(foo as String, bar as String)").Hidden(), + new StatementBlock( + Factory.Code("\r\n").AsStatement(), + new MarkupBlock( + Factory.Markup(" "), + Factory.MarkupTransition(), + Factory.Markup("

    "), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("foo") + .AsImplicitExpression(VBCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Markup("

    \r\n").Accepts(AcceptedCharacters.None)), + Factory.EmptyVB().AsStatement(), + Factory.MetaCode("End Helper").Accepts(AcceptedCharacters.None))), + Factory.EmptyHtml())); + } + + [Fact] + public void ParseHelperStatementGivesWhitespaceAfterCloseParenToMarkup() + { + ParseDocumentTest("@Helper Foo(string foo) " + Environment.NewLine + + " ", + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock(new HelperCodeGenerator(new LocationTagged("Foo(string foo)", 8, 0, 8), headerComplete: true), + Factory.CodeTransition(), + Factory.MetaCode("Helper ").Accepts(AcceptedCharacters.None), + Factory.Code("Foo(string foo)") + .Hidden() + .AutoCompleteWith(SyntaxConstants.VB.EndHelperKeyword), + new StatementBlock( + Factory.Code(" \r\n ").AsStatement()))), + designTimeParser: true, + expectedErrors: + new RazorError( + String.Format( + RazorResources.ParseError_BlockNotTerminated, + "Helper", "End Helper"), + 1, 0, 1)); + } + + [Fact] + public void ParseHelperAcceptsNestedHelpersButOutputsError() + { + ParseDocumentTest("@Helper Foo(string foo)" + Environment.NewLine + + " @Helper Bar(string baz)" + Environment.NewLine + + " End Helper" + Environment.NewLine + + "End Helper", + new MarkupBlock( + Factory.EmptyHtml(), + new HelperBlock(new HelperCodeGenerator(new LocationTagged("Foo(string foo)", 8, 0, 8), headerComplete: true), + Factory.CodeTransition(), + Factory.MetaCode("Helper ").Accepts(AcceptedCharacters.None), + Factory.Code("Foo(string foo)").Hidden(), + new StatementBlock( + Factory.Code("\r\n ").AsStatement(), + new HelperBlock(new HelperCodeGenerator(new LocationTagged("Bar(string baz)", 37, 1, 12), headerComplete: true), + Factory.CodeTransition(), + Factory.MetaCode("Helper ").Accepts(AcceptedCharacters.None), + Factory.Code("Bar(string baz)").Hidden(), + new StatementBlock( + Factory.Code("\r\n ").AsStatement(), + Factory.MetaCode("End Helper").Accepts(AcceptedCharacters.None))), + Factory.Code("\r\n").AsStatement(), + Factory.MetaCode("End Helper").Accepts(AcceptedCharacters.None))), + Factory.EmptyHtml()), + designTimeParser: true, + expectedErrors: new[] + { + new RazorError( + RazorResources.ParseError_Helpers_Cannot_Be_Nested, + 30, 1, 5) + }); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBHtmlDocumentTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBHtmlDocumentTest.cs new file mode 100644 index 0000000000..89e0be59fa --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBHtmlDocumentTest.cs @@ -0,0 +1,251 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.VB +{ + public class VBHtmlDocumentTest : VBHtmlMarkupParserTestBase + { + [Fact] + public void BlockCommentInMarkupDocumentIsHandledCorrectly() + { + ParseDocumentTest(@"
      " + Environment.NewLine + + @" @* This is a block comment
    *@ foo", + new MarkupBlock( + Factory.Markup("
      " + Environment.NewLine + " "), + new CommentBlock( + Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition), + Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.Comment(" This is a block comment
    ", HtmlSymbolType.RazorComment), + Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition)), + Factory.Markup(" foo"))); + } + + [Fact] + public void BlockCommentInMarkupBlockIsHandledCorrectly() + { + ParseBlockTest(@"
      " + Environment.NewLine + + @" @* This is a block comment
    *@ foo ", + new MarkupBlock( + Factory.Markup("
      " + Environment.NewLine + " "), + new CommentBlock( + Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition), + Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.Comment(" This is a block comment
    ", HtmlSymbolType.RazorComment), + Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition)), + Factory.Markup(" foo ").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void BlockCommentAtStatementStartInCodeBlockIsHandledCorrectly() + { + ParseDocumentTest(@"@If Request.IsAuthenticated Then" + Environment.NewLine + + @" @* User is logged in! End If *@" + Environment.NewLine + + @" Write(""Hello friend!"")" + Environment.NewLine + + @"End If", + new MarkupBlock( + Factory.EmptyHtml(), + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("If Request.IsAuthenticated Then" + Environment.NewLine + " ").AsStatement(), + new CommentBlock( + Factory.CodeTransition(VBSymbolType.RazorCommentTransition), + Factory.MetaCode("*", VBSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.Comment(" User is logged in! End If ", VBSymbolType.RazorComment), + Factory.MetaCode("*", VBSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.CodeTransition(VBSymbolType.RazorCommentTransition)), + Factory.Code("" + Environment.NewLine + " Write(\"Hello friend!\")" + Environment.NewLine + "End If") + .AsStatement() + .Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void BlockCommentInStatementInCodeBlockIsHandledCorrectly() + { + ParseDocumentTest(@"@If Request.IsAuthenticated Then" + Environment.NewLine + + @" Dim foo = @* User is logged in! End If *@ bar" + Environment.NewLine + + @" Write(""Hello friend!"")" + Environment.NewLine + + @"End If", + new MarkupBlock( + Factory.EmptyHtml(), + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("If Request.IsAuthenticated Then" + Environment.NewLine + " Dim foo = ").AsStatement(), + new CommentBlock( + Factory.CodeTransition(VBSymbolType.RazorCommentTransition), + Factory.MetaCode("*", VBSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.Comment(" User is logged in! End If ", VBSymbolType.RazorComment), + Factory.MetaCode("*", VBSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.CodeTransition(VBSymbolType.RazorCommentTransition)), + Factory.Code(" bar" + Environment.NewLine + " Write(\"Hello friend!\")" + Environment.NewLine + "End If") + .AsStatement() + .Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void BlockCommentInStringInCodeBlockIsIgnored() + { + ParseDocumentTest(@"@If Request.IsAuthenticated Then" + Environment.NewLine + + @" Dim foo = ""@* User is logged in! End If *@ bar""" + Environment.NewLine + + @" Write(""Hello friend!"")" + Environment.NewLine + + @"End If", + new MarkupBlock( + Factory.EmptyHtml(), + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("If Request.IsAuthenticated Then" + Environment.NewLine + " Dim foo = \"@* User is logged in! End If *@ bar\"" + Environment.NewLine + " Write(\"Hello friend!\")" + Environment.NewLine + "End If") + .AsStatement() + .Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void BlockCommentInTickCommentInCodeBlockIsIgnored() + { + ParseDocumentTest(@"@If Request.IsAuthenticated Then" + Environment.NewLine + + @" Dim foo = '@* User is logged in! End If *@ bar" + Environment.NewLine + + @" Write(""Hello friend!"")" + Environment.NewLine + + @"End If", + new MarkupBlock( + Factory.EmptyHtml(), + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("If Request.IsAuthenticated Then" + Environment.NewLine + " Dim foo = '@* User is logged in! End If *@ bar" + Environment.NewLine + " Write(\"Hello friend!\")" + Environment.NewLine + "End If") + .AsStatement() + .Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void BlockCommentInRemCommentInCodeBlockIsIgnored() + { + ParseDocumentTest(@"@If Request.IsAuthenticated Then" + Environment.NewLine + + @" Dim foo = REM @* User is logged in! End If *@ bar" + Environment.NewLine + + @" Write(""Hello friend!"")" + Environment.NewLine + + @"End If", + new MarkupBlock( + Factory.EmptyHtml(), + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("If Request.IsAuthenticated Then" + Environment.NewLine + " Dim foo = REM @* User is logged in! End If *@ bar" + Environment.NewLine + " Write(\"Hello friend!\")" + Environment.NewLine + "End If") + .AsStatement() + .Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void BlockCommentInImplicitExpressionIsHandledCorrectly() + { + ParseDocumentTest("@Html.Foo@*bar*@", + new MarkupBlock( + Factory.EmptyHtml(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("Html.Foo") + .AsImplicitExpression(KeywordSet) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.EmptyHtml(), + new CommentBlock( + Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition), + Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.Comment("bar", HtmlSymbolType.RazorComment), + Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition)), + Factory.EmptyHtml())); + } + + [Fact] + public void BlockCommentAfterDotOfImplicitExpressionIsHandledCorrectly() + { + ParseDocumentTest("@Html.@*bar*@", + new MarkupBlock( + Factory.EmptyHtml(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("Html") + .AsImplicitExpression(KeywordSet) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Markup("."), + new CommentBlock( + Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition), + Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.Comment("bar", HtmlSymbolType.RazorComment), + Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition)), + Factory.EmptyHtml())); + } + + [Fact] + public void BlockCommentInParensOfImplicitExpressionIsHandledCorrectly() + { + ParseDocumentTest("@Html.Foo(@*bar*@ 4)", + new MarkupBlock( + Factory.EmptyHtml(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("Html.Foo(") + .AsImplicitExpression(KeywordSet) + .Accepts(AcceptedCharacters.Any), + new CommentBlock( + Factory.CodeTransition(VBSymbolType.RazorCommentTransition), + Factory.MetaCode("*", VBSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.Comment("bar", VBSymbolType.RazorComment), + Factory.MetaCode("*", VBSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.CodeTransition(VBSymbolType.RazorCommentTransition)), + Factory.Code(" 4)") + .AsImplicitExpression(KeywordSet) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.EmptyHtml())); + } + + [Fact] + public void BlockCommentInConditionIsHandledCorrectly() + { + ParseDocumentTest("@If @*bar*@ Then End If", + new MarkupBlock( + Factory.EmptyHtml(), + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("If ").AsStatement(), + new CommentBlock( + Factory.CodeTransition(VBSymbolType.RazorCommentTransition), + Factory.MetaCode("*", VBSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.Comment("bar", VBSymbolType.RazorComment), + Factory.MetaCode("*", VBSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.CodeTransition(VBSymbolType.RazorCommentTransition)), + Factory.Code(" Then End If").AsStatement().Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void BlockCommentInExplicitExpressionIsHandledCorrectly() + { + ParseDocumentTest(@"@(1 + @*bar*@ 1)", + new MarkupBlock( + Factory.EmptyHtml(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.MetaCode("(").Accepts(AcceptedCharacters.None), + Factory.Code(@"1 + ").AsExpression(), + new CommentBlock( + Factory.CodeTransition(VBSymbolType.RazorCommentTransition), + Factory.MetaCode("*", VBSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.Comment("bar", VBSymbolType.RazorComment), + Factory.MetaCode("*", VBSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None), + Factory.CodeTransition(VBSymbolType.RazorCommentTransition) + ), + Factory.Code(" 1").AsExpression(), + Factory.MetaCode(")").Accepts(AcceptedCharacters.None) + ), + Factory.EmptyHtml())); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBImplicitExpressionTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBImplicitExpressionTest.cs new file mode 100644 index 0000000000..3b54261273 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBImplicitExpressionTest.cs @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.VB +{ + public class VBImplicitExpressionTest : VBHtmlCodeParserTestBase + { + [Fact] + public void VB_Simple_ImplicitExpression() + { + ParseBlockTest("@foo not-part-of-the-block", + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("foo") + .AsImplicitExpression(VBCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace))); + } + + [Fact] + public void VB_ImplicitExpression_With_Keyword_At_Start() + { + ParseBlockTest("@Partial", + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("Partial") + .AsImplicitExpression(VBCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace))); + } + + [Fact] + public void VB_ImplicitExpression_With_Keyword_In_Body() + { + ParseBlockTest("@Html.Partial", + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("Html.Partial") + .AsImplicitExpression(VBCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace))); + } + + [Fact] + public void VB_ImplicitExpression_With_MethodCallOrArrayIndex() + { + ParseBlockTest("@foo(42) not-part-of-the-block", + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("foo(42)") + .AsImplicitExpression(VBCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace))); + } + + [Fact] + public void VB_ImplicitExpression_Terminates_If_Trailing_Dot_Not_Followed_By_Valid_Token() + { + ParseBlockTest("@foo(42). ", + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("foo(42)") + .AsImplicitExpression(VBCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace))); + } + + [Fact] + public void VB_ImplicitExpression_Supports_Complex_Expressions() + { + ParseBlockTest("@foo(42).bar(Biz.Boz / 42 * 8)(1).Burf not part of the block", + new ExpressionBlock( + Factory.CodeTransition() + .Accepts(AcceptedCharacters.None), + Factory.Code("foo(42).bar(Biz.Boz / 42 * 8)(1).Burf") + .AsImplicitExpression(VBCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace))); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBLayoutDirectiveTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBLayoutDirectiveTest.cs new file mode 100644 index 0000000000..59df5dd9c8 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBLayoutDirectiveTest.cs @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Editor; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.VB +{ + public class VBLayoutDirectiveTest : VBHtmlCodeParserTestBase + { + [Theory] + [InlineData("layout")] + [InlineData("Layout")] + [InlineData("LAYOUT")] + [InlineData("layOut")] + [InlineData("LayOut")] + [InlineData("LaYoUt")] + [InlineData("lAyOuT")] + public void LayoutDirectiveSupportsAnyCasingOfKeyword(string keyword) + { + ParseBlockTest("@" + keyword, + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode(keyword) + ) + ); + } + + [Fact] + public void LayoutDirectiveAcceptsAllTextToEndOfLine() + { + ParseBlockTest("@Layout Foo Bar Baz", + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode("Layout ").Accepts(AcceptedCharacters.None), + Factory.MetaCode("Foo Bar Baz") + .With(new SetLayoutCodeGenerator("Foo Bar Baz")) + .WithEditorHints(EditorHints.VirtualPath | EditorHints.LayoutPage) + ) + ); + } + + [Fact] + public void LayoutDirectiveAcceptsAnyIfNoWhitespaceFollowingLayoutKeyword() + { + ParseBlockTest("@Layout", + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode("Layout") + ) + ); + } + + [Fact] + public void LayoutDirectiveOutputsMarkerSpanIfAnyWhitespaceAfterLayoutKeyword() + { + ParseBlockTest("@Layout ", + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode("Layout ").Accepts(AcceptedCharacters.None), + Factory.EmptyVB() + .AsMetaCode() + .With(new SetLayoutCodeGenerator(String.Empty)) + .WithEditorHints(EditorHints.VirtualPath | EditorHints.LayoutPage) + ) + ); + } + + [Fact] + public void LayoutDirectiveAcceptsTrailingNewlineButDoesNotIncludeItInLayoutPath() + { + ParseBlockTest("@Layout Foo" + Environment.NewLine, + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode("Layout ").Accepts(AcceptedCharacters.None), + Factory.MetaCode("Foo\r\n") + .With(new SetLayoutCodeGenerator("Foo")) + .Accepts(AcceptedCharacters.None) + .WithEditorHints(EditorHints.VirtualPath | EditorHints.LayoutPage) + ) + ); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBNestedStatementsTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBNestedStatementsTest.cs new file mode 100644 index 0000000000..b78b7d1111 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBNestedStatementsTest.cs @@ -0,0 +1,170 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.VB +{ + public class VBNestedStatementsTest : VBHtmlCodeParserTestBase + { + [Fact] + public void VB_Nested_If_Statement() + { + ParseBlockTest("@If True Then" + Environment.NewLine + + " If False Then" + Environment.NewLine + + " End If" + Environment.NewLine + + "End If", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("If True Then\r\n If False Then\r\n End If\r\nEnd If") + .AsStatement() + .Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void VB_Nested_Do_Statement() + { + ParseBlockTest("@Do While True" + Environment.NewLine + + " Do" + Environment.NewLine + + " Loop Until False" + Environment.NewLine + + "Loop", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("Do While True\r\n Do\r\n Loop Until False\r\nLoop") + .AsStatement() + .Accepts(AcceptedCharacters.AnyExceptNewline))); + } + + [Fact] + public void VB_Nested_Markup_Statement_In_If() + { + ParseBlockTest("@If True Then" + Environment.NewLine + + " @

    Tag

    " + Environment.NewLine + + "End If", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("If True Then\r\n") + .AsStatement(), + new MarkupBlock( + Factory.Markup(" "), + Factory.MarkupTransition(), + Factory.Markup("

    Tag

    \r\n") + .Accepts(AcceptedCharacters.None)), + Factory.Code("End If") + .AsStatement() + .Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void VB_Nested_Markup_Statement_In_Code() + { + ParseBlockTest("@Code" + Environment.NewLine + + " Foo()" + Environment.NewLine + + " @

    Tag

    " + Environment.NewLine + + " Bar()" + Environment.NewLine + + "End Code", + new StatementBlock( + Factory.CodeTransition(), + Factory.MetaCode("Code") + .Accepts(AcceptedCharacters.None), + Factory.Code("\r\n Foo()\r\n") + .AsStatement(), + new MarkupBlock( + Factory.Markup(" "), + Factory.MarkupTransition(), + Factory.Markup("

    Tag

    \r\n") + .Accepts(AcceptedCharacters.None)), + Factory.Code(" Bar()\r\n") + .AsStatement(), + Factory.MetaCode("End Code") + .Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void VB_Nested_Markup_Statement_In_Do() + { + ParseBlockTest("@Do" + Environment.NewLine + + " @

    Tag

    " + Environment.NewLine + + "Loop While True", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("Do\r\n") + .AsStatement(), + new MarkupBlock( + Factory.Markup(" "), + Factory.MarkupTransition(), + Factory.Markup("

    Tag

    \r\n") + .Accepts(AcceptedCharacters.None)), + Factory.Code("Loop While True") + .AsStatement() + .Accepts(AcceptedCharacters.AnyExceptNewline))); + } + + [Fact] + public void VB_Nested_Single_Line_Markup_Statement_In_Do() + { + ParseBlockTest("@Do" + Environment.NewLine + + " @:

    Tag" + Environment.NewLine + + "Loop While True", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("Do\r\n") + .AsStatement(), + new MarkupBlock( + Factory.Markup(" "), + Factory.MarkupTransition(), + Factory.MetaMarkup(":", HtmlSymbolType.Colon), + Factory.Markup("

    Tag\r\n") + .Accepts(AcceptedCharacters.None)), + Factory.Code("Loop While True") + .AsStatement() + .Accepts(AcceptedCharacters.AnyExceptNewline))); + } + + [Fact] + public void VB_Nested_Implicit_Expression_In_If() + { + ParseBlockTest("@If True Then" + Environment.NewLine + + " @Foo.Bar" + Environment.NewLine + + "End If", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("If True Then\r\n ") + .AsStatement(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("Foo.Bar") + .AsExpression() + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Code("\r\nEnd If") + .AsStatement() + .Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void VB_Nested_Explicit_Expression_In_If() + { + ParseBlockTest("@If True Then" + Environment.NewLine + + " @(Foo.Bar + 42)" + Environment.NewLine + + "End If", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("If True Then\r\n ") + .AsStatement(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.MetaCode("(") + .Accepts(AcceptedCharacters.None), + Factory.Code("Foo.Bar + 42") + .AsExpression(), + Factory.MetaCode(")") + .Accepts(AcceptedCharacters.None)), + Factory.Code("\r\nEnd If") + .AsStatement() + .Accepts(AcceptedCharacters.None))); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBRazorCommentsTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBRazorCommentsTest.cs new file mode 100644 index 0000000000..b11d725ebd --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBRazorCommentsTest.cs @@ -0,0 +1,175 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.VB +{ + public class VBRazorCommentsTest : VBHtmlMarkupParserTestBase + { + [Fact] + public void UnterminatedRazorComment() + { + ParseDocumentTest("@*", + new MarkupBlock( + Factory.EmptyHtml(), + new CommentBlock( + Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition) + .Accepts(AcceptedCharacters.None), + Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar) + .Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Comment, new HtmlSymbol( + Factory.LocationTracker.CurrentLocation, + String.Empty, + HtmlSymbolType.Unknown)) + .Accepts(AcceptedCharacters.Any))), + new RazorError(RazorResources.ParseError_RazorComment_Not_Terminated, 0, 0, 0)); + } + + [Fact] + public void EmptyRazorComment() + { + ParseDocumentTest("@**@", + new MarkupBlock( + Factory.EmptyHtml(), + new CommentBlock( + Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition) + .Accepts(AcceptedCharacters.None), + Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar) + .Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Comment, new HtmlSymbol( + Factory.LocationTracker.CurrentLocation, + String.Empty, + HtmlSymbolType.Unknown)) + .Accepts(AcceptedCharacters.Any), + Factory.MetaMarkup("*", HtmlSymbolType.RazorCommentStar) + .Accepts(AcceptedCharacters.None), + Factory.MarkupTransition(HtmlSymbolType.RazorCommentTransition) + .Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void RazorCommentInImplicitExpressionMethodCall() + { + ParseDocumentTest(@"@foo(@**@", + new MarkupBlock( + Factory.EmptyHtml(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("foo(") + .AsImplicitExpression(VBCodeParser.DefaultKeywords), + new CommentBlock( + Factory.CodeTransition(VBSymbolType.RazorCommentTransition) + .Accepts(AcceptedCharacters.None), + Factory.MetaCode("*", VBSymbolType.RazorCommentStar) + .Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Comment, new VBSymbol( + Factory.LocationTracker.CurrentLocation, + String.Empty, + VBSymbolType.Unknown)) + .Accepts(AcceptedCharacters.Any), + Factory.MetaCode("*", VBSymbolType.RazorCommentStar) + .Accepts(AcceptedCharacters.None), + Factory.CodeTransition(VBSymbolType.RazorCommentTransition) + .Accepts(AcceptedCharacters.None)), + Factory.EmptyVB() + .AsImplicitExpression(VBCodeParser.DefaultKeywords))), + new RazorError( + String.Format(RazorResources.ParseError_Expected_CloseBracket_Before_EOF, "(", ")"), + 4, 0, 4)); + } + + [Fact] + public void UnterminatedRazorCommentInImplicitExpressionMethodCall() + { + ParseDocumentTest("@foo(@*", + new MarkupBlock( + Factory.EmptyHtml(), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("foo(") + .AsImplicitExpression(VBCodeParser.DefaultKeywords), + new CommentBlock( + Factory.CodeTransition(VBSymbolType.RazorCommentTransition) + .Accepts(AcceptedCharacters.None), + Factory.MetaCode("*", VBSymbolType.RazorCommentStar) + .Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Comment, new VBSymbol( + Factory.LocationTracker.CurrentLocation, + String.Empty, + VBSymbolType.Unknown)) + .Accepts(AcceptedCharacters.Any)))), + new RazorError(RazorResources.ParseError_RazorComment_Not_Terminated, 5, 0, 5), + new RazorError(String.Format(RazorResources.ParseError_Expected_CloseBracket_Before_EOF, "(", ")"), 4, 0, 4)); + } + + [Fact] + public void RazorCommentInVerbatimBlock() + { + ParseDocumentTest("@Code" + Environment.NewLine + + " @Body

    " + Environment.NewLine + + "End Section", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator(String.Empty), + Factory.CodeTransition(), + Factory.MetaCode("Section "), + new MarkupBlock( + Factory.Markup("\r\nFoo\r\n

    Body

    \r\n")), + Factory.MetaCode("End Section").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml()), + new RazorError( + String.Format( + RazorResources.ParseError_Unexpected_Character_At_Section_Name_Start, + RazorResources.ErrorComponent_Newline), + 9, 0, 9)); + } + + [Fact] + public void ParseSectionAllowsNameToBeOnDifferentLineAsSectionKeywordIfUnderscoresUsed() + { + ParseDocumentTest("@Section _" + Environment.NewLine + + "_" + Environment.NewLine + + "Foo" + Environment.NewLine + + "

    Body

    " + Environment.NewLine + + "End Section", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("Foo"), + Factory.CodeTransition(), + Factory.MetaCode("Section _\r\n_\r\nFoo"), + new MarkupBlock( + Factory.Markup("\r\n

    Body

    \r\n")), + Factory.MetaCode("End Section").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void ParseSectionReportsErrorAndTerminatesSectionBlockIfKeywordNotFollowedByIdentifierStartCharacter() + { + ParseDocumentTest("@Section 9" + Environment.NewLine + + "

    Foo

    " + Environment.NewLine + + "End Section", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator(String.Empty), + Factory.CodeTransition(), + Factory.MetaCode("Section "), + new MarkupBlock( + Factory.Markup("9\r\n

    Foo

    \r\n")), + Factory.MetaCode("End Section").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml()), + new RazorError( + String.Format( + RazorResources.ParseError_Unexpected_Character_At_Section_Name_Start, + String.Format(RazorResources.ErrorComponent_Character, "9")), + 9, 0, 9)); + } + + [Fact] + public void ParserOutputsErrorOnNestedSections() + { + ParseDocumentTest("@Section foo" + Environment.NewLine + + " @Section bar" + Environment.NewLine + + "

    Foo

    " + Environment.NewLine + + " End Section" + Environment.NewLine + + "End Section", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("foo"), + Factory.CodeTransition(), + Factory.MetaCode("Section foo"), + new MarkupBlock( + Factory.Markup("\r\n"), + new SectionBlock(new SectionCodeGenerator("bar"), + Factory.Code(" ").AsStatement(), + Factory.CodeTransition(), + Factory.MetaCode("Section bar"), + new MarkupBlock( + Factory.Markup("\r\n

    Foo

    \r\n ")), + Factory.MetaCode("End Section").Accepts(AcceptedCharacters.None)), + Factory.Markup("\r\n")), + Factory.MetaCode("End Section").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml()), + new RazorError( + String.Format( + RazorResources.ParseError_Sections_Cannot_Be_Nested, + RazorResources.SectionExample_VB), + 26, 1, 12)); + } + + [Fact] + public void ParseSectionHandlesEOFAfterIdentifier() + { + ParseDocumentTest("@Section foo", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("foo"), + Factory.CodeTransition(), + Factory.MetaCode("Section foo") + .AutoCompleteWith(SyntaxConstants.VB.EndSectionKeyword), + new MarkupBlock())), + new RazorError( + String.Format( + RazorResources.ParseError_BlockNotTerminated, + "Section", "End Section"), + 1, 0, 1)); + } + + [Fact] + public void ParseSectionHandlesUnterminatedSection() + { + ParseDocumentTest("@Section foo" + Environment.NewLine + + "

    Foo

    ", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("foo"), + Factory.CodeTransition(), + Factory.MetaCode("Section foo") + .AutoCompleteWith(SyntaxConstants.VB.EndSectionKeyword), + new MarkupBlock( + Factory.Markup("\r\n

    Foo

    ")))), + new RazorError( + String.Format( + RazorResources.ParseError_BlockNotTerminated, + "Section", "End Section"), + 1, 0, 1)); + } + + [Fact] + public void ParseDocumentParsesNamedSectionCorrectly() + { + ParseDocumentTest("@Section foo" + Environment.NewLine + + "

    Foo

    " + Environment.NewLine + + "End Section", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("foo"), + Factory.CodeTransition(), + Factory.MetaCode("Section foo"), + new MarkupBlock( + Factory.Markup("\r\n

    Foo

    \r\n")), + Factory.MetaCode("End Section").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void ParseSectionTerminatesOnFirstEndSection() + { + ParseDocumentTest("@Section foo" + Environment.NewLine + + "

    End Section

    ", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("foo"), + Factory.CodeTransition(), + Factory.MetaCode("Section foo"), + new MarkupBlock( + Factory.Markup("\r\n

    ")), + Factory.MetaCode("End Section").Accepts(AcceptedCharacters.None)), + Factory.Markup("

    "))); + } + + [Fact] + public void ParseSectionAllowsEndSectionInVBExpression() + { + ParseDocumentTest("@Section foo" + Environment.NewLine + + " I really want to render the word @(\"End Section\"), so this is how I do it" + Environment.NewLine + + "End Section", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("foo"), + Factory.CodeTransition(), + Factory.MetaCode("Section foo"), + new MarkupBlock( + Factory.Markup("\r\n I really want to render the word "), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.MetaCode("(").Accepts(AcceptedCharacters.None), + Factory.Code("\"End Section\"").AsExpression(), + Factory.MetaCode(")").Accepts(AcceptedCharacters.None)), + Factory.Markup(", so this is how I do it\r\n")), + Factory.MetaCode("End Section").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + // These are tests that are normally in HtmlToCodeSwitchTest, but we want to verify them for VB + // since VB has slightly different section terminating behavior which follow slightly different + // code paths + + [Fact] + public void SectionBodyTreatsTwoAtSignsAsEscapeSequence() + { + ParseDocumentTest("@Section Foo" + Environment.NewLine + + " @@bar" + Environment.NewLine + + "End Section", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("Foo"), + Factory.CodeTransition(), + Factory.MetaCode("Section Foo").AutoCompleteWith(null), + new MarkupBlock( + Factory.Markup("\r\n "), + Factory.Markup("@").Hidden(), + Factory.Markup("@bar\r\n")), + Factory.MetaCode("End Section").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + + [Fact] + public void SectionBodyTreatsPairsOfAtSignsAsEscapeSequence() + { + ParseDocumentTest("@Section Foo" + Environment.NewLine + + " @@@@@bar" + Environment.NewLine + + "End Section", + new MarkupBlock( + Factory.EmptyHtml(), + new SectionBlock(new SectionCodeGenerator("Foo"), + Factory.CodeTransition(), + Factory.MetaCode("Section Foo").AutoCompleteWith(null), + new MarkupBlock( + Factory.Markup("\r\n "), + Factory.Markup("@").Hidden(), + Factory.Markup("@"), + Factory.Markup("@").Hidden(), + Factory.Markup("@"), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("bar") + .AsImplicitExpression(VBCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Markup("\r\n")), + Factory.MetaCode("End Section").Accepts(AcceptedCharacters.None)), + Factory.EmptyHtml())); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBSpecialKeywordsTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBSpecialKeywordsTest.cs new file mode 100644 index 0000000000..ad86c04bf6 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBSpecialKeywordsTest.cs @@ -0,0 +1,172 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.VB +{ + public class VBSpecialKeywordsTest : VBHtmlCodeParserTestBase + { + [Fact] + public void ParseInheritsStatementMarksInheritsSpanAsCanGrowIfMissingTrailingSpace() + { + ParseBlockTest("inherits", + new DirectiveBlock( + Factory.MetaCode("inherits")), + new RazorError( + RazorResources.ParseError_InheritsKeyword_Must_Be_Followed_By_TypeName, + 8, 0, 8)); + } + + [Fact] + public void InheritsBlockAcceptsMultipleGenericArguments() + { + ParseBlockTest("inherits Foo.Bar(Of Biz(Of Qux), String, Integer).Baz", + new DirectiveBlock( + Factory.MetaCode("inherits ").Accepts(AcceptedCharacters.None), + Factory.Code("Foo.Bar(Of Biz(Of Qux), String, Integer).Baz") + .AsBaseType("Foo.Bar(Of Biz(Of Qux), String, Integer).Baz"))); + } + + [Fact] + public void InheritsDirectiveSupportsVSTemplateTokens() + { + ParseBlockTest("@Inherits $rootnamespace$.MyBase", + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode("Inherits ") + .Accepts(AcceptedCharacters.None), + Factory.Code("$rootnamespace$.MyBase") + .AsBaseType("$rootnamespace$.MyBase"))); + } + + [Fact] + public void InheritsBlockOutputsErrorIfInheritsNotFollowedByTypeButAcceptsEntireLineAsCode() + { + ParseBlockTest("inherits " + Environment.NewLine + + "foo", + new DirectiveBlock( + Factory.MetaCode("inherits ").Accepts(AcceptedCharacters.None), + Factory.Code("\r\n").AsBaseType(String.Empty)), + new RazorError( + RazorResources.ParseError_InheritsKeyword_Must_Be_Followed_By_TypeName, + 8, 0, 8)); + } + + [Fact] + public void ParseBlockShouldSupportNamespaceImports() + { + ParseBlockTest("Imports Foo.Bar.Baz.Biz.Boz", + new DirectiveBlock( + Factory.MetaCode("Imports Foo.Bar.Baz.Biz.Boz") + .With(new AddImportCodeGenerator( + ns: " Foo.Bar.Baz.Biz.Boz", + namespaceKeywordLength: SyntaxConstants.VB.ImportsKeywordLength)))); + } + + [Fact] + public void ParseBlockShowsErrorIfNamespaceNotOnSameLineAsImportsKeyword() + { + ParseBlockTest("Imports" + Environment.NewLine + + "Foo", + new DirectiveBlock( + Factory.MetaCode("Imports\r\n") + .With(new AddImportCodeGenerator( + ns: "\r\n", + namespaceKeywordLength: SyntaxConstants.VB.ImportsKeywordLength))), + new RazorError( + RazorResources.ParseError_NamespaceOrTypeAliasExpected, + 7, 0, 7)); + } + + [Fact] + public void ParseBlockShowsErrorIfTypeBeingAliasedNotOnSameLineAsImportsKeyword() + { + ParseBlockTest("Imports Foo =" + Environment.NewLine + + "System.Bar", + new DirectiveBlock( + Factory.MetaCode("Imports Foo =\r\n") + .With(new AddImportCodeGenerator( + ns: " Foo =\r\n", + namespaceKeywordLength: SyntaxConstants.VB.ImportsKeywordLength)))); + } + + [Fact] + public void ParseBlockShouldSupportTypeAliases() + { + ParseBlockTest("Imports Foo = Bar.Baz.Biz.Boz", + new DirectiveBlock( + Factory.MetaCode("Imports Foo = Bar.Baz.Biz.Boz") + .With(new AddImportCodeGenerator( + ns: " Foo = Bar.Baz.Biz.Boz", + namespaceKeywordLength: SyntaxConstants.VB.ImportsKeywordLength)))); + } + + [Fact] + public void ParseBlockThrowsErrorIfOptionIsNotFollowedByStrictOrExplicit() + { + ParseBlockTest("Option FizzBuzz On", + new DirectiveBlock( + Factory.MetaCode("Option FizzBuzz On") + .With(new SetVBOptionCodeGenerator(optionName: null, value: true))), + new RazorError( + String.Format(RazorResources.ParseError_UnknownOption, "FizzBuzz"), + 7, 0, 7)); + } + + [Fact] + public void ParseBlockThrowsErrorIfOptionStrictIsNotFollowedByOnOrOff() + { + ParseBlockTest("Option Strict Yes", + new DirectiveBlock( + Factory.MetaCode("Option Strict Yes") + .With(SetVBOptionCodeGenerator.Strict(true))), + new RazorError( + String.Format( + RazorResources.ParseError_InvalidOptionValue, + "Strict", "Yes"), + 14, 0, 14)); + } + + [Fact] + public void ParseBlockReadsToAfterOnKeywordIfOptionStrictBlock() + { + ParseBlockTest("Option Strict On Foo Bar Baz", + new DirectiveBlock( + Factory.MetaCode("Option Strict On") + .With(SetVBOptionCodeGenerator.Strict(true)))); + } + + [Fact] + public void ParseBlockReadsToAfterOffKeywordIfOptionStrictBlock() + { + ParseBlockTest("Option Strict Off Foo Bar Baz", + new DirectiveBlock( + Factory.MetaCode("Option Strict Off") + .With(SetVBOptionCodeGenerator.Strict(false)))); + } + + [Fact] + public void ParseBlockReadsToAfterOnKeywordIfOptionExplicitBlock() + { + ParseBlockTest("Option Explicit On Foo Bar Baz", + new DirectiveBlock( + Factory.MetaCode("Option Explicit On") + .With(SetVBOptionCodeGenerator.Explicit(true)))); + } + + [Fact] + public void ParseBlockReadsToAfterOffKeywordIfOptionExplicitBlock() + { + ParseBlockTest("Option Explicit Off Foo Bar Baz", + new DirectiveBlock( + Factory.MetaCode("Option Explicit Off") + .With(SetVBOptionCodeGenerator.Explicit(false)))); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBStatementTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBStatementTest.cs new file mode 100644 index 0000000000..c5f807a8b8 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBStatementTest.cs @@ -0,0 +1,221 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.VB +{ + public class VBStatementTest : VBHtmlCodeParserTestBase + { + [Fact] + public void VB_Inherits_Statement() + { + ParseBlockTest("@Inherits System.Foo.Bar(Of Baz)", + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode("Inherits ").Accepts(AcceptedCharacters.None), + Factory.Code("System.Foo.Bar(Of Baz)") + .AsBaseType("System.Foo.Bar(Of Baz)"))); + } + + [Fact] + public void InheritsDirectiveSupportsArrays() + { + ParseBlockTest("@Inherits System.String(())()", + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode("Inherits ") + .Accepts(AcceptedCharacters.None), + Factory.Code("System.String(())()") + .AsBaseType("System.String(())()"))); + } + + [Fact] + public void InheritsDirectiveSupportsNestedGenerics() + { + ParseBlockTest("@Inherits System.Web.Mvc.WebViewPage(Of IEnumerable(Of MvcApplication2.Models.RegisterModel))", + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode("Inherits ") + .Accepts(AcceptedCharacters.None), + Factory.Code("System.Web.Mvc.WebViewPage(Of IEnumerable(Of MvcApplication2.Models.RegisterModel))") + .AsBaseType("System.Web.Mvc.WebViewPage(Of IEnumerable(Of MvcApplication2.Models.RegisterModel))"))); + } + + [Fact] + public void InheritsDirectiveSupportsTypeKeywords() + { + ParseBlockTest("@Inherits String", + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode("Inherits ").Accepts(AcceptedCharacters.None), + Factory.Code("String").AsBaseType("String"))); + } + + [Fact] + public void VB_Option_Strict_Statement() + { + ParseBlockTest("@Option Strict Off", + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode("Option Strict Off") + .With(SetVBOptionCodeGenerator.Strict(false)))); + } + + [Fact] + public void VB_Option_Explicit_Statement() + { + ParseBlockTest("@Option Explicit Off", + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode("Option Explicit Off") + .With(SetVBOptionCodeGenerator.Explicit(false)))); + } + + [Fact] + public void VB_Imports_Statement() + { + ParseBlockTest("@Imports Biz = System.Foo.Bar(Of Boz.Baz(Of Qux))", + new DirectiveBlock( + Factory.CodeTransition(), + Factory.MetaCode("Imports Biz = System.Foo.Bar(Of Boz.Baz(Of Qux))") + .With(new AddImportCodeGenerator( + ns: " Biz = System.Foo.Bar(Of Boz.Baz(Of Qux))", + namespaceKeywordLength: SyntaxConstants.VB.ImportsKeywordLength)))); + } + + [Fact] + public void VB_Using_Statement() + { + ParseBlockTest("@Using foo as Bar" + Environment.NewLine + + " foo()" + Environment.NewLine + + "End Using", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("Using foo as Bar\r\n foo()\r\nEnd Using") + .AsStatement() + .Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void VB_Do_Loop_Statement() + { + ParseBlockTest("@Do" + Environment.NewLine + + " foo()" + Environment.NewLine + + "Loop While True", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("Do\r\n foo()\r\nLoop While True") + .AsStatement() + .Accepts(AcceptedCharacters.AnyExceptNewline))); + } + + [Fact] + public void VB_While_Statement() + { + ParseBlockTest("@While True" + Environment.NewLine + + " foo()" + Environment.NewLine + + "End While", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("While True\r\n foo()\r\nEnd While") + .AsStatement() + .Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void VB_If_Statement() + { + ParseBlockTest("@If True Then" + Environment.NewLine + + " foo()" + Environment.NewLine + + "ElseIf False Then" + Environment.NewLine + + " bar()" + Environment.NewLine + + "Else" + Environment.NewLine + + " baz()" + Environment.NewLine + + "End If", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("If True Then\r\n foo()\r\nElseIf False Then\r\n bar()\r\nElse\r\n baz()\r\nEnd If") + .AsStatement() + .Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void VB_Select_Statement() + { + ParseBlockTest("@Select Case foo" + Environment.NewLine + + " Case 1" + Environment.NewLine + + " foo()" + Environment.NewLine + + " Case 2" + Environment.NewLine + + " bar()" + Environment.NewLine + + " Case Else" + Environment.NewLine + + " baz()" + Environment.NewLine + + "End Select", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("Select Case foo\r\n Case 1\r\n foo()\r\n Case 2\r\n bar()\r\n Case Else\r\n baz()\r\nEnd Select") + .AsStatement() + .Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void VB_For_Statement() + { + ParseBlockTest("@For Each foo In bar" + Environment.NewLine + + " baz()" + Environment.NewLine + + "Next", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("For Each foo In bar\r\n baz()\r\nNext") + .AsStatement() + .Accepts(AcceptedCharacters.AnyExceptNewline))); + } + + [Fact] + public void VB_Try_Statement() + { + ParseBlockTest("@Try" + Environment.NewLine + + " foo()" + Environment.NewLine + + "Catch ex as Exception" + Environment.NewLine + + " bar()" + Environment.NewLine + + "Finally" + Environment.NewLine + + " baz()" + Environment.NewLine + + "End Try", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("Try\r\n foo()\r\nCatch ex as Exception\r\n bar()\r\nFinally\r\n baz()\r\nEnd Try") + .AsStatement() + .Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void VB_With_Statement() + { + ParseBlockTest("@With foo" + Environment.NewLine + + " .bar()" + Environment.NewLine + + "End With", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("With foo\r\n .bar()\r\nEnd With") + .AsStatement() + .Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void VB_SyncLock_Statement() + { + ParseBlockTest("@SyncLock foo" + Environment.NewLine + + " foo.bar()" + Environment.NewLine + + "End SyncLock", + new StatementBlock( + Factory.CodeTransition(), + Factory.Code("SyncLock foo\r\n foo.bar()\r\nEnd SyncLock") + .AsStatement() + .Accepts(AcceptedCharacters.None))); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBTemplateTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBTemplateTest.cs new file mode 100644 index 0000000000..f3533621d5 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBTemplateTest.cs @@ -0,0 +1,214 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.VB +{ + public class VBTemplateTest : VBHtmlCodeParserTestBase + { + private const string TestTemplateCode = "@@

    Foo #@item

    "; + + private TemplateBlock TestTemplate() + { + return new TemplateBlock(new TemplateBlockCodeGenerator(), + new MarkupBlock( + Factory.MarkupTransition(), + Factory.MetaMarkup("@"), + Factory.Markup("

    Foo #"), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("item") + .AsImplicitExpression(VBCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Markup("

    ").Accepts(AcceptedCharacters.None))); + } + + private const string TestNestedTemplateCode = "@@

    Foo #@Html.Repeat(10,@@

    @item

    )

    "; + + private TemplateBlock TestNestedTemplate() + { + return new TemplateBlock(new TemplateBlockCodeGenerator(), + new MarkupBlock( + Factory.MarkupTransition(), + Factory.MetaMarkup("@"), + Factory.Markup("

    Foo #"), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("Html.Repeat(10,") + .AsImplicitExpression(VBCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.Any), + new TemplateBlock(new TemplateBlockCodeGenerator(), + new MarkupBlock( + Factory.MarkupTransition(), + Factory.MetaMarkup("@"), + Factory.Markup("

    "), + new ExpressionBlock( + Factory.CodeTransition(), + Factory.Code("item") + .AsImplicitExpression(VBCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Markup("

    ").Accepts(AcceptedCharacters.None))), + Factory.Code(")") + .AsImplicitExpression(VBCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)), + Factory.Markup("

    ").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockHandlesSimpleAnonymousSectionInExplicitExpressionParens() + { + ParseBlockTest("(Html.Repeat(10," + TestTemplateCode + "))", + new ExpressionBlock( + Factory.MetaCode("(").Accepts(AcceptedCharacters.None), + Factory.Code("Html.Repeat(10,").AsExpression(), + TestTemplate(), + Factory.Code(")").AsExpression(), + Factory.MetaCode(")").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockHandlesSimpleAnonymousSectionInImplicitExpressionParens() + { + ParseBlockTest("Html.Repeat(10," + TestTemplateCode + ")", + new ExpressionBlock( + Factory.Code("Html.Repeat(10,").AsImplicitExpression(KeywordSet), + TestTemplate(), + Factory.Code(")").AsImplicitExpression(KeywordSet).Accepts(AcceptedCharacters.NonWhiteSpace))); + } + + [Fact] + public void ParseBlockHandlesTwoAnonymousSectionsInImplicitExpressionParens() + { + ParseBlockTest("Html.Repeat(10," + TestTemplateCode + "," + TestTemplateCode + ")", + new ExpressionBlock( + Factory.Code("Html.Repeat(10,").AsImplicitExpression(KeywordSet), + TestTemplate(), + Factory.Code(",").AsImplicitExpression(KeywordSet), + TestTemplate(), + Factory.Code(")").AsImplicitExpression(KeywordSet).Accepts(AcceptedCharacters.NonWhiteSpace))); + } + + [Fact] + public void ParseBlockProducesErrorButCorrectlyParsesNestedAnonymousSectionInImplicitExpressionParens() + { + ParseBlockTest("Html.Repeat(10," + TestNestedTemplateCode + ")", + new ExpressionBlock( + Factory.Code("Html.Repeat(10,").AsImplicitExpression(KeywordSet), + TestNestedTemplate(), + Factory.Code(")").AsImplicitExpression(KeywordSet).Accepts(AcceptedCharacters.NonWhiteSpace)), + GetNestedSectionError(41, 0, 41)); + } + + [Fact] + public void ParseBlockHandlesSimpleAnonymousSectionInStatementWithinCodeBlock() + { + ParseBlockTest("For Each foo in Bar " + Environment.NewLine + + " Html.ExecuteTemplate(foo," + TestTemplateCode + ")" + Environment.NewLine + + "Next foo", + new StatementBlock( + Factory.Code("For Each foo in Bar \r\n Html.ExecuteTemplate(foo,") + .AsStatement(), + TestTemplate(), + Factory.Code(")\r\nNext foo") + .AsStatement() + .Accepts(AcceptedCharacters.WhiteSpace | AcceptedCharacters.NonWhiteSpace))); + } + + [Fact] + public void ParseBlockHandlesTwoAnonymousSectionsInStatementWithinCodeBlock() + { + ParseBlockTest("For Each foo in Bar " + Environment.NewLine + + " Html.ExecuteTemplate(foo," + TestTemplateCode + "," + TestTemplateCode + ")" + Environment.NewLine + + "Next foo", + new StatementBlock( + Factory.Code("For Each foo in Bar \r\n Html.ExecuteTemplate(foo,") + .AsStatement(), + TestTemplate(), + Factory.Code(",").AsStatement(), + TestTemplate(), + Factory.Code(")\r\nNext foo") + .AsStatement() + .Accepts(AcceptedCharacters.WhiteSpace | AcceptedCharacters.NonWhiteSpace))); + } + + [Fact] + public void ParseBlockProducesErrorButCorrectlyParsesNestedAnonymousSectionInStatementWithinCodeBlock() + { + ParseBlockTest("For Each foo in Bar " + Environment.NewLine + + " Html.ExecuteTemplate(foo," + TestNestedTemplateCode + ")" + Environment.NewLine + + "Next foo", + new StatementBlock( + Factory.Code("For Each foo in Bar \r\n Html.ExecuteTemplate(foo,") + .AsStatement(), + TestNestedTemplate(), + Factory.Code(")\r\nNext foo") + .AsStatement() + .Accepts(AcceptedCharacters.WhiteSpace | AcceptedCharacters.NonWhiteSpace)), + GetNestedSectionError(77, 1, 55)); + } + + [Fact] + public void ParseBlockHandlesSimpleAnonymousSectionInStatementWithinStatementBlock() + { + ParseBlockTest("Code " + Environment.NewLine + + " Dim foo = bar" + Environment.NewLine + + " Html.ExecuteTemplate(foo," + TestTemplateCode + ")" + Environment.NewLine + + "End Code", + new StatementBlock( + Factory.MetaCode("Code").Accepts(AcceptedCharacters.None), + Factory.Code(" \r\n Dim foo = bar\r\n Html.ExecuteTemplate(foo,") + .AsStatement(), + TestTemplate(), + Factory.Code(")\r\n").AsStatement(), + Factory.MetaCode("End Code").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockHandlessTwoAnonymousSectionsInStatementWithinStatementBlock() + { + ParseBlockTest("Code" + Environment.NewLine + + " Dim foo = bar" + Environment.NewLine + + " Html.ExecuteTemplate(foo," + TestTemplateCode + "," + TestTemplateCode + ")" + Environment.NewLine + + "End Code", + new StatementBlock( + Factory.MetaCode("Code").Accepts(AcceptedCharacters.None), + Factory.Code("\r\n Dim foo = bar\r\n Html.ExecuteTemplate(foo,") + .AsStatement(), + TestTemplate(), + Factory.Code(",").AsStatement(), + TestTemplate(), + Factory.Code(")\r\n").AsStatement(), + Factory.MetaCode("End Code").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockProducesErrorButCorrectlyParsesNestedAnonymousSectionInStatementWithinStatementBlock() + { + ParseBlockTest("Code" + Environment.NewLine + + " Dim foo = bar" + Environment.NewLine + + " Html.ExecuteTemplate(foo," + TestNestedTemplateCode + ")" + Environment.NewLine + + "End Code", + new StatementBlock( + Factory.MetaCode("Code").Accepts(AcceptedCharacters.None), + Factory.Code("\r\n Dim foo = bar\r\n Html.ExecuteTemplate(foo,") + .AsStatement(), + TestNestedTemplate(), + Factory.Code(")\r\n").AsStatement(), + Factory.MetaCode("End Code").Accepts(AcceptedCharacters.None)), + GetNestedSectionError(80, 2, 55)); + } + + private static RazorError GetNestedSectionError(int absoluteIndex, int lineIndex, int characterIndex) + { + return new RazorError( + RazorResources.ParseError_InlineMarkup_Blocks_Cannot_Be_Nested, + absoluteIndex, lineIndex, characterIndex); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBToMarkupSwitchTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBToMarkupSwitchTest.cs new file mode 100644 index 0000000000..ad16c145e6 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/VB/VBToMarkupSwitchTest.cs @@ -0,0 +1,105 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Editor; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser.VB +{ + public class VBToMarkupSwitchTest : VBHtmlCodeParserTestBase + { + [Fact] + public void ParseBlockSwitchesToMarkupWhenAtSignFollowedByLessThanInStatementBlock() + { + ParseBlockTest("Code" + Environment.NewLine + + " If True Then" + Environment.NewLine + + " @

    It's True!

    " + Environment.NewLine + + " End If" + Environment.NewLine + + "End Code", + new StatementBlock( + Factory.MetaCode("Code").Accepts(AcceptedCharacters.None), + Factory.Code("\r\n If True Then\r\n").AsStatement(), + new MarkupBlock( + Factory.Markup(" "), + Factory.MarkupTransition(), + Factory.Markup("

    It's True!

    \r\n").Accepts(AcceptedCharacters.None)), + Factory.Code(" End If\r\n").AsStatement(), + Factory.MetaCode("End Code").Accepts(AcceptedCharacters.None))); + } + + [Fact] + public void ParseBlockGivesWhiteSpacePreceedingMarkupBlockToCodeInDesignTimeMode() + { + ParseBlockTest("Code" + Environment.NewLine + + " @

    Foo

    " + Environment.NewLine + + "End Code", + new StatementBlock( + Factory.MetaCode("Code").Accepts(AcceptedCharacters.None), + Factory.Code("\r\n ").AsStatement(), + new MarkupBlock( + Factory.MarkupTransition(), + Factory.Markup("

    Foo

    ").Accepts(AcceptedCharacters.None)), + Factory.Code("\r\n").AsStatement(), + Factory.MetaCode("End Code").Accepts(AcceptedCharacters.None)), + designTimeParser: true); + } + + [Theory] + [InlineData("While", "End While", AcceptedCharacters.None)] + [InlineData("If", "End If", AcceptedCharacters.None)] + [InlineData("Select", "End Select", AcceptedCharacters.None)] + [InlineData("For", "Next", AcceptedCharacters.WhiteSpace | AcceptedCharacters.NonWhiteSpace)] + [InlineData("Try", "End Try", AcceptedCharacters.None)] + [InlineData("With", "End With", AcceptedCharacters.None)] + [InlineData("Using", "End Using", AcceptedCharacters.None)] + public void SimpleMarkupSwitch(string keyword, string endSequence, AcceptedCharacters acceptedCharacters) + { + ParseBlockTest(keyword + Environment.NewLine + + " If True Then" + Environment.NewLine + + " @

    It's True!

    " + Environment.NewLine + + " End If" + Environment.NewLine + + endSequence, + new StatementBlock( + Factory.Code(keyword + "\r\n If True Then\r\n").AsStatement(), + new MarkupBlock( + Factory.Markup(" "), + Factory.MarkupTransition(), + Factory.Markup("

    It's True!

    \r\n").Accepts(AcceptedCharacters.None)), + Factory.Code(" End If\r\n" + endSequence).AsStatement().Accepts(acceptedCharacters))); + } + + [Theory] + [InlineData("While", "End While", AcceptedCharacters.None)] + [InlineData("If", "End If", AcceptedCharacters.None)] + [InlineData("Select", "End Select", AcceptedCharacters.None)] + [InlineData("For", "Next", AcceptedCharacters.WhiteSpace | AcceptedCharacters.NonWhiteSpace)] + [InlineData("Try", "End Try", AcceptedCharacters.None)] + [InlineData("With", "End With", AcceptedCharacters.None)] + [InlineData("Using", "End Using", AcceptedCharacters.None)] + public void SingleLineMarkupSwitch(string keyword, string endSequence, AcceptedCharacters acceptedCharacters) + { + ParseBlockTest(keyword + Environment.NewLine + + " If True Then" + Environment.NewLine + + " @:

    It's True!

    " + Environment.NewLine + + " This is code!" + Environment.NewLine + + " End If" + Environment.NewLine + + endSequence, + new StatementBlock( + Factory.Code(keyword + "\r\n If True Then\r\n").AsStatement(), + new MarkupBlock( + Factory.Markup(" "), + Factory.MarkupTransition(), + Factory.MetaMarkup(":", HtmlSymbolType.Colon), + Factory.Markup("

    It's True!

    \r\n") + .With(new SingleLineMarkupEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString)) + .Accepts(AcceptedCharacters.None)), + Factory.Code(" This is code!\r\n End If\r\n" + endSequence) + .AsStatement() + .Accepts(acceptedCharacters))); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/WhitespaceRewriterTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/WhitespaceRewriterTest.cs new file mode 100644 index 0000000000..c7e0a5d2e2 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Parser/WhitespaceRewriterTest.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Test.Framework; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Parser +{ + public class WhitespaceRewriterTest + { + [Fact] + public void Constructor_Requires_NonNull_SymbolConverter() + { + Assert.ThrowsArgumentNull(() => new WhiteSpaceRewriter(null), "markupSpanFactory"); + } + + [Fact] + public void Rewrite_Moves_Whitespace_Preceeding_ExpressionBlock_To_Parent_Block() + { + // Arrange + var factory = SpanFactory.CreateCsHtml(); + Block start = new MarkupBlock( + factory.Markup("test"), + new ExpressionBlock( + factory.Code(" ").AsExpression(), + factory.CodeTransition(SyntaxConstants.TransitionString), + factory.Code("foo").AsExpression() + ), + factory.Markup("test") + ); + WhiteSpaceRewriter rewriter = new WhiteSpaceRewriter(new HtmlMarkupParser().BuildSpan); + + // Act + Block actual = rewriter.Rewrite(start); + + factory.Reset(); + + // Assert + ParserTestBase.EvaluateParseTree(actual, new MarkupBlock( + factory.Markup("test"), + factory.Markup(" "), + new ExpressionBlock( + factory.CodeTransition(SyntaxConstants.TransitionString), + factory.Code("foo").AsExpression() + ), + factory.Markup("test") + )); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/RazorCodeLanguageTest.cs b/test/Microsoft.AspNet.Razor.Test/RazorCodeLanguageTest.cs new file mode 100644 index 0000000000..c2663d8a17 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/RazorCodeLanguageTest.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test +{ + public class RazorCodeLanguageTest + { + [Fact] + public void ServicesPropertyContainsEntriesForCSharpCodeLanguageService() + { + // Assert + Assert.Equal(2, RazorCodeLanguage.Languages.Count); + Assert.IsType(RazorCodeLanguage.Languages["cshtml"]); + Assert.IsType(RazorCodeLanguage.Languages["vbhtml"]); + } + + [Fact] + public void GetServiceByExtensionReturnsEntryMatchingExtensionWithoutPreceedingDot() + { + Assert.IsType(RazorCodeLanguage.GetLanguageByExtension("cshtml")); + } + + [Fact] + public void GetServiceByExtensionReturnsEntryMatchingExtensionWithPreceedingDot() + { + Assert.IsType(RazorCodeLanguage.GetLanguageByExtension(".cshtml")); + } + + [Fact] + public void GetServiceByExtensionReturnsNullIfNoServiceForSpecifiedExtension() + { + Assert.Null(RazorCodeLanguage.GetLanguageByExtension("foobar")); + } + + [Fact] + public void MultipleCallsToGetServiceWithSameExtensionReturnSameObject() + { + // Arrange + RazorCodeLanguage expected = RazorCodeLanguage.GetLanguageByExtension("cshtml"); + + // Act + RazorCodeLanguage actual = RazorCodeLanguage.GetLanguageByExtension("cshtml"); + + // Assert + Assert.Same(expected, actual); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/RazorDirectiveAttributeTest.cs b/test/Microsoft.AspNet.Razor.Test/RazorDirectiveAttributeTest.cs new file mode 100644 index 0000000000..081809b20e --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/RazorDirectiveAttributeTest.cs @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Linq; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test +{ + public class RazorDirectiveAttributeTest + { + [Fact] + public void ConstructorThrowsIfNameIsNullOrEmpty() + { + // Act and Assert + Assert.ThrowsArgumentNullOrEmptyString(() => new RazorDirectiveAttribute(name: null, value: "blah"), "name"); + Assert.ThrowsArgumentNullOrEmptyString(() => new RazorDirectiveAttribute(name: "", value: "blah"), "name"); + } + + [Fact] + public void EnsureRazorDirectiveProperties() + { + // Arrange + var attribute = (AttributeUsageAttribute)typeof(RazorDirectiveAttribute).GetCustomAttributes(typeof(AttributeUsageAttribute), inherit: false) + .SingleOrDefault(); + + // Assert + Assert.True(attribute.AllowMultiple); + Assert.True(attribute.ValidOn == AttributeTargets.Class); + Assert.True(attribute.Inherited); + } + + [Fact] + public void EqualsAndGetHashCodeIgnoresCase() + { + // Arrange + var attribute1 = new RazorDirectiveAttribute("foo", "bar"); + var attribute2 = new RazorDirectiveAttribute("fOo", "BAr"); + + // Act + var hashCode1 = attribute1.GetHashCode(); + var hashCode2 = attribute2.GetHashCode(); + + // Assert + Assert.Equal(attribute1, attribute2); + Assert.Equal(hashCode1, hashCode2); + } + + [Fact] + public void EqualsAndGetHashCodeDoNotThrowIfValueIsNullOrEmpty() + { + // Arrange + var attribute1 = new RazorDirectiveAttribute("foo", null); + var attribute2 = new RazorDirectiveAttribute("foo", "BAr"); + + // Act + bool result = attribute1.Equals(attribute2); + var hashCode = attribute1.GetHashCode(); + + // Assert + Assert.False(result); + // If we've got this far, GetHashCode did not throw + } + + [Fact] + public void EqualsAndGetHashCodeReturnDifferentValuesForNullAndEmpty() + { + // Arrange + var attribute1 = new RazorDirectiveAttribute("foo", null); + var attribute2 = new RazorDirectiveAttribute("foo", ""); + + // Act + bool result = attribute1.Equals(attribute2); + var hashCode1 = attribute1.GetHashCode(); + var hashCode2 = attribute2.GetHashCode(); + + // Assert + Assert.False(result); + Assert.NotEqual(hashCode1, hashCode2); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/RazorEngineHostTest.cs b/test/Microsoft.AspNet.Razor.Test/RazorEngineHostTest.cs new file mode 100644 index 0000000000..d122f8a8e9 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/RazorEngineHostTest.cs @@ -0,0 +1,187 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.CodeDom; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test +{ + public class RazorEngineHostTest + { + [Fact] + public void ConstructorRequiresNonNullCodeLanguage() + { + Assert.ThrowsArgumentNull(() => new RazorEngineHost(null), "codeLanguage"); + Assert.ThrowsArgumentNull(() => new RazorEngineHost(null, () => new HtmlMarkupParser()), "codeLanguage"); + } + + [Fact] + public void ConstructorRequiresNonNullMarkupParser() + { + Assert.ThrowsArgumentNull(() => new RazorEngineHost(new CSharpRazorCodeLanguage(), null), "markupParserFactory"); + } + + [Fact] + public void ConstructorWithCodeLanguageSetsPropertiesAppropriately() + { + // Arrange + RazorCodeLanguage language = new CSharpRazorCodeLanguage(); + + // Act + RazorEngineHost host = new RazorEngineHost(language); + + // Assert + VerifyCommonDefaults(host); + Assert.Same(language, host.CodeLanguage); + Assert.IsType(host.CreateMarkupParser()); + } + + [Fact] + public void ConstructorWithCodeLanguageAndMarkupParserSetsPropertiesAppropriately() + { + // Arrange + RazorCodeLanguage language = new CSharpRazorCodeLanguage(); + ParserBase expected = new HtmlMarkupParser(); + + // Act + RazorEngineHost host = new RazorEngineHost(language, () => expected); + + // Assert + VerifyCommonDefaults(host); + Assert.Same(language, host.CodeLanguage); + Assert.Same(expected, host.CreateMarkupParser()); + } + + [Fact] + public void DecorateCodeParserRequiresNonNullCodeParser() + { + Assert.ThrowsArgumentNull(() => CreateHost().DecorateCodeParser(null), "incomingCodeParser"); + } + + [Fact] + public void DecorateMarkupParserRequiresNonNullMarkupParser() + { + Assert.ThrowsArgumentNull(() => CreateHost().DecorateMarkupParser(null), "incomingMarkupParser"); + } + + [Fact] + public void DecorateCodeGeneratorRequiresNonNullCodeGenerator() + { + Assert.ThrowsArgumentNull(() => CreateHost().DecorateCodeGenerator(null), "incomingCodeGenerator"); + } + + [Fact] + public void PostProcessGeneratedCodeRequiresNonNullCompileUnit() + { + Assert.ThrowsArgumentNull(() => CreateHost().PostProcessGeneratedCode(codeCompileUnit: null, + generatedNamespace: new CodeNamespace(), + generatedClass: new CodeTypeDeclaration(), + executeMethod: new CodeMemberMethod()), + "codeCompileUnit"); + } + + [Fact] + public void PostProcessGeneratedCodeRequiresNonNullGeneratedNamespace() + { + Assert.ThrowsArgumentNull(() => CreateHost().PostProcessGeneratedCode(codeCompileUnit: new CodeCompileUnit(), + generatedNamespace: null, + generatedClass: new CodeTypeDeclaration(), + executeMethod: new CodeMemberMethod()), + "generatedNamespace"); + } + + [Fact] + public void PostProcessGeneratedCodeRequiresNonNullGeneratedClass() + { + Assert.ThrowsArgumentNull(() => CreateHost().PostProcessGeneratedCode(codeCompileUnit: new CodeCompileUnit(), + generatedNamespace: new CodeNamespace(), + generatedClass: null, + executeMethod: new CodeMemberMethod()), + "generatedClass"); + } + + [Fact] + public void PostProcessGeneratedCodeRequiresNonNullExecuteMethod() + { + Assert.ThrowsArgumentNull(() => CreateHost().PostProcessGeneratedCode(codeCompileUnit: new CodeCompileUnit(), + generatedNamespace: new CodeNamespace(), + generatedClass: new CodeTypeDeclaration(), + executeMethod: null), + "executeMethod"); + } + + [Fact] + public void DecorateCodeParserDoesNotModifyIncomingParser() + { + // Arrange + ParserBase expected = new CSharpCodeParser(); + + // Act + ParserBase actual = CreateHost().DecorateCodeParser(expected); + + // Assert + Assert.Same(expected, actual); + } + + [Fact] + public void DecorateMarkupParserReturnsIncomingParser() + { + // Arrange + ParserBase expected = new HtmlMarkupParser(); + + // Act + ParserBase actual = CreateHost().DecorateMarkupParser(expected); + + // Assert + Assert.Same(expected, actual); + } + + [Fact] + public void DecorateCodeGeneratorReturnsIncomingCodeGenerator() + { + // Arrange + RazorCodeGenerator expected = new CSharpRazorCodeGenerator("Foo", "Bar", "Baz", CreateHost()); + + // Act + RazorCodeGenerator actual = CreateHost().DecorateCodeGenerator(expected); + + // Assert + Assert.Same(expected, actual); + } + + [Fact] + public void PostProcessGeneratedCodeDoesNotModifyCode() + { + // Arrange + CodeCompileUnit compileUnit = new CodeCompileUnit(); + CodeNamespace ns = new CodeNamespace(); + CodeTypeDeclaration typeDecl = new CodeTypeDeclaration(); + CodeMemberMethod execMethod = new CodeMemberMethod(); + + // Act + CreateHost().PostProcessGeneratedCode(compileUnit, ns, typeDecl, execMethod); + + // Assert + Assert.Empty(compileUnit.Namespaces); + Assert.Empty(ns.Imports); + Assert.Empty(ns.Types); + Assert.Empty(typeDecl.Members); + Assert.Empty(execMethod.Statements); + } + + private static RazorEngineHost CreateHost() + { + return new RazorEngineHost(new CSharpRazorCodeLanguage()); + } + + private static void VerifyCommonDefaults(RazorEngineHost host) + { + Assert.Equal(GeneratedClassContext.Default, host.GeneratedClassContext); + Assert.Empty(host.NamespaceImports); + Assert.False(host.DesignTimeMode); + Assert.Equal(RazorEngineHost.InternalDefaultClassName, host.DefaultClassName); + Assert.Equal(RazorEngineHost.InternalDefaultNamespace, host.DefaultNamespace); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/RazorTemplateEngineTest.cs b/test/Microsoft.AspNet.Razor.Test/RazorTemplateEngineTest.cs new file mode 100644 index 0000000000..4523b0f5f5 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/RazorTemplateEngineTest.cs @@ -0,0 +1,198 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.IO; +using System.Threading; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.Text; +using System.Web.WebPages.TestUtils; +using Microsoft.TestCommon; +using Moq; + +namespace Microsoft.AspNet.Razor.Test +{ + public class RazorTemplateEngineTest + { + [Fact] + public void ConstructorRequiresNonNullHost() + { + Assert.ThrowsArgumentNull(() => new RazorTemplateEngine(null), "host"); + } + + [Fact] + public void ConstructorInitializesHost() + { + // Arrange + RazorEngineHost host = new RazorEngineHost(new CSharpRazorCodeLanguage()); + + // Act + RazorTemplateEngine engine = new RazorTemplateEngine(host); + + // Assert + Assert.Same(host, engine.Host); + } + + [Fact] + public void CreateParserMethodIsConstructedFromHost() + { + // Arrange + RazorEngineHost host = CreateHost(); + RazorTemplateEngine engine = new RazorTemplateEngine(host); + + // Act + RazorParser parser = engine.CreateParser(); + + // Assert + Assert.IsType(parser.CodeParser); + Assert.IsType(parser.MarkupParser); + } + + [Fact] + public void CreateParserMethodSetsParserContextToDesignTimeModeIfHostSetToDesignTimeMode() + { + // Arrange + RazorEngineHost host = CreateHost(); + RazorTemplateEngine engine = new RazorTemplateEngine(host); + host.DesignTimeMode = true; + + // Act + RazorParser parser = engine.CreateParser(); + + // Assert + Assert.True(parser.DesignTimeMode); + } + + [Fact] + public void CreateParserMethodPassesParsersThroughDecoratorMethodsOnHost() + { + // Arrange + ParserBase expectedCode = new Mock().Object; + ParserBase expectedMarkup = new Mock().Object; + + var mockHost = new Mock(new CSharpRazorCodeLanguage()) { CallBase = true }; + mockHost.Setup(h => h.DecorateCodeParser(It.IsAny())) + .Returns(expectedCode); + mockHost.Setup(h => h.DecorateMarkupParser(It.IsAny())) + .Returns(expectedMarkup); + RazorTemplateEngine engine = new RazorTemplateEngine(mockHost.Object); + + // Act + RazorParser actual = engine.CreateParser(); + + // Assert + Assert.Equal(expectedCode, actual.CodeParser); + Assert.Equal(expectedMarkup, actual.MarkupParser); + } + + [Fact] + public void CreateCodeGeneratorMethodPassesCodeGeneratorThroughDecorateMethodOnHost() + { + // Arrange + var mockHost = new Mock(new CSharpRazorCodeLanguage()) { CallBase = true }; + + RazorCodeGenerator expected = new Mock("Foo", "Bar", "Baz", mockHost.Object).Object; + + mockHost.Setup(h => h.DecorateCodeGenerator(It.IsAny())) + .Returns(expected); + RazorTemplateEngine engine = new RazorTemplateEngine(mockHost.Object); + + // Act + RazorCodeGenerator actual = engine.CreateCodeGenerator("Foo", "Bar", "Baz"); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void ParseTemplateCopiesTextReaderContentToSeekableTextReaderAndPassesToParseTemplateCore() + { + // Arrange + Mock mockEngine = new Mock(CreateHost()); + TextReader reader = new StringReader("foo"); + CancellationTokenSource source = new CancellationTokenSource(); + + // Act + mockEngine.Object.ParseTemplate(reader, cancelToken: source.Token); + + // Assert + mockEngine.Verify(e => e.ParseTemplateCore(It.Is(l => l.ReadToEnd() == "foo"), + source.Token)); + } + + [Fact] + public void GenerateCodeCopiesTextReaderContentToSeekableTextReaderAndPassesToGenerateCodeCore() + { + // Arrange + Mock mockEngine = new Mock(CreateHost()); + TextReader reader = new StringReader("foo"); + CancellationTokenSource source = new CancellationTokenSource(); + string className = "Foo"; + string ns = "Bar"; + string src = "Baz"; + + // Act + mockEngine.Object.GenerateCode(reader, className: className, rootNamespace: ns, sourceFileName: src, cancelToken: source.Token); + + // Assert + mockEngine.Verify(e => e.GenerateCodeCore(It.Is(l => l.ReadToEnd() == "foo"), + className, ns, src, source.Token)); + } + + [Fact] + public void ParseTemplateOutputsResultsOfParsingProvidedTemplateSource() + { + // Arrange + RazorTemplateEngine engine = new RazorTemplateEngine(CreateHost()); + + // Act + ParserResults results = engine.ParseTemplate(new StringTextBuffer("foo @bar(")); + + // Assert + Assert.False(results.Success); + Assert.Single(results.ParserErrors); + Assert.NotNull(results.Document); + } + + [Fact] + public void GenerateOutputsResultsOfParsingAndGeneration() + { + // Arrange + RazorTemplateEngine engine = new RazorTemplateEngine(CreateHost()); + + // Act + GeneratorResults results = engine.GenerateCode(new StringTextBuffer("foo @bar(")); + + // Assert + Assert.False(results.Success); + Assert.Single(results.ParserErrors); + Assert.NotNull(results.Document); + Assert.NotNull(results.GeneratedCode); + Assert.Null(results.DesignTimeLineMappings); + } + + [Fact] + public void GenerateOutputsDesignTimeMappingsIfDesignTimeSetOnHost() + { + // Arrange + RazorTemplateEngine engine = new RazorTemplateEngine(CreateHost(designTime: true)); + + // Act + GeneratorResults results = engine.GenerateCode(new StringTextBuffer("foo @bar()"), className: null, rootNamespace: null, sourceFileName: "foo.cshtml"); + + // Assert + Assert.True(results.Success); + Assert.Empty(results.ParserErrors); + Assert.NotNull(results.Document); + Assert.NotNull(results.GeneratedCode); + Assert.NotNull(results.DesignTimeLineMappings); + } + + private static RazorEngineHost CreateHost(bool designTime = false) + { + return new RazorEngineHost(new CSharpRazorCodeLanguage()) + { + DesignTimeMode = designTime + }; + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/StringTextBuffer.cs b/test/Microsoft.AspNet.Razor.Test/StringTextBuffer.cs new file mode 100644 index 0000000000..c9455167e6 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/StringTextBuffer.cs @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Text; + +namespace System.Web.WebPages.TestUtils +{ + public class StringTextBuffer : ITextBuffer, IDisposable + { + private string _buffer; + public bool Disposed { get; set; } + + public StringTextBuffer(string buffer) + { + _buffer = buffer; + } + + public int Length + { + get { return _buffer.Length; } + } + + public int Position { get; set; } + + public int Read() + { + if (Position >= _buffer.Length) + { + return -1; + } + return _buffer[Position++]; + } + + public int Peek() + { + if (Position >= _buffer.Length) + { + return -1; + } + return _buffer[Position]; + } + + public void Dispose() + { + Disposed = true; + } + + public object VersionToken + { + get { return _buffer; } + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Blocks.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Blocks.cs new file mode 100644 index 0000000000..f92286bfa6 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Blocks.cs @@ -0,0 +1,194 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class Blocks { +#line hidden +public Blocks() { +} +public override void Execute() { + +#line 1 "Blocks.cshtml" + + int i = 1; + + +#line default +#line hidden +WriteLiteral("\r\n\r\n"); + + +#line 5 "Blocks.cshtml" + while(i <= 10) { + + +#line default +#line hidden +WriteLiteral("

    Hello from C#, #"); + + +#line 6 "Blocks.cshtml" + Write(i); + + +#line default +#line hidden +WriteLiteral("

    \r\n"); + + +#line 7 "Blocks.cshtml" + i += 1; +} + + +#line default +#line hidden +WriteLiteral("\r\n"); + + +#line 10 "Blocks.cshtml" + if(i == 11) { + + +#line default +#line hidden +WriteLiteral("

    We wrote 10 lines!

    \r\n"); + + +#line 12 "Blocks.cshtml" +} + + +#line default +#line hidden +WriteLiteral("\r\n"); + + +#line 14 "Blocks.cshtml" + switch(i) { + case 11: + + +#line default +#line hidden +WriteLiteral("

    No really, we wrote 10 lines!

    \r\n"); + + +#line 17 "Blocks.cshtml" + break; + default: + + +#line default +#line hidden +WriteLiteral("

    Actually, we didn\'t...

    \r\n"); + + +#line 20 "Blocks.cshtml" + break; +} + + +#line default +#line hidden +WriteLiteral("\r\n"); + + +#line 23 "Blocks.cshtml" + for(int j = 1; j <= 10; j += 2) { + + +#line default +#line hidden +WriteLiteral("

    Hello again from C#, #"); + + +#line 24 "Blocks.cshtml" + Write(j); + + +#line default +#line hidden +WriteLiteral("

    \r\n"); + + +#line 25 "Blocks.cshtml" +} + + +#line default +#line hidden +WriteLiteral("\r\n"); + + +#line 27 "Blocks.cshtml" + try { + + +#line default +#line hidden +WriteLiteral("

    That time, we wrote 5 lines!

    \r\n"); + + +#line 29 "Blocks.cshtml" +} catch(Exception ex) { + + +#line default +#line hidden +WriteLiteral("

    Oh no! An error occurred: "); + + +#line 30 "Blocks.cshtml" + Write(ex.Message); + + +#line default +#line hidden +WriteLiteral("

    \r\n"); + + +#line 31 "Blocks.cshtml" +} + + +#line default +#line hidden +WriteLiteral("\r\n

    i is now "); + + +#line 33 "Blocks.cshtml" + Write(i); + + +#line default +#line hidden +WriteLiteral("

    \r\n\r\n"); + + +#line 35 "Blocks.cshtml" + lock(new object()) { + + +#line default +#line hidden +WriteLiteral("

    This block is locked, for your security!

    \r\n"); + + +#line 37 "Blocks.cshtml" +} + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/CodeBlock.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/CodeBlock.cs new file mode 100644 index 0000000000..a47b997e99 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/CodeBlock.cs @@ -0,0 +1,31 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class CodeBlock { +#line hidden +public CodeBlock() { +} +public override void Execute() { + +#line 1 "CodeBlock.cshtml" + + for(int i = 1; i <= 10; i++) { + Output.Write("

    Hello from C#, #" + i.ToString() + "

    "); + } + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/CodeBlockAtEOF.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/CodeBlockAtEOF.cs new file mode 100644 index 0000000000..1563d2e041 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/CodeBlockAtEOF.cs @@ -0,0 +1,27 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class CodeBlockAtEOF { +#line hidden +public CodeBlockAtEOF() { +} +public override void Execute() { + +#line 1 "CodeBlockAtEOF.cshtml" + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Comments.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Comments.cs new file mode 100644 index 0000000000..61e70fcb2b --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Comments.cs @@ -0,0 +1,38 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class Comments { +public override void Execute() { + + +#line 1 "Comments.cshtml" + //This is not going to be rendered + + +#line default +#line hidden +WriteLiteral("

    This is going to be rendered

    \r\n"); + + + +#line 3 "Comments.cshtml" + /* Neither is this + nor this + nor this */ + +#line default +#line hidden + +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ConditionalAttributes.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ConditionalAttributes.cs new file mode 100644 index 0000000000..90d8a9a577 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ConditionalAttributes.cs @@ -0,0 +1,197 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class ConditionalAttributes { +#line hidden +public ConditionalAttributes() { +} +public override void Execute() { + +#line 1 "ConditionalAttributes.cshtml" + + var ch = true; + var cls = "bar"; + + +#line default +#line hidden +WriteLiteral(" \r\n"); + +WriteLiteral(" (cls + +#line default +#line hidden +, 82), false) +); + +WriteLiteral(" />\r\n"); + +WriteLiteral(" (cls + +#line default +#line hidden +, 110), false) +); + +WriteLiteral(" />\r\n"); + +WriteLiteral(" (cls + +#line default +#line hidden +, 134), false) +, Tuple.Create(Tuple.Create(" ", 138), Tuple.Create("foo", 139), true) +); + +WriteLiteral(" />\r\n"); + +WriteLiteral(" (ch + +#line default +#line hidden +, 184), false) +); + +WriteLiteral(" />\r\n"); + +WriteLiteral(" (ch + +#line default +#line hidden +, 233), false) +); + +WriteLiteral(" />\r\n"); + +WriteLiteral(" (new Template(__razor_attribute_value_writer => { + + +#line 10 "ConditionalAttributes.cshtml" + if(cls != null) { + +#line default +#line hidden + +#line 10 "ConditionalAttributes.cshtml" +WriteTo(__razor_attribute_value_writer, cls); + + +#line default +#line hidden + +#line 10 "ConditionalAttributes.cshtml" + } + +#line default +#line hidden +}), 256), false) +); + +WriteLiteral(" />\r\n"); + +WriteLiteral(" (Href("~/Foo") +, 300), false) +); + +WriteLiteral(" />\r\n"); + +WriteLiteral(" (Url.Content("~/Scripts/jquery-1.6.2.min.js") + +#line default +#line hidden +, 328), false) +); + +WriteLiteral(" type=\"text/javascript\""); + +WriteLiteral(">\r\n"); + +WriteLiteral(" (Url.Content("~/Scripts/modernizr-2.0.6-development-only.js") + +#line default +#line hidden +, 426), false) +); + +WriteLiteral(" type=\"text/javascript\""); + +WriteLiteral(">\r\n"); + +WriteLiteral(" \r\n"); + + +#line 15 "ConditionalAttributes.cshtml" + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/DesignTime.Tabs.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/DesignTime.Tabs.cs new file mode 100644 index 0000000000..d727ecd786 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/DesignTime.Tabs.cs @@ -0,0 +1,110 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class DesignTime { +private static object @__o; +#line hidden +#line 9 "DesignTime.cshtml" +public static Template Foo() { +#line default +#line hidden +return new Template(__razor_helper_writer => { + +#line 10 "DesignTime.cshtml" + + if(true) { + + +#line default +#line hidden + +#line 11 "DesignTime.cshtml" + + } + + +#line default +#line hidden +}); + +#line 12 "DesignTime.cshtml" +} +#line default +#line hidden + +public DesignTime() { +} +public override void Execute() { + +#line 1 "DesignTime.cshtml" + for(int i = 1; i <= 10; i++) { + + +#line default +#line hidden + +#line 2 "DesignTime.cshtml" + __o = i; + + +#line default +#line hidden + +#line 3 "DesignTime.cshtml" + + } + +#line default +#line hidden + +#line 4 "DesignTime.cshtml" +__o = Foo(Bar.Baz); + + +#line default +#line hidden + +#line 5 "DesignTime.cshtml" +__o = Foo(item => new Template(__razor_template_writer => { + + +#line default +#line hidden + +#line 6 "DesignTime.cshtml" + __o = baz; + + +#line default +#line hidden + +#line 7 "DesignTime.cshtml" + })); + + +#line default +#line hidden +DefineSection("Footer", () => { + + +#line 8 "DesignTime.cshtml" +__o = bar; + + +#line default +#line hidden +}); + +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/DesignTime.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/DesignTime.cs new file mode 100644 index 0000000000..bcaded8858 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/DesignTime.cs @@ -0,0 +1,110 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class DesignTime { +private static object @__o; +#line hidden +#line 9 "DesignTime.cshtml" +public static Template Foo() { +#line default +#line hidden +return new Template(__razor_helper_writer => { + +#line 10 "DesignTime.cshtml" + + if(true) { + + +#line default +#line hidden + +#line 11 "DesignTime.cshtml" + + } + + +#line default +#line hidden +}); + +#line 12 "DesignTime.cshtml" +} +#line default +#line hidden + +public DesignTime() { +} +public override void Execute() { + +#line 1 "DesignTime.cshtml" + for(int i = 1; i <= 10; i++) { + + +#line default +#line hidden + +#line 2 "DesignTime.cshtml" + __o = i; + + +#line default +#line hidden + +#line 3 "DesignTime.cshtml" + + } + +#line default +#line hidden + +#line 4 "DesignTime.cshtml" +__o = Foo(Bar.Baz); + + +#line default +#line hidden + +#line 5 "DesignTime.cshtml" +__o = Foo(item => new Template(__razor_template_writer => { + + +#line default +#line hidden + +#line 6 "DesignTime.cshtml" + __o = baz; + + +#line default +#line hidden + +#line 7 "DesignTime.cshtml" + })); + + +#line default +#line hidden +DefineSection("Footer", () => { + + +#line 8 "DesignTime.cshtml" +__o = bar; + + +#line default +#line hidden +}); + +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/EmptyCodeBlock.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/EmptyCodeBlock.cs new file mode 100644 index 0000000000..5aa9721678 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/EmptyCodeBlock.cs @@ -0,0 +1,27 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class EmptyCodeBlock { +#line hidden +public EmptyCodeBlock() { +} +public override void Execute() { + +#line 1 "EmptyCodeBlock.cshtml" + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/EmptyExplicitExpression.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/EmptyExplicitExpression.cs new file mode 100644 index 0000000000..bac2bc5d60 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/EmptyExplicitExpression.cs @@ -0,0 +1,29 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class EmptyExplicitExpression { +private static object @__o; +#line hidden +public EmptyExplicitExpression() { +} +public override void Execute() { + +#line 1 "EmptyExplicitExpression.cshtml" +__o = ; + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/EmptyImplicitExpression.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/EmptyImplicitExpression.cs new file mode 100644 index 0000000000..bf8cdefa37 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/EmptyImplicitExpression.cs @@ -0,0 +1,29 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class EmptyImplicitExpression { +private static object @__o; +#line hidden +public EmptyImplicitExpression() { +} +public override void Execute() { + +#line 1 "EmptyImplicitExpression.cshtml" +__o = ; + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/EmptyImplicitExpressionInCode.Tabs.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/EmptyImplicitExpressionInCode.Tabs.cs new file mode 100644 index 0000000000..63851ecfb9 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/EmptyImplicitExpressionInCode.Tabs.cs @@ -0,0 +1,43 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class EmptyImplicitExpressionInCode { +private static object @__o; +#line hidden +public EmptyImplicitExpressionInCode() { +} +public override void Execute() { + +#line 1 "EmptyImplicitExpressionInCode.cshtml" + + + +#line default +#line hidden + +#line 2 "EmptyImplicitExpressionInCode.cshtml" +__o = ; + + +#line default +#line hidden + +#line 3 "EmptyImplicitExpressionInCode.cshtml" + + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/EmptyImplicitExpressionInCode.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/EmptyImplicitExpressionInCode.cs new file mode 100644 index 0000000000..3c8deb41a0 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/EmptyImplicitExpressionInCode.cs @@ -0,0 +1,43 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class EmptyImplicitExpressionInCode { +private static object @__o; +#line hidden +public EmptyImplicitExpressionInCode() { +} +public override void Execute() { + +#line 1 "EmptyImplicitExpressionInCode.cshtml" + + + +#line default +#line hidden + +#line 2 "EmptyImplicitExpressionInCode.cshtml" +__o = ; + + +#line default +#line hidden + +#line 3 "EmptyImplicitExpressionInCode.cshtml" + + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ExplicitExpression.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ExplicitExpression.cs new file mode 100644 index 0000000000..95651766c6 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ExplicitExpression.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class ExplicitExpression { +#line hidden +public ExplicitExpression() { +} +public override void Execute() { +WriteLiteral("1 + 1 = "); + + +#line 1 "ExplicitExpression.cshtml" + Write(1+1); + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ExplicitExpressionAtEOF.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ExplicitExpressionAtEOF.cs new file mode 100644 index 0000000000..941af4e02c --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ExplicitExpressionAtEOF.cs @@ -0,0 +1,29 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class ExplicitExpressionAtEOF { +private static object @__o; +#line hidden +public ExplicitExpressionAtEOF() { +} +public override void Execute() { + +#line 1 "ExplicitExpressionAtEOF.cshtml" +__o = ; + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ExpressionsInCode.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ExpressionsInCode.cs new file mode 100644 index 0000000000..4171f401a7 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ExpressionsInCode.cs @@ -0,0 +1,89 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class ExpressionsInCode { +#line hidden +public ExpressionsInCode() { +} +public override void Execute() { + +#line 1 "ExpressionsInCode.cshtml" + + object foo = null; + string bar = "Foo"; + + +#line default +#line hidden +WriteLiteral("\r\n\r\n"); + + +#line 6 "ExpressionsInCode.cshtml" + if(foo != null) { + + +#line default +#line hidden + +#line 7 "ExpressionsInCode.cshtml" +Write(foo); + + +#line default +#line hidden + +#line 7 "ExpressionsInCode.cshtml" + +} else { + + +#line default +#line hidden +WriteLiteral("

    Foo is Null!

    \r\n"); + + +#line 10 "ExpressionsInCode.cshtml" +} + + +#line default +#line hidden +WriteLiteral("\r\n

    \r\n"); + + +#line 13 "ExpressionsInCode.cshtml" + if(!String.IsNullOrEmpty(bar)) { + + +#line default +#line hidden + +#line 14 "ExpressionsInCode.cshtml" +Write(bar.Replace("F", "B")); + + +#line default +#line hidden + +#line 14 "ExpressionsInCode.cshtml" + +} + + +#line default +#line hidden +WriteLiteral("

    "); + +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/FunctionsBlock.DesignTime.Tabs.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/FunctionsBlock.DesignTime.Tabs.cs new file mode 100644 index 0000000000..442d6a0641 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/FunctionsBlock.DesignTime.Tabs.cs @@ -0,0 +1,46 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class FunctionsBlock { +private static object @__o; +#line hidden +#line 1 "FunctionsBlock.cshtml" + + + +#line default +#line hidden + +#line 2 "FunctionsBlock.cshtml" + + Random _rand = new Random(); + private int RandomInt() { + return _rand.Next(); + } + +#line default +#line hidden + +public FunctionsBlock() { +} +public override void Execute() { + +#line 3 "FunctionsBlock.cshtml" + __o = RandomInt(); + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/FunctionsBlock.DesignTime.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/FunctionsBlock.DesignTime.cs new file mode 100644 index 0000000000..534039f889 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/FunctionsBlock.DesignTime.cs @@ -0,0 +1,46 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class FunctionsBlock { +private static object @__o; +#line hidden +#line 1 "FunctionsBlock.cshtml" + + + +#line default +#line hidden + +#line 2 "FunctionsBlock.cshtml" + + Random _rand = new Random(); + private int RandomInt() { + return _rand.Next(); + } + +#line default +#line hidden + +public FunctionsBlock() { +} +public override void Execute() { + +#line 3 "FunctionsBlock.cshtml" + __o = RandomInt(); + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/FunctionsBlock.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/FunctionsBlock.cs new file mode 100644 index 0000000000..0b9f2a378d --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/FunctionsBlock.cs @@ -0,0 +1,49 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class FunctionsBlock { +#line hidden +#line 1 "FunctionsBlock.cshtml" + + + +#line default +#line hidden + +#line 5 "FunctionsBlock.cshtml" + + Random _rand = new Random(); + private int RandomInt() { + return _rand.Next(); + } + +#line default +#line hidden + +public FunctionsBlock() { +} +public override void Execute() { +WriteLiteral("\r\n"); + +WriteLiteral("\r\nHere\'s a random number: "); + + +#line 12 "FunctionsBlock.cshtml" + Write(RandomInt()); + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/FunctionsBlockMinimal.DesignTime.Tabs.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/FunctionsBlockMinimal.DesignTime.Tabs.cs new file mode 100644 index 0000000000..6384a74cf0 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/FunctionsBlockMinimal.DesignTime.Tabs.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class FunctionsBlockMinimal { +#line hidden +#line 1 "FunctionsBlockMinimal.cshtml" + +string foo(string input) { + return input + "!"; +} + +#line default +#line hidden + +public FunctionsBlockMinimal() { +} +public override void Execute() { +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/FunctionsBlock_Tabs.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/FunctionsBlock_Tabs.cs new file mode 100644 index 0000000000..863fdda875 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/FunctionsBlock_Tabs.cs @@ -0,0 +1,49 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class FunctionsBlock_Tabs { +#line hidden +#line 1 "FunctionsBlock_Tabs.cshtml" + + + +#line default +#line hidden + +#line 5 "FunctionsBlock_Tabs.cshtml" + + Random _rand = new Random(); + private int RandomInt() { + return _rand.Next(); + } + +#line default +#line hidden + +public FunctionsBlock_Tabs() { +} +public override void Execute() { +WriteLiteral("\r\n"); + +WriteLiteral("\r\nHere\'s a random number: "); + + +#line 12 "FunctionsBlock_Tabs.cshtml" + Write(RandomInt()); + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Helpers.Instance.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Helpers.Instance.cs new file mode 100644 index 0000000000..4664e58839 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Helpers.Instance.cs @@ -0,0 +1,106 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class Helpers { +#line hidden +#line 1 "Helpers.cshtml" +public Template Bold(string s) { +#line default +#line hidden +return new Template(__razor_helper_writer => { + +#line 1 "Helpers.cshtml" + + s = s.ToUpper(); + + +#line default +#line hidden +WriteLiteralTo(__razor_helper_writer, " "); + + +#line 3 "Helpers.cshtml" +WriteTo(__razor_helper_writer, s); + + +#line default +#line hidden +WriteLiteralTo(__razor_helper_writer, "\r\n"); + + +#line 4 "Helpers.cshtml" + + +#line default +#line hidden +}); + +#line 4 "Helpers.cshtml" +} +#line default +#line hidden + +#line 6 "Helpers.cshtml" +public Template Italic(string s) { +#line default +#line hidden +return new Template(__razor_helper_writer => { + +#line 6 "Helpers.cshtml" + + s = s.ToUpper(); + + +#line default +#line hidden +WriteLiteralTo(__razor_helper_writer, " "); + + +#line 8 "Helpers.cshtml" +WriteTo(__razor_helper_writer, s); + + +#line default +#line hidden +WriteLiteralTo(__razor_helper_writer, "\r\n"); + + +#line 9 "Helpers.cshtml" + + +#line default +#line hidden +}); + +#line 9 "Helpers.cshtml" +} +#line default +#line hidden + +public Helpers() { +} +public override void Execute() { +WriteLiteral("\r\n"); + +WriteLiteral("\r\n"); + + +#line 11 "Helpers.cshtml" +Write(Bold("Hello")); + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Helpers.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Helpers.cs new file mode 100644 index 0000000000..7e6c4eab06 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Helpers.cs @@ -0,0 +1,106 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class Helpers { +#line hidden +#line 1 "Helpers.cshtml" +public static Template Bold(string s) { +#line default +#line hidden +return new Template(__razor_helper_writer => { + +#line 1 "Helpers.cshtml" + + s = s.ToUpper(); + + +#line default +#line hidden +WriteLiteralTo(__razor_helper_writer, " "); + + +#line 3 "Helpers.cshtml" +WriteTo(__razor_helper_writer, s); + + +#line default +#line hidden +WriteLiteralTo(__razor_helper_writer, "\r\n"); + + +#line 4 "Helpers.cshtml" + + +#line default +#line hidden +}); + +#line 4 "Helpers.cshtml" +} +#line default +#line hidden + +#line 6 "Helpers.cshtml" +public static Template Italic(string s) { +#line default +#line hidden +return new Template(__razor_helper_writer => { + +#line 6 "Helpers.cshtml" + + s = s.ToUpper(); + + +#line default +#line hidden +WriteLiteralTo(__razor_helper_writer, " "); + + +#line 8 "Helpers.cshtml" +WriteTo(__razor_helper_writer, s); + + +#line default +#line hidden +WriteLiteralTo(__razor_helper_writer, "\r\n"); + + +#line 9 "Helpers.cshtml" + + +#line default +#line hidden +}); + +#line 9 "Helpers.cshtml" +} +#line default +#line hidden + +public Helpers() { +} +public override void Execute() { +WriteLiteral("\r\n"); + +WriteLiteral("\r\n"); + + +#line 11 "Helpers.cshtml" +Write(Bold("Hello")); + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HelpersMissingCloseParen.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HelpersMissingCloseParen.cs new file mode 100644 index 0000000000..e434440814 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HelpersMissingCloseParen.cs @@ -0,0 +1,66 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class HelpersMissingCloseParen { +#line hidden +#line 1 "HelpersMissingCloseParen.cshtml" +public static Template Bold(string s) { +#line default +#line hidden +return new Template(__razor_helper_writer => { + +#line 1 "HelpersMissingCloseParen.cshtml" + + s = s.ToUpper(); + + +#line default +#line hidden +WriteLiteralTo(__razor_helper_writer, " "); + + +#line 3 "HelpersMissingCloseParen.cshtml" +WriteTo(__razor_helper_writer, s); + + +#line default +#line hidden +WriteLiteralTo(__razor_helper_writer, "\r\n"); + + +#line 4 "HelpersMissingCloseParen.cshtml" + + +#line default +#line hidden +}); + +#line 4 "HelpersMissingCloseParen.cshtml" +} +#line default +#line hidden + +#line 6 "HelpersMissingCloseParen.cshtml" +public static Template Italic(string s +@Bold("Hello") +#line default +#line hidden + +public HelpersMissingCloseParen() { +} +public override void Execute() { +WriteLiteral("\r\n"); + +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HelpersMissingName.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HelpersMissingName.cs new file mode 100644 index 0000000000..aa0ea14602 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HelpersMissingName.cs @@ -0,0 +1,21 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class HelpersMissingName { +#line hidden +public HelpersMissingName() { +} +public override void Execute() { +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HelpersMissingOpenBrace.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HelpersMissingOpenBrace.cs new file mode 100644 index 0000000000..aef9e881ee --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HelpersMissingOpenBrace.cs @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class HelpersMissingOpenBrace { +#line hidden +#line 1 "HelpersMissingOpenBrace.cshtml" +public static Template Bold(string s) { +#line default +#line hidden +return new Template(__razor_helper_writer => { + +#line 1 "HelpersMissingOpenBrace.cshtml" + + s = s.ToUpper(); + + +#line default +#line hidden +WriteLiteralTo(__razor_helper_writer, " "); + + +#line 3 "HelpersMissingOpenBrace.cshtml" +WriteTo(__razor_helper_writer, s); + + +#line default +#line hidden +WriteLiteralTo(__razor_helper_writer, "\r\n"); + + +#line 4 "HelpersMissingOpenBrace.cshtml" + + +#line default +#line hidden +}); + +#line 4 "HelpersMissingOpenBrace.cshtml" +} +#line default +#line hidden + +#line 6 "HelpersMissingOpenBrace.cshtml" +public static Template Italic(string s) +#line default +#line hidden + +public HelpersMissingOpenBrace() { +} +public override void Execute() { +WriteLiteral("\r\n"); + + +#line 7 "HelpersMissingOpenBrace.cshtml" +Write(Italic(s)); + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HelpersMissingOpenParen.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HelpersMissingOpenParen.cs new file mode 100644 index 0000000000..b4f46de6a0 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HelpersMissingOpenParen.cs @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class HelpersMissingOpenParen { +#line hidden +#line 1 "HelpersMissingOpenParen.cshtml" +public static Template Bold(string s) { +#line default +#line hidden +return new Template(__razor_helper_writer => { + +#line 1 "HelpersMissingOpenParen.cshtml" + + s = s.ToUpper(); + + +#line default +#line hidden +WriteLiteralTo(__razor_helper_writer, " "); + + +#line 3 "HelpersMissingOpenParen.cshtml" +WriteTo(__razor_helper_writer, s); + + +#line default +#line hidden +WriteLiteralTo(__razor_helper_writer, "\r\n"); + + +#line 4 "HelpersMissingOpenParen.cshtml" + + +#line default +#line hidden +}); + +#line 4 "HelpersMissingOpenParen.cshtml" +} +#line default +#line hidden + +#line 6 "HelpersMissingOpenParen.cshtml" +public static Template Italic +#line default +#line hidden + +public HelpersMissingOpenParen() { +} +public override void Execute() { +WriteLiteral("\r\n"); + + +#line 7 "HelpersMissingOpenParen.cshtml" +Write(Bold("Hello")); + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HiddenSpansInCode.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HiddenSpansInCode.cs new file mode 100644 index 0000000000..31c1da097f --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HiddenSpansInCode.cs @@ -0,0 +1,35 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class HiddenSpansInCode { +#line hidden +public HiddenSpansInCode() { +} +public override void Execute() { + +#line 1 "HiddenSpansInCode.cshtml" + + + +#line default +#line hidden + +#line 2 "HiddenSpansInCode.cshtml" + @Da + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HtmlCommentWithQuote_Double.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HtmlCommentWithQuote_Double.cs new file mode 100644 index 0000000000..115da3dfff --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HtmlCommentWithQuote_Double.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class HtmlCommentWithQuote_Double { +#line hidden +public HtmlCommentWithQuote_Double() { +} +public override void Execute() { +WriteLiteral("\r\n(Href("~/images/submit.png") +, 22), false) +); + +WriteLiteral(" />"); + +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HtmlCommentWithQuote_Single.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HtmlCommentWithQuote_Single.cs new file mode 100644 index 0000000000..df58ff1e3a --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/HtmlCommentWithQuote_Single.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class HtmlCommentWithQuote_Single { +#line hidden +public HtmlCommentWithQuote_Single() { +} +public override void Execute() { +WriteLiteral("\r\n(Href("~/images/submit.png") +, 22), false) +); + +WriteLiteral(" />"); + +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ImplicitExpression.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ImplicitExpression.cs new file mode 100644 index 0000000000..6d9870377c --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ImplicitExpression.cs @@ -0,0 +1,45 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class ImplicitExpression { +#line hidden +public ImplicitExpression() { +} +public override void Execute() { + +#line 1 "ImplicitExpression.cshtml" + for(int i = 1; i <= 10; i++) { + + +#line default +#line hidden +WriteLiteral("

    This is item #"); + + +#line 2 "ImplicitExpression.cshtml" + Write(i); + + +#line default +#line hidden +WriteLiteral("

    \r\n"); + + +#line 3 "ImplicitExpression.cshtml" +} + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ImplicitExpressionAtEOF.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ImplicitExpressionAtEOF.cs new file mode 100644 index 0000000000..0eff69ae22 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ImplicitExpressionAtEOF.cs @@ -0,0 +1,29 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class ImplicitExpressionAtEOF { +private static object @__o; +#line hidden +public ImplicitExpressionAtEOF() { +} +public override void Execute() { + +#line 1 "ImplicitExpressionAtEOF.cshtml" +__o = ; + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Imports.DesignTime.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Imports.DesignTime.cs new file mode 100644 index 0000000000..082da73bbd --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Imports.DesignTime.cs @@ -0,0 +1,53 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { + +#line 3 "Imports.cshtml" +using System; + +#line default +#line hidden + +#line 1 "Imports.cshtml" +using System.IO; + +#line default +#line hidden + +#line 2 "Imports.cshtml" +using Foo = System.Text.Encoding; + +#line default +#line hidden + +public class Imports { +private static object @__o; +#line hidden +public Imports() { +} +public override void Execute() { + +#line 4 "Imports.cshtml" + __o = typeof(Path).FullName; + + +#line default +#line hidden + +#line 5 "Imports.cshtml" + __o = typeof(Foo).FullName; + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Imports.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Imports.cs new file mode 100644 index 0000000000..58105a3b10 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Imports.cs @@ -0,0 +1,58 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { + +#line 3 "Imports.cshtml" +using System; + +#line default +#line hidden + +#line 1 "Imports.cshtml" +using System.IO; + +#line default +#line hidden + +#line 2 "Imports.cshtml" +using Foo = System.Text.Encoding; + +#line default +#line hidden + +public class Imports { +#line hidden +public Imports() { +} +public override void Execute() { +WriteLiteral("\r\n

    Path\'s full type name is "); + + +#line 5 "Imports.cshtml" + Write(typeof(Path).FullName); + + +#line default +#line hidden +WriteLiteral("

    \r\n

    Foo\'s actual full type name is "); + + +#line 6 "Imports.cshtml" + Write(typeof(Foo).FullName); + + +#line default +#line hidden +WriteLiteral("

    "); + +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Inherits.Designtime.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Inherits.Designtime.cs new file mode 100644 index 0000000000..44af46d987 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Inherits.Designtime.cs @@ -0,0 +1,40 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class Inherits : foo.bar>.boz bar { +private static object @__o; +#line hidden +public Inherits() { +} +private void @__RazorDesignTimeHelpers__() { +#pragma warning disable 219 + +#line 2 "Inherits.cshtml" + foo.bar>.boz bar __inheritsHelper = null; + + +#line default +#line hidden +#pragma warning restore 219 +} +public override void Execute() { + +#line 1 "Inherits.cshtml" +__o = foo(); + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Inherits.Runtime.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Inherits.Runtime.cs new file mode 100644 index 0000000000..77e3633eac --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Inherits.Runtime.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class Inherits : foo.bar>.boz bar { +#line hidden +public Inherits() { +} +public override void Execute() { + +#line 1 "Inherits.cshtml" +Write(foo()); + + +#line default +#line hidden +WriteLiteral("\r\n\r\n"); + +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/InlineBlocks.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/InlineBlocks.cs new file mode 100644 index 0000000000..4dbd24e98b --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/InlineBlocks.cs @@ -0,0 +1,87 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class InlineBlocks { +#line hidden +#line 1 "InlineBlocks.cshtml" +public static Template Link(string link) { +#line default +#line hidden +return new Template(__razor_helper_writer => { + +#line 1 "InlineBlocks.cshtml" + + + +#line default +#line hidden +WriteLiteralTo(__razor_helper_writer, " (new Template(__razor_attribute_value_writer => { + + +#line 2 "InlineBlocks.cshtml" + if(link != null) { + +#line default +#line hidden + +#line 2 "InlineBlocks.cshtml" +WriteTo(__razor_attribute_value_writer, link); + + +#line default +#line hidden + +#line 2 "InlineBlocks.cshtml" + } else { + +#line default +#line hidden +WriteLiteralTo(__razor_attribute_value_writer, " "); + +WriteLiteralTo(__razor_attribute_value_writer, "#"); + +WriteLiteralTo(__razor_attribute_value_writer, " "); + + +#line 2 "InlineBlocks.cshtml" + } + +#line default +#line hidden +}), 42), false) +); + +WriteLiteralTo(__razor_helper_writer, " />\r\n"); + + +#line 3 "InlineBlocks.cshtml" + + +#line default +#line hidden +}); + +#line 3 "InlineBlocks.cshtml" +} +#line default +#line hidden + +public InlineBlocks() { +} +public override void Execute() { +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Instrumented.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Instrumented.cs new file mode 100644 index 0000000000..f9f99bb17f --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Instrumented.cs @@ -0,0 +1,359 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class Instrumented { +#line hidden +#line 1 "Instrumented.cshtml" +public static Template Strong(string s) { +#line default +#line hidden +return new Template(__razor_helper_writer => { + +#line 1 "Instrumented.cshtml" + + + +#line default +#line hidden +BeginContext(__razor_helper_writer, "~/Instrumented.cshtml", 28, 12, true); + +WriteLiteralTo(__razor_helper_writer, " "); + +EndContext(__razor_helper_writer, "~/Instrumented.cshtml", 28, 12, true); + +BeginContext(__razor_helper_writer, "~/Instrumented.cshtml", 41, 1, false); + + +#line 2 "Instrumented.cshtml" +WriteTo(__razor_helper_writer, s); + + +#line default +#line hidden +EndContext(__razor_helper_writer, "~/Instrumented.cshtml", 41, 1, false); + +BeginContext(__razor_helper_writer, "~/Instrumented.cshtml", 42, 11, true); + +WriteLiteralTo(__razor_helper_writer, "\r\n"); + +EndContext(__razor_helper_writer, "~/Instrumented.cshtml", 42, 11, true); + + +#line 3 "Instrumented.cshtml" + + +#line default +#line hidden +}); + +#line 3 "Instrumented.cshtml" +} +#line default +#line hidden + +public Instrumented() { +} +public override void Execute() { +BeginContext("~/Instrumented.cshtml", 56, 2, true); + +WriteLiteral("\r\n"); + +EndContext("~/Instrumented.cshtml", 56, 2, true); + + +#line 5 "Instrumented.cshtml" + + int i = 1; + var foo = + +#line default +#line hidden +item => new Template(__razor_template_writer => { + +BeginContext(__razor_template_writer, "~/Instrumented.cshtml", 93, 10, true); + +WriteLiteralTo(__razor_template_writer, "

    Bar

    "); + +EndContext(__razor_template_writer, "~/Instrumented.cshtml", 93, 10, true); + +}) + +#line 7 "Instrumented.cshtml" + ; + + +#line default +#line hidden +BeginContext("~/Instrumented.cshtml", 106, 4, true); + +WriteLiteral(" "); + +EndContext("~/Instrumented.cshtml", 106, 4, true); + +BeginContext("~/Instrumented.cshtml", 112, 14, true); + +WriteLiteral("Hello, World\r\n"); + +EndContext("~/Instrumented.cshtml", 112, 14, true); + +BeginContext("~/Instrumented.cshtml", 126, 25, true); + +WriteLiteral("

    Hello, World

    \r\n"); + +EndContext("~/Instrumented.cshtml", 126, 25, true); + + +#line 10 "Instrumented.cshtml" + + +#line default +#line hidden +BeginContext("~/Instrumented.cshtml", 152, 4, true); + +WriteLiteral("\r\n\r\n"); + +EndContext("~/Instrumented.cshtml", 152, 4, true); + + +#line 12 "Instrumented.cshtml" + while(i <= 10) { + + +#line default +#line hidden +BeginContext("~/Instrumented.cshtml", 175, 23, true); + +WriteLiteral("

    Hello from C#, #"); + +EndContext("~/Instrumented.cshtml", 175, 23, true); + +BeginContext("~/Instrumented.cshtml", 200, 1, false); + + +#line 13 "Instrumented.cshtml" + Write(i); + + +#line default +#line hidden +EndContext("~/Instrumented.cshtml", 200, 1, false); + +BeginContext("~/Instrumented.cshtml", 202, 6, true); + +WriteLiteral("

    \r\n"); + +EndContext("~/Instrumented.cshtml", 202, 6, true); + + +#line 14 "Instrumented.cshtml" + i += 1; +} + + +#line default +#line hidden +BeginContext("~/Instrumented.cshtml", 224, 2, true); + +WriteLiteral("\r\n"); + +EndContext("~/Instrumented.cshtml", 224, 2, true); + + +#line 17 "Instrumented.cshtml" + if(i == 11) { + + +#line default +#line hidden +BeginContext("~/Instrumented.cshtml", 242, 31, true); + +WriteLiteral("

    We wrote 10 lines!

    \r\n"); + +EndContext("~/Instrumented.cshtml", 242, 31, true); + + +#line 19 "Instrumented.cshtml" +} + + +#line default +#line hidden +BeginContext("~/Instrumented.cshtml", 276, 2, true); + +WriteLiteral("\r\n"); + +EndContext("~/Instrumented.cshtml", 276, 2, true); + + +#line 21 "Instrumented.cshtml" + switch(i) { + case 11: + + +#line default +#line hidden +BeginContext("~/Instrumented.cshtml", 306, 46, true); + +WriteLiteral("

    No really, we wrote 10 lines!

    \r\n"); + +EndContext("~/Instrumented.cshtml", 306, 46, true); + + +#line 24 "Instrumented.cshtml" + break; + default: + + +#line default +#line hidden +BeginContext("~/Instrumented.cshtml", 382, 39, true); + +WriteLiteral("

    Actually, we didn\'t...

    \r\n"); + +EndContext("~/Instrumented.cshtml", 382, 39, true); + + +#line 27 "Instrumented.cshtml" + break; +} + + +#line default +#line hidden +BeginContext("~/Instrumented.cshtml", 440, 2, true); + +WriteLiteral("\r\n"); + +EndContext("~/Instrumented.cshtml", 440, 2, true); + + +#line 30 "Instrumented.cshtml" + for(int j = 1; j <= 10; j += 2) { + + +#line default +#line hidden +BeginContext("~/Instrumented.cshtml", 478, 29, true); + +WriteLiteral("

    Hello again from C#, #"); + +EndContext("~/Instrumented.cshtml", 478, 29, true); + +BeginContext("~/Instrumented.cshtml", 509, 1, false); + + +#line 31 "Instrumented.cshtml" + Write(j); + + +#line default +#line hidden +EndContext("~/Instrumented.cshtml", 509, 1, false); + +BeginContext("~/Instrumented.cshtml", 511, 6, true); + +WriteLiteral("

    \r\n"); + +EndContext("~/Instrumented.cshtml", 511, 6, true); + + +#line 32 "Instrumented.cshtml" +} + + +#line default +#line hidden +BeginContext("~/Instrumented.cshtml", 520, 2, true); + +WriteLiteral("\r\n"); + +EndContext("~/Instrumented.cshtml", 520, 2, true); + + +#line 34 "Instrumented.cshtml" + try { + + +#line default +#line hidden +BeginContext("~/Instrumented.cshtml", 530, 41, true); + +WriteLiteral("

    That time, we wrote 5 lines!

    \r\n"); + +EndContext("~/Instrumented.cshtml", 530, 41, true); + + +#line 36 "Instrumented.cshtml" +} catch(Exception ex) { + + +#line default +#line hidden +BeginContext("~/Instrumented.cshtml", 596, 33, true); + +WriteLiteral("

    Oh no! An error occurred: "); + +EndContext("~/Instrumented.cshtml", 596, 33, true); + +BeginContext("~/Instrumented.cshtml", 631, 10, false); + + +#line 37 "Instrumented.cshtml" + Write(ex.Message); + + +#line default +#line hidden +EndContext("~/Instrumented.cshtml", 631, 10, false); + +BeginContext("~/Instrumented.cshtml", 642, 6, true); + +WriteLiteral("

    \r\n"); + +EndContext("~/Instrumented.cshtml", 642, 6, true); + + +#line 38 "Instrumented.cshtml" +} + + +#line default +#line hidden +BeginContext("~/Instrumented.cshtml", 651, 2, true); + +WriteLiteral("\r\n"); + +EndContext("~/Instrumented.cshtml", 651, 2, true); + + +#line 40 "Instrumented.cshtml" + lock(new object()) { + + +#line default +#line hidden +BeginContext("~/Instrumented.cshtml", 676, 53, true); + +WriteLiteral("

    This block is locked, for your security!

    \r\n"); + +EndContext("~/Instrumented.cshtml", 676, 53, true); + + +#line 42 "Instrumented.cshtml" +} + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/LayoutDirective.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/LayoutDirective.cs new file mode 100644 index 0000000000..86c0e0f6ea --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/LayoutDirective.cs @@ -0,0 +1,22 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class LayoutDirective { +#line hidden +public LayoutDirective() { +} +public override void Execute() { +Layout = "~/Foo/Bar/Baz"; +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/MarkupInCodeBlock.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/MarkupInCodeBlock.cs new file mode 100644 index 0000000000..14a1f096a0 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/MarkupInCodeBlock.cs @@ -0,0 +1,49 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class MarkupInCodeBlock { +#line hidden +public MarkupInCodeBlock() { +} +public override void Execute() { + +#line 1 "MarkupInCodeBlock.cshtml" + + for(int i = 1; i <= 10; i++) { + + +#line default +#line hidden +WriteLiteral("

    Hello from C#, #"); + + +#line 3 "MarkupInCodeBlock.cshtml" + Write(i.ToString()); + + +#line default +#line hidden +WriteLiteral("

    \r\n"); + + +#line 4 "MarkupInCodeBlock.cshtml" + } + + +#line default +#line hidden +WriteLiteral("\r\n"); + +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/NestedCodeBlocks.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/NestedCodeBlocks.cs new file mode 100644 index 0000000000..424e26c4c1 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/NestedCodeBlocks.cs @@ -0,0 +1,42 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class NestedCodeBlocks { +#line hidden +public NestedCodeBlocks() { +} +public override void Execute() { + +#line 1 "NestedCodeBlocks.cshtml" + if(foo) { + + +#line default +#line hidden + +#line 2 "NestedCodeBlocks.cshtml" + if(bar) { + } + +#line default +#line hidden + +#line 3 "NestedCodeBlocks.cshtml" + +} + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/NestedHelpers.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/NestedHelpers.cs new file mode 100644 index 0000000000..48c0e11469 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/NestedHelpers.cs @@ -0,0 +1,111 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class NestedHelpers { +#line hidden +#line 3 "NestedHelpers.cshtml" +public static Template Bold(string s) { +#line default +#line hidden +return new Template(__razor_helper_writer => { + +#line 3 "NestedHelpers.cshtml" + + s = s.ToUpper(); + + +#line default +#line hidden +WriteLiteralTo(__razor_helper_writer, " "); + + +#line 5 "NestedHelpers.cshtml" +WriteTo(__razor_helper_writer, s); + + +#line default +#line hidden +WriteLiteralTo(__razor_helper_writer, "\r\n"); + + +#line 6 "NestedHelpers.cshtml" + + +#line default +#line hidden +}); + +#line 6 "NestedHelpers.cshtml" +} +#line default +#line hidden + +#line 1 "NestedHelpers.cshtml" +public static Template Italic(string s) { +#line default +#line hidden +return new Template(__razor_helper_writer => { + +#line 1 "NestedHelpers.cshtml" + + s = s.ToUpper(); + + +#line default +#line hidden + +#line 6 "NestedHelpers.cshtml" + + + +#line default +#line hidden +WriteLiteralTo(__razor_helper_writer, " "); + + +#line 7 "NestedHelpers.cshtml" +WriteTo(__razor_helper_writer, Bold(s)); + + +#line default +#line hidden +WriteLiteralTo(__razor_helper_writer, "\r\n"); + + +#line 8 "NestedHelpers.cshtml" + + +#line default +#line hidden +}); + +#line 8 "NestedHelpers.cshtml" +} +#line default +#line hidden + +public NestedHelpers() { +} +public override void Execute() { +WriteLiteral("\r\n"); + + +#line 10 "NestedHelpers.cshtml" +Write(Italic("Hello")); + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/NoLinePragmas.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/NoLinePragmas.cs new file mode 100644 index 0000000000..3fd500a59b --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/NoLinePragmas.cs @@ -0,0 +1,102 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class NoLinePragmas { +#line hidden +public NoLinePragmas() { +} +public override void Execute() { + + int i = 1; + +WriteLiteral("\r\n\r\n"); + + while(i <= 10) { + +WriteLiteral("

    Hello from C#, #"); + + Write(i); + +WriteLiteral("

    \r\n"); + + i += 1; +} + +WriteLiteral("\r\n"); + + if(i == 11) { + +WriteLiteral("

    We wrote 10 lines!

    \r\n"); + +} + +WriteLiteral("\r\n"); + + switch(i) { + case 11: + +WriteLiteral("

    No really, we wrote 10 lines!

    \r\n"); + + break; + default: + +WriteLiteral("

    Actually, we didn\'t...

    \r\n"); + + break; +} + +WriteLiteral("\r\n"); + + for(int j = 1; j <= 10; j += 2) { + +WriteLiteral("

    Hello again from C#, #"); + + Write(j); + +WriteLiteral("

    \r\n"); + +} + +WriteLiteral("\r\n"); + + try { + +WriteLiteral("

    That time, we wrote 5 lines!

    \r\n"); + +} catch(Exception ex) { + +WriteLiteral("

    Oh no! An error occurred: "); + + Write(ex.Message); + +WriteLiteral("

    \r\n"); + +} + + + + +WriteLiteral("

    i is now "); + + Write(i); + +WriteLiteral("

    \r\n\r\n"); + + lock(new object()) { + +WriteLiteral("

    This block is locked, for your security!

    \r\n"); + +} +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/OpenedIf.DesignTime.Tabs.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/OpenedIf.DesignTime.Tabs.cs new file mode 100644 index 0000000000..c64677bbb9 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/OpenedIf.DesignTime.Tabs.cs @@ -0,0 +1,41 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class OpenedIf { +#line hidden +public OpenedIf() { +} +public override void Execute() { + +#line 1 "OpenedIf.cshtml" +if (true) { + + +#line default +#line hidden + +#line 2 "OpenedIf.cshtml" + + + +#line default +#line hidden + +#line 3 "OpenedIf.cshtml" + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/OpenedIf.DesignTime.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/OpenedIf.DesignTime.cs new file mode 100644 index 0000000000..d0f3774358 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/OpenedIf.DesignTime.cs @@ -0,0 +1,41 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class OpenedIf { +#line hidden +public OpenedIf() { +} +public override void Execute() { + +#line 1 "OpenedIf.cshtml" +if (true) { + + +#line default +#line hidden + +#line 2 "OpenedIf.cshtml" + + + +#line default +#line hidden + +#line 3 "OpenedIf.cshtml" + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ParserError.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ParserError.cs new file mode 100644 index 0000000000..79d25e62af --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ParserError.cs @@ -0,0 +1,31 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class ParserError { +#line hidden +public ParserError() { +} +public override void Execute() { + +#line 1 "ParserError.cshtml" + +/* +int i =10; +int j =20; +} + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/RazorComments.DesignTime.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/RazorComments.DesignTime.cs new file mode 100644 index 0000000000..dc7322289a --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/RazorComments.DesignTime.cs @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class RazorComments { +private static object @__o; +#line hidden +public RazorComments() { +} +public override void Execute() { + +#line 1 "RazorComments.cshtml" + + + +#line default +#line hidden + +#line 2 "RazorComments.cshtml" + + Exception foo = + +#line default +#line hidden + +#line 3 "RazorComments.cshtml" + null; + if(foo != null) { + throw foo; + } + + +#line default +#line hidden + +#line 4 "RazorComments.cshtml" + var bar = "@* bar *@"; + +#line default +#line hidden + +#line 5 "RazorComments.cshtml" + __o = bar; + + +#line default +#line hidden + +#line 6 "RazorComments.cshtml" +__o = a + +#line default +#line hidden + +#line 7 "RazorComments.cshtml" + b; + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/RazorComments.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/RazorComments.cs new file mode 100644 index 0000000000..72d45d6011 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/RazorComments.cs @@ -0,0 +1,83 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class RazorComments { +#line hidden +public RazorComments() { +} +public override void Execute() { +WriteLiteral("\r\n

    This should "); + +WriteLiteral(" be shown

    \r\n\r\n"); + + +#line 4 "RazorComments.cshtml" + + + +#line default +#line hidden + +#line 5 "RazorComments.cshtml" + + Exception foo = + +#line default +#line hidden + +#line 6 "RazorComments.cshtml" + null; + if(foo != null) { + throw foo; + } + + +#line default +#line hidden +WriteLiteral("\r\n\r\n"); + + +#line 12 "RazorComments.cshtml" + var bar = "@* bar *@"; + +#line default +#line hidden +WriteLiteral("\r\n

    But this should show the comment syntax: "); + + +#line 13 "RazorComments.cshtml" + Write(bar); + + +#line default +#line hidden +WriteLiteral("

    \r\n\r\n"); + + +#line 15 "RazorComments.cshtml" +Write(a + +#line default +#line hidden + +#line 15 "RazorComments.cshtml" + b); + + +#line default +#line hidden +WriteLiteral("\r\n"); + +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ResolveUrl.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ResolveUrl.cs new file mode 100644 index 0000000000..f99f1ec199 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ResolveUrl.cs @@ -0,0 +1,230 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class ResolveUrl { +#line hidden +public ResolveUrl() { +} +public override void Execute() { +WriteLiteral("(Href("~/Foo") +, 9), false) +); + +WriteLiteral(">Foo
    \r\n(Href("~/Products/") +, 34), false) + +#line 2 "ResolveUrl.cshtml" +, Tuple.Create(Tuple.Create("", 45), Tuple.Create(product.id + +#line default +#line hidden +, 45), false) +); + +WriteLiteral(">"); + + +#line 2 "ResolveUrl.cshtml" + Write(product.Name); + + +#line default +#line hidden +WriteLiteral("\r\n(Href("~/Products/") +, 86), false) + +#line 3 "ResolveUrl.cshtml" +, Tuple.Create(Tuple.Create("", 97), Tuple.Create(product.id + +#line default +#line hidden +, 97), false) +, Tuple.Create(Tuple.Create("", 108), Tuple.Create("/Detail", 108), true) +); + +WriteLiteral(">Details\r\n(Href("~/A+Really(Crazy),Url.Is:This/") +, 139), false) + +#line 4 "ResolveUrl.cshtml" +, Tuple.Create(Tuple.Create("", 169), Tuple.Create(product.id + +#line default +#line hidden +, 169), false) +, Tuple.Create(Tuple.Create("", 180), Tuple.Create("/Detail", 180), true) +); + +WriteLiteral(">Crazy Url!\r\n\r\n"); + + +#line 6 "ResolveUrl.cshtml" + + + +#line default +#line hidden +WriteLiteral(" "); + +WriteLiteral("\r\n (Href("~/Foo") +, 240), false) +); + +WriteLiteral(">Foo\r\n (Href("~/Products/") +, 273), false) + +#line 9 "ResolveUrl.cshtml" +, Tuple.Create(Tuple.Create("", 284), Tuple.Create(product.id + +#line default +#line hidden +, 284), false) +); + +WriteLiteral(">"); + + +#line 9 "ResolveUrl.cshtml" + Write(product.Name); + + +#line default +#line hidden +WriteLiteral("\r\n (Href("~/Products/") +, 333), false) + +#line 10 "ResolveUrl.cshtml" +, Tuple.Create(Tuple.Create("", 344), Tuple.Create(product.id + +#line default +#line hidden +, 344), false) +, Tuple.Create(Tuple.Create("", 355), Tuple.Create("/Detail", 355), true) +); + +WriteLiteral(">Details\r\n (Href("~/A+Really(Crazy),Url.Is:This/") +, 394), false) + +#line 11 "ResolveUrl.cshtml" +, Tuple.Create(Tuple.Create("", 424), Tuple.Create(product.id + +#line default +#line hidden +, 424), false) +, Tuple.Create(Tuple.Create("", 435), Tuple.Create("/Detail", 435), true) +); + +WriteLiteral(">Crazy Url!\r\n "); + +WriteLiteral("\r\n"); + + +#line 13 "ResolveUrl.cshtml" + + +#line default +#line hidden +WriteLiteral("\r\n\r\n"); + +DefineSection("Foo", () => { + +WriteLiteral("\r\n (Href("~/Foo") +, 507), false) +); + +WriteLiteral(">Foo\r\n (Href("~/Products/") +, 536), false) + +#line 17 "ResolveUrl.cshtml" +, Tuple.Create(Tuple.Create("", 547), Tuple.Create(product.id + +#line default +#line hidden +, 547), false) +); + +WriteLiteral(">"); + + +#line 17 "ResolveUrl.cshtml" + Write(product.Name); + + +#line default +#line hidden +WriteLiteral("\r\n (Href("~/Products/") +, 592), false) + +#line 18 "ResolveUrl.cshtml" +, Tuple.Create(Tuple.Create("", 603), Tuple.Create(product.id + +#line default +#line hidden +, 603), false) +, Tuple.Create(Tuple.Create("", 614), Tuple.Create("/Detail", 614), true) +); + +WriteLiteral(">Details\r\n (Href("~/A+Really(Crazy),Url.Is:This/") +, 649), false) + +#line 19 "ResolveUrl.cshtml" +, Tuple.Create(Tuple.Create("", 679), Tuple.Create(product.id + +#line default +#line hidden +, 679), false) +, Tuple.Create(Tuple.Create("", 690), Tuple.Create("/Detail", 690), true) +); + +WriteLiteral(">Crazy Url!\r\n"); + +}); + +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Sections.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Sections.cs new file mode 100644 index 0000000000..13f24b7344 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Sections.cs @@ -0,0 +1,45 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class Sections { +#line hidden +public Sections() { +} +public override void Execute() { + +#line 1 "Sections.cshtml" + + Layout = "_SectionTestLayout.cshtml" + + +#line default +#line hidden +WriteLiteral("\r\n\r\n
    This is in the Body>\r\n\r\n"); + +DefineSection("Section2", () => { + +WriteLiteral("\r\n
    This is in Section 2
    \r\n"); + +}); + +WriteLiteral("\r\n"); + +DefineSection("Section1", () => { + +WriteLiteral("\r\n
    This is in Section 1
    \r\n"); + +}); + +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/SimpleUnspacedIf.DesignTime.Tabs.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/SimpleUnspacedIf.DesignTime.Tabs.cs new file mode 100644 index 0000000000..3374bdbdab --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/SimpleUnspacedIf.DesignTime.Tabs.cs @@ -0,0 +1,36 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class SimpleUnspacedIf { +#line hidden +public SimpleUnspacedIf() { +} +public override void Execute() { + +#line 1 "SimpleUnspacedIf.cshtml" +if (true) +{ + + +#line default +#line hidden + +#line 2 "SimpleUnspacedIf.cshtml" + +} + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Templates.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Templates.cs new file mode 100644 index 0000000000..8131acf7ed --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Templates.cs @@ -0,0 +1,180 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class Templates { +#line hidden +#line 1 "Templates.cshtml" + + public HelperResult Repeat(int times, Func template) { + return new HelperResult((writer) => { + for(int i = 0; i < times; i++) { + ((HelperResult)template(i)).WriteTo(writer); + } + }); + } + +#line default +#line hidden + +public Templates() { +} +public override void Execute() { +WriteLiteral("\r\n"); + + +#line 11 "Templates.cshtml" + + Func foo = + +#line default +#line hidden +item => new Template(__razor_template_writer => { + +WriteLiteralTo(__razor_template_writer, "This works "); + + +#line 12 "Templates.cshtml" + WriteTo(__razor_template_writer, item); + + +#line default +#line hidden +WriteLiteralTo(__razor_template_writer, "!"); + +}) + +#line 12 "Templates.cshtml" + ; + + +#line default +#line hidden + +#line 13 "Templates.cshtml" +Write(foo("")); + + +#line default +#line hidden + +#line 13 "Templates.cshtml" + + + +#line default +#line hidden +WriteLiteral("\r\n\r\n
      \r\n"); + + +#line 17 "Templates.cshtml" +Write(Repeat(10, item => new Template(__razor_template_writer => { + + +#line default +#line hidden +WriteLiteralTo(__razor_template_writer, "
    • Item #"); + + +#line 17 "Templates.cshtml" +WriteTo(__razor_template_writer, item); + + +#line default +#line hidden +WriteLiteralTo(__razor_template_writer, "
    • "); + + +#line 17 "Templates.cshtml" + }))); + + +#line default +#line hidden +WriteLiteral("\r\n
    \r\n\r\n

    \r\n"); + + +#line 21 "Templates.cshtml" +Write(Repeat(10, + item => new Template(__razor_template_writer => { + + +#line default +#line hidden +WriteLiteralTo(__razor_template_writer, " This is line#"); + + +#line 22 "Templates.cshtml" +WriteTo(__razor_template_writer, item); + + +#line default +#line hidden +WriteLiteralTo(__razor_template_writer, " of markup
    \r\n"); + + +#line 23 "Templates.cshtml" +}))); + + +#line default +#line hidden +WriteLiteral("\r\n

    \r\n\r\n
      \r\n"); + +WriteLiteral(" "); + + +#line 27 "Templates.cshtml" +Write(Repeat(10, item => new Template(__razor_template_writer => { + + +#line default +#line hidden +WriteLiteralTo(__razor_template_writer, "
    • \r\n Item #"); + + +#line 28 "Templates.cshtml" +WriteTo(__razor_template_writer, item); + + +#line default +#line hidden +WriteLiteralTo(__razor_template_writer, "\r\n"); + + +#line 29 "Templates.cshtml" + + +#line default +#line hidden + +#line 29 "Templates.cshtml" + var parent = item; + +#line default +#line hidden +WriteLiteralTo(__razor_template_writer, "\r\n
        \r\n
      • Child Items... ?
      • \r\n "); + +WriteLiteralTo(__razor_template_writer, "\r\n
      \r\n
    • "); + + +#line 34 "Templates.cshtml" + }))); + + +#line default +#line hidden +WriteLiteral("\r\n
    "); + +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/UnfinishedExpressionInCode.Tabs.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/UnfinishedExpressionInCode.Tabs.cs new file mode 100644 index 0000000000..a5e36034a6 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/UnfinishedExpressionInCode.Tabs.cs @@ -0,0 +1,43 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class UnfinishedExpressionInCode { +private static object @__o; +#line hidden +public UnfinishedExpressionInCode() { +} +public override void Execute() { + +#line 1 "UnfinishedExpressionInCode.cshtml" + + + +#line default +#line hidden + +#line 2 "UnfinishedExpressionInCode.cshtml" +__o = DateTime.; + + +#line default +#line hidden + +#line 3 "UnfinishedExpressionInCode.cshtml" + + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/UnfinishedExpressionInCode.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/UnfinishedExpressionInCode.cs new file mode 100644 index 0000000000..2a469eeaa3 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/UnfinishedExpressionInCode.cs @@ -0,0 +1,43 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TestOutput { +using System; + +public class UnfinishedExpressionInCode { +private static object @__o; +#line hidden +public UnfinishedExpressionInCode() { +} +public override void Execute() { + +#line 1 "UnfinishedExpressionInCode.cshtml" + + + +#line default +#line hidden + +#line 2 "UnfinishedExpressionInCode.cshtml" +__o = DateTime.; + + +#line default +#line hidden + +#line 3 "UnfinishedExpressionInCode.cshtml" + + + +#line default +#line hidden +} +} +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Blocks.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Blocks.cshtml new file mode 100644 index 0000000000..8d27de89cc --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Blocks.cshtml @@ -0,0 +1,37 @@ +@{ + int i = 1; +} + +@while(i <= 10) { +

    Hello from C#, #@(i)

    + i += 1; +} + +@if(i == 11) { +

    We wrote 10 lines!

    +} + +@switch(i) { + case 11: +

    No really, we wrote 10 lines!

    + break; + default: +

    Actually, we didn't...

    + break; +} + +@for(int j = 1; j <= 10; j += 2) { +

    Hello again from C#, #@(j)

    +} + +@try { +

    That time, we wrote 5 lines!

    +} catch(Exception ex) { +

    Oh no! An error occurred: @(ex.Message)

    +} + +

    i is now @i

    + +@lock(new object()) { +

    This block is locked, for your security!

    +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/CodeBlock.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/CodeBlock.cshtml new file mode 100644 index 0000000000..1c78883a10 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/CodeBlock.cshtml @@ -0,0 +1,5 @@ +@{ + for(int i = 1; i <= 10; i++) { + Output.Write("

    Hello from C#, #" + i.ToString() + "

    "); + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/CodeBlockAtEOF.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/CodeBlockAtEOF.cshtml new file mode 100644 index 0000000000..38417d481c --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/CodeBlockAtEOF.cshtml @@ -0,0 +1 @@ +@{ \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ConditionalAttributes.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ConditionalAttributes.cshtml new file mode 100644 index 0000000000..be1a9c2079 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ConditionalAttributes.cshtml @@ -0,0 +1,15 @@ +@{ + var ch = true; + var cls = "bar"; + +

    +

    +

    + + +

    + + + + +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/DesignTime.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/DesignTime.cshtml new file mode 100644 index 0000000000..581b164dcb --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/DesignTime.cshtml @@ -0,0 +1,21 @@ +

    + +

    +@(Foo(Bar.Baz)) +@Foo(@

    Bar @baz Biz

    ) +

    + +@section Footer { +

    Foo

    + @bar +} + +@helper Foo() { + if(true) { +

    Foo

    + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/EmptyCodeBlock.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/EmptyCodeBlock.cshtml new file mode 100644 index 0000000000..0366199cd5 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/EmptyCodeBlock.cshtml @@ -0,0 +1,3 @@ +This is markup + +@{} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/EmptyExplicitExpression.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/EmptyExplicitExpression.cshtml new file mode 100644 index 0000000000..6790c7eba2 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/EmptyExplicitExpression.cshtml @@ -0,0 +1,3 @@ +This is markup + +@() \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/EmptyImplicitExpression.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/EmptyImplicitExpression.cshtml new file mode 100644 index 0000000000..021306da6b --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/EmptyImplicitExpression.cshtml @@ -0,0 +1,3 @@ +This is markup + +@! \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/EmptyImplicitExpressionInCode.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/EmptyImplicitExpressionInCode.cshtml new file mode 100644 index 0000000000..a1db8cd602 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/EmptyImplicitExpressionInCode.cshtml @@ -0,0 +1,3 @@ +@{ + @ +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ExplicitExpression.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ExplicitExpression.cshtml new file mode 100644 index 0000000000..10730f1114 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ExplicitExpression.cshtml @@ -0,0 +1 @@ +1 + 1 = @(1+1) \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ExplicitExpressionAtEOF.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ExplicitExpressionAtEOF.cshtml new file mode 100644 index 0000000000..a0fdfc9a21 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ExplicitExpressionAtEOF.cshtml @@ -0,0 +1,3 @@ +This is markup + +@( \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ExpressionsInCode.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ExpressionsInCode.cshtml new file mode 100644 index 0000000000..a4d4caa007 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ExpressionsInCode.cshtml @@ -0,0 +1,16 @@ +@{ + object foo = null; + string bar = "Foo"; +} + +@if(foo != null) { + @foo +} else { +

    Foo is Null!

    +} + +

    +@if(!String.IsNullOrEmpty(bar)) { + @(bar.Replace("F", "B")) +} +

    \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/FunctionsBlock.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/FunctionsBlock.cshtml new file mode 100644 index 0000000000..5d06b37224 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/FunctionsBlock.cshtml @@ -0,0 +1,12 @@ +@functions { + +} + +@functions { + Random _rand = new Random(); + private int RandomInt() { + return _rand.Next(); + } +} + +Here's a random number: @RandomInt() \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/FunctionsBlockMinimal.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/FunctionsBlockMinimal.cshtml new file mode 100644 index 0000000000..6b3de50b2c --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/FunctionsBlockMinimal.cshtml @@ -0,0 +1,7 @@ + + + @functions{ +string foo(string input) { + return input + "!"; +} +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/FunctionsBlock_Tabs.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/FunctionsBlock_Tabs.cshtml new file mode 100644 index 0000000000..14a280f136 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/FunctionsBlock_Tabs.cshtml @@ -0,0 +1,12 @@ +@functions { + +} + +@functions { + Random _rand = new Random(); + private int RandomInt() { + return _rand.Next(); + } +} + +Here's a random number: @RandomInt() \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Helpers.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Helpers.cshtml new file mode 100644 index 0000000000..7ad443fe9f --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Helpers.cshtml @@ -0,0 +1,11 @@ +@helper Bold(string s) { + s = s.ToUpper(); + @s +} + +@helper Italic(string s) { + s = s.ToUpper(); + @s +} + +@Bold("Hello") \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HelpersMissingCloseParen.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HelpersMissingCloseParen.cshtml new file mode 100644 index 0000000000..e787dea27f --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HelpersMissingCloseParen.cshtml @@ -0,0 +1,7 @@ +@helper Bold(string s) { + s = s.ToUpper(); + @s +} + +@helper Italic(string s +@Bold("Hello") \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HelpersMissingName.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HelpersMissingName.cshtml new file mode 100644 index 0000000000..504cbee6a4 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HelpersMissingName.cshtml @@ -0,0 +1 @@ +@helper \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HelpersMissingOpenBrace.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HelpersMissingOpenBrace.cshtml new file mode 100644 index 0000000000..b9702e38ac --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HelpersMissingOpenBrace.cshtml @@ -0,0 +1,7 @@ +@helper Bold(string s) { + s = s.ToUpper(); + @s +} + +@helper Italic(string s) +@Italic(s) \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HelpersMissingOpenParen.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HelpersMissingOpenParen.cshtml new file mode 100644 index 0000000000..f3825f7e99 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HelpersMissingOpenParen.cshtml @@ -0,0 +1,7 @@ +@helper Bold(string s) { + s = s.ToUpper(); + @s +} + +@helper Italic +@Bold("Hello") \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HiddenSpansInCode.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HiddenSpansInCode.cshtml new file mode 100644 index 0000000000..a6addbe97c --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HiddenSpansInCode.cshtml @@ -0,0 +1,3 @@ +@{ + @@Da +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HtmlCommentWithQuote_Double.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HtmlCommentWithQuote_Double.cshtml new file mode 100644 index 0000000000..6a99437910 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HtmlCommentWithQuote_Double.cshtml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HtmlCommentWithQuote_Single.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HtmlCommentWithQuote_Single.cshtml new file mode 100644 index 0000000000..dc1b21a546 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/HtmlCommentWithQuote_Single.cshtml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ImplicitExpression.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ImplicitExpression.cshtml new file mode 100644 index 0000000000..dcce7fa572 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ImplicitExpression.cshtml @@ -0,0 +1,3 @@ +@for(int i = 1; i <= 10; i++) { +

    This is item #@i

    +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ImplicitExpressionAtEOF.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ImplicitExpressionAtEOF.cshtml new file mode 100644 index 0000000000..365d20e003 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ImplicitExpressionAtEOF.cshtml @@ -0,0 +1,3 @@ +This is markup + +@ \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Imports.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Imports.cshtml new file mode 100644 index 0000000000..332e26f022 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Imports.cshtml @@ -0,0 +1,6 @@ +@using System.IO +@using Foo = System.Text.Encoding +@using System + +

    Path's full type name is @typeof(Path).FullName

    +

    Foo's actual full type name is @typeof(Foo).FullName

    \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Inherits.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Inherits.cshtml new file mode 100644 index 0000000000..b449937d8d --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Inherits.cshtml @@ -0,0 +1,3 @@ +@foo() + +@inherits foo.bar>.boz bar diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/InlineBlocks.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/InlineBlocks.cshtml new file mode 100644 index 0000000000..0a8b3d8f4c --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/InlineBlocks.cshtml @@ -0,0 +1,3 @@ +@helper Link(string link) { +
    +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Instrumented.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Instrumented.cshtml new file mode 100644 index 0000000000..4d9b03eb84 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Instrumented.cshtml @@ -0,0 +1,42 @@ +@helper Strong(string s) { + @s +} + +@{ + int i = 1; + var foo = @

    Bar

    ; + @:Hello, World +

    Hello, World

    +} + +@while(i <= 10) { +

    Hello from C#, #@(i)

    + i += 1; +} + +@if(i == 11) { +

    We wrote 10 lines!

    +} + +@switch(i) { + case 11: +

    No really, we wrote 10 lines!

    + break; + default: +

    Actually, we didn't...

    + break; +} + +@for(int j = 1; j <= 10; j += 2) { +

    Hello again from C#, #@(j)

    +} + +@try { +

    That time, we wrote 5 lines!

    +} catch(Exception ex) { +

    Oh no! An error occurred: @(ex.Message)

    +} + +@lock(new object()) { +

    This block is locked, for your security!

    +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/LayoutDirective.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/LayoutDirective.cshtml new file mode 100644 index 0000000000..155c550b8e --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/LayoutDirective.cshtml @@ -0,0 +1 @@ +@layout ~/Foo/Bar/Baz \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/MarkupInCodeBlock.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/MarkupInCodeBlock.cshtml new file mode 100644 index 0000000000..712ef42848 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/MarkupInCodeBlock.cshtml @@ -0,0 +1,5 @@ +@{ + for(int i = 1; i <= 10; i++) { +

    Hello from C#, #@(i.ToString())

    + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/NestedCodeBlocks.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/NestedCodeBlocks.cshtml new file mode 100644 index 0000000000..070875f5fa --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/NestedCodeBlocks.cshtml @@ -0,0 +1,4 @@ +@if(foo) { + @if(bar) { + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/NestedHelpers.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/NestedHelpers.cshtml new file mode 100644 index 0000000000..63158ff0eb --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/NestedHelpers.cshtml @@ -0,0 +1,10 @@ +@helper Italic(string s) { + s = s.ToUpper(); + @helper Bold(string s) { + s = s.ToUpper(); + @s + } + @Bold(s) +} + +@Italic("Hello") \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/NoLinePragmas.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/NoLinePragmas.cshtml new file mode 100644 index 0000000000..36e96c46b9 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/NoLinePragmas.cshtml @@ -0,0 +1,38 @@ +@{ + int i = 1; +} + +@while(i <= 10) { +

    Hello from C#, #@(i)

    + i += 1; +} + +@if(i == 11) { +

    We wrote 10 lines!

    +} + +@switch(i) { + case 11: +

    No really, we wrote 10 lines!

    + break; + default: +

    Actually, we didn't...

    + break; +} + +@for(int j = 1; j <= 10; j += 2) { +

    Hello again from C#, #@(j)

    +} + +@try { +

    That time, we wrote 5 lines!

    +} catch(Exception ex) { +

    Oh no! An error occurred: @(ex.Message)

    +} + +@* With has no equivalent in C# *@ +

    i is now @i

    + +@lock(new object()) { +

    This block is locked, for your security!

    +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/OpenedIf.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/OpenedIf.cshtml new file mode 100644 index 0000000000..33162eb90b --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/OpenedIf.cshtml @@ -0,0 +1,5 @@ + + +@if (true) { + + \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ParserError.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ParserError.cshtml new file mode 100644 index 0000000000..ab30e853fd --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ParserError.cshtml @@ -0,0 +1,5 @@ +@{ +/* +int i =10; +int j =20; +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/RazorComments.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/RazorComments.cshtml new file mode 100644 index 0000000000..bd4820f914 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/RazorComments.cshtml @@ -0,0 +1,15 @@ +@*This is not going to be rendered*@ +

    This should @* not *@ be shown

    + +@{ + @* throw new Exception("Oh no!") *@ + Exception foo = @* new Exception("Oh no!") *@ null; + if(foo != null) { + throw foo; + } +} + +@{ var bar = "@* bar *@"; } +

    But this should show the comment syntax: @bar

    + +@(a@**@b) diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ResolveUrl.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ResolveUrl.cshtml new file mode 100644 index 0000000000..fc8c77cf08 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ResolveUrl.cshtml @@ -0,0 +1,20 @@ +
    Foo +@product.Name +Details +Crazy Url! + +@{ + + Foo + @product.Name + Details + Crazy Url! + +} + +@section Foo { + Foo + @product.Name + Details + Crazy Url! +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Sections.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Sections.cshtml new file mode 100644 index 0000000000..1d8cbefe7d --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Sections.cshtml @@ -0,0 +1,13 @@ +@{ + Layout = "_SectionTestLayout.cshtml" +} + +
    This is in the Body> + +@section Section2 { +
    This is in Section 2
    +} + +@section Section1 { +
    This is in Section 1
    +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/SimpleUnspacedIf.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/SimpleUnspacedIf.cshtml new file mode 100644 index 0000000000..27f2750eb2 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/SimpleUnspacedIf.cshtml @@ -0,0 +1,4 @@ +@if (true) +{ +
    +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Templates.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Templates.cshtml new file mode 100644 index 0000000000..c9523f0bfa --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Templates.cshtml @@ -0,0 +1,35 @@ +@functions { + public HelperResult Repeat(int times, Func template) { + return new HelperResult((writer) => { + for(int i = 0; i < times; i++) { + ((HelperResult)template(i)).WriteTo(writer); + } + }); + } +} + +@{ + Func foo = @This works @item!; + @foo("") +} + +
      +@(Repeat(10, @
    • Item #@item
    • )) +
    + +

    +@Repeat(10, + @: This is line#@item of markup
    +) +

    + +
      + @Repeat(10, @
    • + Item #@item + @{var parent = item;} +
        +
      • Child Items... ?
      • + @*Repeat(10, @
      • Item #@(parent).@item
      • )*@ +
      +
    • ) +
    \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/UnfinishedExpressionInCode.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/UnfinishedExpressionInCode.cshtml new file mode 100644 index 0000000000..bf1d796466 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/UnfinishedExpressionInCode.cshtml @@ -0,0 +1,3 @@ +@{ +@DateTime. +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Blocks.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Blocks.vb new file mode 100644 index 0000000000..d0dc84d572 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Blocks.vb @@ -0,0 +1,255 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class Blocks +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() + +#ExternalSource("Blocks.vbhtml",1) + + Dim i as Integer = 1 + + +#End ExternalSource +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Blocks.vbhtml",5) + While i <= 10 + + +#End ExternalSource +WriteLiteral(" ") + +WriteLiteral("

    Hello from VB.Net, #") + + +#ExternalSource("Blocks.vbhtml",6) + Write(i) + + +#End ExternalSource +WriteLiteral("

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Blocks.vbhtml",7) + i += 1 +End While + + +#End ExternalSource +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Blocks.vbhtml",10) + If i = 11 Then + + +#End ExternalSource +WriteLiteral(" ") + +WriteLiteral("

    We wrote 10 lines!

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Blocks.vbhtml",12) +End If + + +#End ExternalSource +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Blocks.vbhtml",14) + Do + + +#End ExternalSource +WriteLiteral(" ") + +WriteLiteral("

    Hello again: ") + + +#ExternalSource("Blocks.vbhtml",15) + Write(i) + + +#End ExternalSource +WriteLiteral("

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Blocks.vbhtml",16) + i -= 1 +Loop While i > 0 + + +#End ExternalSource +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Blocks.vbhtml",19) + Select Case i + Case 11 + + +#End ExternalSource +WriteLiteral(" ") + +WriteLiteral("

    No really, we wrote 10 lines!

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Blocks.vbhtml",22) + Case Else + + +#End ExternalSource +WriteLiteral(" ") + +WriteLiteral("

    We wrote a bunch more lines too!

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Blocks.vbhtml",24) +End Select + + +#End ExternalSource +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Blocks.vbhtml",26) + For j as Integer = 1 to 10 Step 2 + + +#End ExternalSource +WriteLiteral(" ") + +WriteLiteral("

    Hello again from VB.Net, #") + + +#ExternalSource("Blocks.vbhtml",27) + Write(j) + + +#End ExternalSource +WriteLiteral("

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Blocks.vbhtml",28) +Next j + + +#End ExternalSource +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Blocks.vbhtml",30) + Try + + +#End ExternalSource +WriteLiteral(" ") + +WriteLiteral("

    That time, we wrote 5 lines!

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Blocks.vbhtml",32) +Catch ex as Exception + + +#End ExternalSource +WriteLiteral(" ") + +WriteLiteral("

    Oh no! An error occurred: ") + + +#ExternalSource("Blocks.vbhtml",33) + Write(ex.Message) + + +#End ExternalSource +WriteLiteral("

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Blocks.vbhtml",34) +End Try + + +#End ExternalSource +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Blocks.vbhtml",36) + With i + + +#End ExternalSource +WriteLiteral(" ") + +WriteLiteral("

    i is now ") + + +#ExternalSource("Blocks.vbhtml",37) + Write(.ToString()) + + +#End ExternalSource +WriteLiteral("

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Blocks.vbhtml",38) +End With + + +#End ExternalSource +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Blocks.vbhtml",40) + SyncLock New Object() + + +#End ExternalSource +WriteLiteral(" ") + +WriteLiteral("

    This block is locked, for your security!

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Blocks.vbhtml",42) +End SyncLock + + +#End ExternalSource +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Blocks.vbhtml",44) + Using New System.IO.MemoryStream() + + +#End ExternalSource +WriteLiteral(" ") + +WriteLiteral("

    Some random memory stream will be disposed after rendering this block

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Blocks.vbhtml",46) +End Using + +#End ExternalSource +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/CodeBlock.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/CodeBlock.vb new file mode 100644 index 0000000000..b77904b3c6 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/CodeBlock.vb @@ -0,0 +1,31 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class CodeBlock +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() + +#ExternalSource("CodeBlock.vbhtml",1) + + Test() + + +#End ExternalSource +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/CodeBlockAtEOF.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/CodeBlockAtEOF.vb new file mode 100644 index 0000000000..4550554d69 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/CodeBlockAtEOF.vb @@ -0,0 +1,29 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class CodeBlockAtEOF +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() + +#ExternalSource("CodeBlockAtEOF.vbhtml",1) + + +#End ExternalSource +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Comments.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Comments.vb new file mode 100644 index 0000000000..b6cabd32d3 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Comments.vb @@ -0,0 +1,51 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class Comments +Public Overrides Sub Execute() + + +#ExternalSource("Comments.vbhtml",1) + 'This is not going to be rendered + + +#End ExternalSource +WriteLiteral("

    This is going to be rendered

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + + +#ExternalSource("Comments.vbhtml",3) + REM Neither is this + + +#End ExternalSource + + +#ExternalSource("Comments.vbhtml",4) + rem nor this + + +#End ExternalSource + + +#ExternalSource("Comments.vbhtml",5) + rEm nor this + +#End ExternalSource + +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ConditionalAttributes.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ConditionalAttributes.vb new file mode 100644 index 0000000000..157413314c --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ConditionalAttributes.vb @@ -0,0 +1,187 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class ConditionalAttributes + +#ExternalSource("ConditionalAttributes.vbhtml", 1) +Public Shared Function Link(ByVal url As String, text As String) As Template + +#End ExternalSource +Return New Template(Sub (__razor_helper_writer) + +#ExternalSource("ConditionalAttributes.vbhtml", 1) + + + +#End ExternalSource +WriteLiteralTo(__razor_helper_writer, " ") + +WriteLiteralTo(__razor_helper_writer, "") + + +#ExternalSource("ConditionalAttributes.vbhtml", 2) +WriteTo(__razor_helper_writer, text) + + +#End ExternalSource +WriteLiteralTo(__razor_helper_writer, " "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("ConditionalAttributes.vbhtml", 3) + + +#End ExternalSource +End Sub) +End Function + +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() +WriteLiteral(" "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("ConditionalAttributes.vbhtml",5) + + Dim ch = True + Dim cls = "bar" + + +#End ExternalSource +WriteLiteral(" ") + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +WriteLiteral(" ") + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +WriteLiteral(" ") + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +WriteLiteral(" ") + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +WriteLiteral(" ") + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +WriteLiteral(" ") + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +WriteLiteral(" ") + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +WriteLiteral(" ") + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("ConditionalAttributes.vbhtml",16) + + +#End ExternalSource +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/DesignTime.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/DesignTime.vb new file mode 100644 index 0000000000..59041bd7c4 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/DesignTime.vb @@ -0,0 +1,101 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class DesignTime +Private Shared __o As Object + +#ExternalSource("DesignTime.vbhtml", 9) +Public Shared Function Foo() As Template + +#End ExternalSource +Return New Template(Sub (__razor_helper_writer) + +#ExternalSource("DesignTime.vbhtml", 10) + + If True Then + + +#End ExternalSource + +#ExternalSource("DesignTime.vbhtml", 11) + + End If + + +#End ExternalSource +End Sub) +End Function + +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() + +#ExternalSource("DesignTime.vbhtml",1) + For i = 1 to 10 + + +#End ExternalSource + +#ExternalSource("DesignTime.vbhtml",2) + __o = i + + +#End ExternalSource + +#ExternalSource("DesignTime.vbhtml",3) + + Next + + +#End ExternalSource + +#ExternalSource("DesignTime.vbhtml",4) +__o = Foo(Bar.Baz) + + +#End ExternalSource + +#ExternalSource("DesignTime.vbhtml",5) +__o = Foo(Function (item) New Template(Sub (__razor_template_writer) + + +#End ExternalSource + +#ExternalSource("DesignTime.vbhtml",6) + __o = baz + + +#End ExternalSource + +#ExternalSource("DesignTime.vbhtml",7) + End Sub)) + + +#End ExternalSource +DefineSection("Footer", Sub () + + +#ExternalSource("DesignTime.vbhtml",8) +__o = bar + + +#End ExternalSource +End Sub) + +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/EmptyExplicitExpression.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/EmptyExplicitExpression.vb new file mode 100644 index 0000000000..ce8fc4c1a3 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/EmptyExplicitExpression.vb @@ -0,0 +1,31 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class EmptyExplicitExpression +Private Shared __o As Object +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() + +#ExternalSource("EmptyExplicitExpression.vbhtml",1) +__o = + + +#End ExternalSource +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/EmptyImplicitExpression.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/EmptyImplicitExpression.vb new file mode 100644 index 0000000000..786c049dac --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/EmptyImplicitExpression.vb @@ -0,0 +1,31 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class EmptyImplicitExpression +Private Shared __o As Object +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() + +#ExternalSource("EmptyImplicitExpression.vbhtml",1) +__o = + + +#End ExternalSource +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/EmptyImplicitExpressionInCode.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/EmptyImplicitExpressionInCode.vb new file mode 100644 index 0000000000..9e4b2b1cf7 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/EmptyImplicitExpressionInCode.vb @@ -0,0 +1,43 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class EmptyImplicitExpressionInCode +Private Shared __o As Object +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() + +#ExternalSource("EmptyImplicitExpressionInCode.vbhtml",1) + + + +#End ExternalSource + +#ExternalSource("EmptyImplicitExpressionInCode.vbhtml",2) +__o = + + +#End ExternalSource + +#ExternalSource("EmptyImplicitExpressionInCode.vbhtml",3) + + + +#End ExternalSource +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/EmptySection.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/EmptySection.vb new file mode 100644 index 0000000000..be6bb88f4d --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/EmptySection.vb @@ -0,0 +1,30 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class EmptySection +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() +DefineSection("anything", Sub () + +WriteLiteral(" ") + +End Sub) + +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ExplicitExpression.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ExplicitExpression.vb new file mode 100644 index 0000000000..d2e7a9fdc8 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ExplicitExpression.vb @@ -0,0 +1,30 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class ExplicitExpression +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() + +#ExternalSource("ExplicitExpression.vbhtml",1) +Write(Foo(Bar.Baz)) + + +#End ExternalSource +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ExplicitExpressionAtEOF.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ExplicitExpressionAtEOF.vb new file mode 100644 index 0000000000..7fb5bd7777 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ExplicitExpressionAtEOF.vb @@ -0,0 +1,31 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class ExplicitExpressionAtEOF +Private Shared __o As Object +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() + +#ExternalSource("ExplicitExpressionAtEOF.vbhtml",1) +__o = + + +#End ExternalSource +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ExpressionsInCode.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ExpressionsInCode.vb new file mode 100644 index 0000000000..bbbd62b2a4 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ExpressionsInCode.vb @@ -0,0 +1,86 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class ExpressionsInCode +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() + +#ExternalSource("ExpressionsInCode.vbhtml",1) + + Dim foo As Object = Nothing + Dim bar as String = "Foo" + + +#End ExternalSource +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("ExpressionsInCode.vbhtml",6) + If foo IsNot Nothing Then + + +#End ExternalSource + +#ExternalSource("ExpressionsInCode.vbhtml",7) +Write(foo) + + +#End ExternalSource + +#ExternalSource("ExpressionsInCode.vbhtml",7) + +Else + + +#End ExternalSource +WriteLiteral(" ") + +WriteLiteral("

    Foo is Null!

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("ExpressionsInCode.vbhtml",10) +End If + + +#End ExternalSource +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&"

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("ExpressionsInCode.vbhtml",13) + If Not String.IsNullOrEmpty(bar) Then + + +#End ExternalSource + +#ExternalSource("ExpressionsInCode.vbhtml",14) +Write(bar.Replace("F", "B")) + + +#End ExternalSource + +#ExternalSource("ExpressionsInCode.vbhtml",14) + +End If + + +#End ExternalSource +WriteLiteral("

    ") + +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/FunctionsBlock.DesignTime.Tabs.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/FunctionsBlock.DesignTime.Tabs.vb new file mode 100644 index 0000000000..cd737ed9f6 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/FunctionsBlock.DesignTime.Tabs.vb @@ -0,0 +1,47 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class FunctionsBlock +Private Shared __o As Object + +#ExternalSource("FunctionsBlock.vbhtml",1) + + + +#End ExternalSource + +#ExternalSource("FunctionsBlock.vbhtml",2) + + Private _rand as New Random() + Private Function RandomInt() as Integer + Return _rand.Next() + End Function + +#End ExternalSource + +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() + +#ExternalSource("FunctionsBlock.vbhtml",3) + __o = RandomInt() + + +#End ExternalSource +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/FunctionsBlock.DesignTime.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/FunctionsBlock.DesignTime.vb new file mode 100644 index 0000000000..3ade673952 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/FunctionsBlock.DesignTime.vb @@ -0,0 +1,47 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class FunctionsBlock +Private Shared __o As Object + +#ExternalSource("FunctionsBlock.vbhtml",1) + + + +#End ExternalSource + +#ExternalSource("FunctionsBlock.vbhtml",2) + + Private _rand as New Random() + Private Function RandomInt() as Integer + Return _rand.Next() + End Function + +#End ExternalSource + +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() + +#ExternalSource("FunctionsBlock.vbhtml",3) + __o = RandomInt() + + +#End ExternalSource +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/FunctionsBlock.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/FunctionsBlock.vb new file mode 100644 index 0000000000..b14fedc3c1 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/FunctionsBlock.vb @@ -0,0 +1,50 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class FunctionsBlock + +#ExternalSource("FunctionsBlock.vbhtml",1) + + + +#End ExternalSource + +#ExternalSource("FunctionsBlock.vbhtml",5) + + Private _rand as New Random() + Private Function RandomInt() as Integer + Return _rand.Next() + End Function + +#End ExternalSource + +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&"Here's a random number: ") + + +#ExternalSource("FunctionsBlock.vbhtml",12) + Write(RandomInt()) + + +#End ExternalSource +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Helpers.Instance.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Helpers.Instance.vb new file mode 100644 index 0000000000..8442d9432d --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Helpers.Instance.vb @@ -0,0 +1,99 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class Helpers + +#ExternalSource("Helpers.vbhtml", 1) +Public Function Bold(s as String) As Template + +#End ExternalSource +Return New Template(Sub (__razor_helper_writer) + +#ExternalSource("Helpers.vbhtml", 1) + + s = s.ToUpper() + + +#End ExternalSource +WriteLiteralTo(__razor_helper_writer, " ") + +WriteLiteralTo(__razor_helper_writer, "") + + +#ExternalSource("Helpers.vbhtml", 3) +WriteTo(__razor_helper_writer, s) + + +#End ExternalSource +WriteLiteralTo(__razor_helper_writer, ""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Helpers.vbhtml", 4) + + +#End ExternalSource +End Sub) +End Function + +#ExternalSource("Helpers.vbhtml", 6) +Public Function Italic(s as String) As Template + +#End ExternalSource +Return New Template(Sub (__razor_helper_writer) + +#ExternalSource("Helpers.vbhtml", 6) + + s = s.ToUpper() + + +#End ExternalSource +WriteLiteralTo(__razor_helper_writer, " ") + +WriteLiteralTo(__razor_helper_writer, "") + + +#ExternalSource("Helpers.vbhtml", 8) +WriteTo(__razor_helper_writer, s) + + +#End ExternalSource +WriteLiteralTo(__razor_helper_writer, ""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Helpers.vbhtml", 9) + + +#End ExternalSource +End Sub) +End Function + +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Helpers.vbhtml",11) +Write(Bold("Hello")) + + +#End ExternalSource +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Helpers.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Helpers.vb new file mode 100644 index 0000000000..4a129fe467 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Helpers.vb @@ -0,0 +1,99 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class Helpers + +#ExternalSource("Helpers.vbhtml", 1) +Public Shared Function Bold(s as String) As Template + +#End ExternalSource +Return New Template(Sub (__razor_helper_writer) + +#ExternalSource("Helpers.vbhtml", 1) + + s = s.ToUpper() + + +#End ExternalSource +WriteLiteralTo(__razor_helper_writer, " ") + +WriteLiteralTo(__razor_helper_writer, "") + + +#ExternalSource("Helpers.vbhtml", 3) +WriteTo(__razor_helper_writer, s) + + +#End ExternalSource +WriteLiteralTo(__razor_helper_writer, ""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Helpers.vbhtml", 4) + + +#End ExternalSource +End Sub) +End Function + +#ExternalSource("Helpers.vbhtml", 6) +Public Shared Function Italic(s as String) As Template + +#End ExternalSource +Return New Template(Sub (__razor_helper_writer) + +#ExternalSource("Helpers.vbhtml", 6) + + s = s.ToUpper() + + +#End ExternalSource +WriteLiteralTo(__razor_helper_writer, " ") + +WriteLiteralTo(__razor_helper_writer, "") + + +#ExternalSource("Helpers.vbhtml", 8) +WriteTo(__razor_helper_writer, s) + + +#End ExternalSource +WriteLiteralTo(__razor_helper_writer, ""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Helpers.vbhtml", 9) + + +#End ExternalSource +End Sub) +End Function + +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Helpers.vbhtml",11) +Write(Bold("Hello")) + + +#End ExternalSource +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/HelpersMissingCloseParen.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/HelpersMissingCloseParen.vb new file mode 100644 index 0000000000..f9cc177410 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/HelpersMissingCloseParen.vb @@ -0,0 +1,66 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class HelpersMissingCloseParen + +#ExternalSource("HelpersMissingCloseParen.vbhtml", 1) +Public Shared Function Bold(s as String) As Template + +#End ExternalSource +Return New Template(Sub (__razor_helper_writer) + +#ExternalSource("HelpersMissingCloseParen.vbhtml", 1) + + s = s.ToUpper() + + +#End ExternalSource +WriteLiteralTo(__razor_helper_writer, " ") + +WriteLiteralTo(__razor_helper_writer, "") + + +#ExternalSource("HelpersMissingCloseParen.vbhtml", 3) +WriteTo(__razor_helper_writer, s) + + +#End ExternalSource +WriteLiteralTo(__razor_helper_writer, ""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("HelpersMissingCloseParen.vbhtml", 4) + + +#End ExternalSource +End Sub) +End Function + +#ExternalSource("HelpersMissingCloseParen.vbhtml", 6) +Public Shared Function Italic(s as String + +@Bold("Hello") +#End ExternalSource +End Function + +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/HelpersMissingName.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/HelpersMissingName.vb new file mode 100644 index 0000000000..79c3c00d0f --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/HelpersMissingName.vb @@ -0,0 +1,24 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class HelpersMissingName +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/HelpersMissingOpenParen.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/HelpersMissingOpenParen.vb new file mode 100644 index 0000000000..a199de01ec --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/HelpersMissingOpenParen.vb @@ -0,0 +1,72 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class HelpersMissingOpenParen + +#ExternalSource("HelpersMissingOpenParen.vbhtml", 1) +Public Shared Function Bold(s as String) As Template + +#End ExternalSource +Return New Template(Sub (__razor_helper_writer) + +#ExternalSource("HelpersMissingOpenParen.vbhtml", 1) + + s = s.ToUpper() + + +#End ExternalSource +WriteLiteralTo(__razor_helper_writer, " ") + +WriteLiteralTo(__razor_helper_writer, "") + + +#ExternalSource("HelpersMissingOpenParen.vbhtml", 3) +WriteTo(__razor_helper_writer, s) + + +#End ExternalSource +WriteLiteralTo(__razor_helper_writer, ""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("HelpersMissingOpenParen.vbhtml", 4) + + +#End ExternalSource +End Sub) +End Function + +#ExternalSource("HelpersMissingOpenParen.vbhtml", 6) +Public Shared Function Italic +#End ExternalSource +End Function + +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("HelpersMissingOpenParen.vbhtml",8) +Write(Bold("Hello")) + + +#End ExternalSource +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ImplicitExpression.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ImplicitExpression.vb new file mode 100644 index 0000000000..dd9e756fe9 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ImplicitExpression.vb @@ -0,0 +1,56 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class ImplicitExpression +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() + +#ExternalSource("ImplicitExpression.vbhtml",1) + For i = 1 To 10 + + +#End ExternalSource +WriteLiteral(" ") + +WriteLiteral("

    This is item #") + + +#ExternalSource("ImplicitExpression.vbhtml",2) + Write(i) + + +#End ExternalSource +WriteLiteral("

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("ImplicitExpression.vbhtml",3) +Next + + +#End ExternalSource +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("ImplicitExpression.vbhtml",5) +Write(SyntaxSampleHelpers.CodeForLink(Me)) + + +#End ExternalSource +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ImplicitExpressionAtEOF.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ImplicitExpressionAtEOF.vb new file mode 100644 index 0000000000..debff90d32 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ImplicitExpressionAtEOF.vb @@ -0,0 +1,31 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class ImplicitExpressionAtEOF +Private Shared __o As Object +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() + +#ExternalSource("ImplicitExpressionAtEOF.vbhtml",1) +__o = + + +#End ExternalSource +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Imports.DesignTime.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Imports.DesignTime.vb new file mode 100644 index 0000000000..5868d5ab1f --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Imports.DesignTime.vb @@ -0,0 +1,41 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports Foo = System.Text.Encoding + +Imports System +Imports System.IO + + +Namespace TestOutput +Public Class [Imports] +Private Shared __o As Object +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() + +#ExternalSource("Imports.vbhtml",4) + __o = GetType(Path).FullName + + +#End ExternalSource + +#ExternalSource("Imports.vbhtml",5) + __o = GetType(Foo).FullName + + +#End ExternalSource +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Imports.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Imports.vb new file mode 100644 index 0000000000..d35ce2ab4c --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Imports.vb @@ -0,0 +1,46 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports Foo = System.Text.Encoding + +Imports System +Imports System.IO + + +Namespace TestOutput +Public Class [Imports] +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&"

    Path's full type name is ") + + +#ExternalSource("Imports.vbhtml",5) + Write(GetType(Path).FullName) + + +#End ExternalSource +WriteLiteral("

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&"

    Foo's actual full type name is ") + + +#ExternalSource("Imports.vbhtml",6) + Write(GetType(Foo).FullName) + + +#End ExternalSource +WriteLiteral("

    ") + +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Inherits.Designtime.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Inherits.Designtime.vb new file mode 100644 index 0000000000..fc62324b67 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Inherits.Designtime.vb @@ -0,0 +1,35 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class [Inherits] +Inherits System.Web.WebPages.WebPage +Public Sub New() +MyBase.New +End Sub +Private Sub __RazorDesignTimeHelpers__() + + +#ExternalSource("Inherits.vbhtml",1) +Dim __inheritsHelper As System.Web.WebPages.WebPage = Nothing + + +#End ExternalSource + +End Sub +Public Overrides Sub Execute() +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Inherits.Runtime.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Inherits.Runtime.vb new file mode 100644 index 0000000000..1395dac155 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Inherits.Runtime.vb @@ -0,0 +1,25 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class [Inherits] +Inherits System.Web.WebPages.WebPage +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Instrumented.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Instrumented.vb new file mode 100644 index 0000000000..d800bbe45d --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Instrumented.vb @@ -0,0 +1,511 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class Instrumented + +#ExternalSource("Instrumented.vbhtml", 1) +Public Shared Function Strong(s As String) As Template + +#End ExternalSource +Return New Template(Sub (__razor_helper_writer) + +#ExternalSource("Instrumented.vbhtml", 1) + + + +#End ExternalSource +BeginContext(__razor_helper_writer, "~/Instrumented.vbhtml", 29, 4, true) + +WriteLiteralTo(__razor_helper_writer, " ") + +EndContext(__razor_helper_writer, "~/Instrumented.vbhtml", 29, 4, true) + +BeginContext(__razor_helper_writer, "~/Instrumented.vbhtml", 34, 8, true) + +WriteLiteralTo(__razor_helper_writer, "") + +EndContext(__razor_helper_writer, "~/Instrumented.vbhtml", 34, 8, true) + +BeginContext(__razor_helper_writer, "~/Instrumented.vbhtml", 43, 1, false) + + +#ExternalSource("Instrumented.vbhtml", 2) +WriteTo(__razor_helper_writer, s) + + +#End ExternalSource +EndContext(__razor_helper_writer, "~/Instrumented.vbhtml", 43, 1, false) + +BeginContext(__razor_helper_writer, "~/Instrumented.vbhtml", 44, 11, true) + +WriteLiteralTo(__razor_helper_writer, ""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +EndContext(__razor_helper_writer, "~/Instrumented.vbhtml", 44, 11, true) + + +#ExternalSource("Instrumented.vbhtml", 3) + + +#End ExternalSource +End Sub) +End Function + +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() +BeginContext("~/Instrumented.vbhtml", 65, 4, true) + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +EndContext("~/Instrumented.vbhtml", 65, 4, true) + + +#ExternalSource("Instrumented.vbhtml",5) + + Dim i As Integer = 1 + Dim foo = + +#End ExternalSource +Function (item) New Template(Sub (__razor_template_writer) + +BeginContext(__razor_template_writer, "~/Instrumented.vbhtml", 118, 12, true) + +WriteLiteralTo(__razor_template_writer, "

    Foo

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +EndContext(__razor_template_writer, "~/Instrumented.vbhtml", 118, 12, true) + +BeginContext("~/Instrumented.vbhtml", 130, 4, true) + +WriteLiteral(" ") + +EndContext("~/Instrumented.vbhtml", 130, 4, true) + +BeginContext("~/Instrumented.vbhtml", 136, 15, true) + +WriteLiteral("Hello, World!"&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +EndContext("~/Instrumented.vbhtml", 136, 15, true) + +BeginContext("~/Instrumented.vbhtml", 151, 4, true) + +WriteLiteral(" ") + +EndContext("~/Instrumented.vbhtml", 151, 4, true) + +BeginContext("~/Instrumented.vbhtml", 156, 22, true) + +WriteLiteral("

    Hello, World!

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +EndContext("~/Instrumented.vbhtml", 156, 22, true) + +End Sub) + +#ExternalSource("Instrumented.vbhtml",10) + + +#End ExternalSource +BeginContext("~/Instrumented.vbhtml", 186, 4, true) + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +EndContext("~/Instrumented.vbhtml", 186, 4, true) + + +#ExternalSource("Instrumented.vbhtml",12) + While i <= 10 + + +#End ExternalSource +BeginContext("~/Instrumented.vbhtml", 206, 4, true) + +WriteLiteral(" ") + +EndContext("~/Instrumented.vbhtml", 206, 4, true) + +BeginContext("~/Instrumented.vbhtml", 211, 23, true) + +WriteLiteral("

    Hello from VB.Net, #") + +EndContext("~/Instrumented.vbhtml", 211, 23, true) + +BeginContext("~/Instrumented.vbhtml", 236, 1, false) + + +#ExternalSource("Instrumented.vbhtml",13) + Write(i) + + +#End ExternalSource +EndContext("~/Instrumented.vbhtml", 236, 1, false) + +BeginContext("~/Instrumented.vbhtml", 238, 6, true) + +WriteLiteral("

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +EndContext("~/Instrumented.vbhtml", 238, 6, true) + + +#ExternalSource("Instrumented.vbhtml",14) + i += 1 +End While + + +#End ExternalSource +BeginContext("~/Instrumented.vbhtml", 267, 2, true) + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +EndContext("~/Instrumented.vbhtml", 267, 2, true) + + +#ExternalSource("Instrumented.vbhtml",17) + If i = 11 Then + + +#End ExternalSource +BeginContext("~/Instrumented.vbhtml", 286, 4, true) + +WriteLiteral(" ") + +EndContext("~/Instrumented.vbhtml", 286, 4, true) + +BeginContext("~/Instrumented.vbhtml", 291, 27, true) + +WriteLiteral("

    We wrote 10 lines!

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +EndContext("~/Instrumented.vbhtml", 291, 27, true) + + +#ExternalSource("Instrumented.vbhtml",19) +End If + + +#End ExternalSource +BeginContext("~/Instrumented.vbhtml", 326, 2, true) + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +EndContext("~/Instrumented.vbhtml", 326, 2, true) + + +#ExternalSource("Instrumented.vbhtml",21) + Do + + +#End ExternalSource +BeginContext("~/Instrumented.vbhtml", 333, 4, true) + +WriteLiteral(" ") + +EndContext("~/Instrumented.vbhtml", 333, 4, true) + +BeginContext("~/Instrumented.vbhtml", 338, 16, true) + +WriteLiteral("

    Hello again: ") + +EndContext("~/Instrumented.vbhtml", 338, 16, true) + +BeginContext("~/Instrumented.vbhtml", 355, 1, false) + + +#ExternalSource("Instrumented.vbhtml",22) + Write(i) + + +#End ExternalSource +EndContext("~/Instrumented.vbhtml", 355, 1, false) + +BeginContext("~/Instrumented.vbhtml", 356, 6, true) + +WriteLiteral("

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +EndContext("~/Instrumented.vbhtml", 356, 6, true) + + +#ExternalSource("Instrumented.vbhtml",23) + i -= 1 +Loop While i > 0 + + +#End ExternalSource +BeginContext("~/Instrumented.vbhtml", 392, 2, true) + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +EndContext("~/Instrumented.vbhtml", 392, 2, true) + + +#ExternalSource("Instrumented.vbhtml",26) + Select Case i + Case 11 + + +#End ExternalSource +BeginContext("~/Instrumented.vbhtml", 423, 8, true) + +WriteLiteral(" ") + +EndContext("~/Instrumented.vbhtml", 423, 8, true) + +BeginContext("~/Instrumented.vbhtml", 432, 38, true) + +WriteLiteral("

    No really, we wrote 10 lines!

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +EndContext("~/Instrumented.vbhtml", 432, 38, true) + + +#ExternalSource("Instrumented.vbhtml",29) + Case Else + + +#End ExternalSource +BeginContext("~/Instrumented.vbhtml", 485, 8, true) + +WriteLiteral(" ") + +EndContext("~/Instrumented.vbhtml", 485, 8, true) + +BeginContext("~/Instrumented.vbhtml", 494, 41, true) + +WriteLiteral("

    We wrote a bunch more lines too!

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +EndContext("~/Instrumented.vbhtml", 494, 41, true) + + +#ExternalSource("Instrumented.vbhtml",31) +End Select + + +#End ExternalSource +BeginContext("~/Instrumented.vbhtml", 547, 2, true) + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +EndContext("~/Instrumented.vbhtml", 547, 2, true) + + +#ExternalSource("Instrumented.vbhtml",33) + For j as Integer = 1 to 10 Step 2 + + +#End ExternalSource +BeginContext("~/Instrumented.vbhtml", 585, 4, true) + +WriteLiteral(" ") + +EndContext("~/Instrumented.vbhtml", 585, 4, true) + +BeginContext("~/Instrumented.vbhtml", 590, 29, true) + +WriteLiteral("

    Hello again from VB.Net, #") + +EndContext("~/Instrumented.vbhtml", 590, 29, true) + +BeginContext("~/Instrumented.vbhtml", 621, 1, false) + + +#ExternalSource("Instrumented.vbhtml",34) + Write(j) + + +#End ExternalSource +EndContext("~/Instrumented.vbhtml", 621, 1, false) + +BeginContext("~/Instrumented.vbhtml", 623, 6, true) + +WriteLiteral("

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +EndContext("~/Instrumented.vbhtml", 623, 6, true) + + +#ExternalSource("Instrumented.vbhtml",35) +Next j + + +#End ExternalSource +BeginContext("~/Instrumented.vbhtml", 637, 2, true) + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +EndContext("~/Instrumented.vbhtml", 637, 2, true) + + +#ExternalSource("Instrumented.vbhtml",37) + Try + + +#End ExternalSource +BeginContext("~/Instrumented.vbhtml", 645, 4, true) + +WriteLiteral(" ") + +EndContext("~/Instrumented.vbhtml", 645, 4, true) + +BeginContext("~/Instrumented.vbhtml", 650, 37, true) + +WriteLiteral("

    That time, we wrote 5 lines!

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +EndContext("~/Instrumented.vbhtml", 650, 37, true) + + +#ExternalSource("Instrumented.vbhtml",39) +Catch ex as Exception + + +#End ExternalSource +BeginContext("~/Instrumented.vbhtml", 710, 4, true) + +WriteLiteral(" ") + +EndContext("~/Instrumented.vbhtml", 710, 4, true) + +BeginContext("~/Instrumented.vbhtml", 715, 29, true) + +WriteLiteral("

    Oh no! An error occurred: ") + +EndContext("~/Instrumented.vbhtml", 715, 29, true) + +BeginContext("~/Instrumented.vbhtml", 746, 10, false) + + +#ExternalSource("Instrumented.vbhtml",40) + Write(ex.Message) + + +#End ExternalSource +EndContext("~/Instrumented.vbhtml", 746, 10, false) + +BeginContext("~/Instrumented.vbhtml", 757, 6, true) + +WriteLiteral("

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +EndContext("~/Instrumented.vbhtml", 757, 6, true) + + +#ExternalSource("Instrumented.vbhtml",41) +End Try + + +#End ExternalSource +BeginContext("~/Instrumented.vbhtml", 772, 2, true) + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +EndContext("~/Instrumented.vbhtml", 772, 2, true) + + +#ExternalSource("Instrumented.vbhtml",43) + With i + + +#End ExternalSource +BeginContext("~/Instrumented.vbhtml", 783, 4, true) + +WriteLiteral(" ") + +EndContext("~/Instrumented.vbhtml", 783, 4, true) + +BeginContext("~/Instrumented.vbhtml", 788, 12, true) + +WriteLiteral("

    i is now ") + +EndContext("~/Instrumented.vbhtml", 788, 12, true) + +BeginContext("~/Instrumented.vbhtml", 802, 11, false) + + +#ExternalSource("Instrumented.vbhtml",44) + Write(.ToString()) + + +#End ExternalSource +EndContext("~/Instrumented.vbhtml", 802, 11, false) + +BeginContext("~/Instrumented.vbhtml", 814, 6, true) + +WriteLiteral("

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +EndContext("~/Instrumented.vbhtml", 814, 6, true) + + +#ExternalSource("Instrumented.vbhtml",45) +End With + + +#End ExternalSource +BeginContext("~/Instrumented.vbhtml", 830, 2, true) + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +EndContext("~/Instrumented.vbhtml", 830, 2, true) + + +#ExternalSource("Instrumented.vbhtml",47) + SyncLock New Object() + + +#End ExternalSource +BeginContext("~/Instrumented.vbhtml", 856, 4, true) + +WriteLiteral(" ") + +EndContext("~/Instrumented.vbhtml", 856, 4, true) + +BeginContext("~/Instrumented.vbhtml", 861, 49, true) + +WriteLiteral("

    This block is locked, for your security!

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +EndContext("~/Instrumented.vbhtml", 861, 49, true) + + +#ExternalSource("Instrumented.vbhtml",49) +End SyncLock + + +#End ExternalSource +BeginContext("~/Instrumented.vbhtml", 924, 2, true) + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +EndContext("~/Instrumented.vbhtml", 924, 2, true) + + +#ExternalSource("Instrumented.vbhtml",51) + Using New System.IO.MemoryStream() + + +#End ExternalSource +BeginContext("~/Instrumented.vbhtml", 963, 4, true) + +WriteLiteral(" ") + +EndContext("~/Instrumented.vbhtml", 963, 4, true) + +BeginContext("~/Instrumented.vbhtml", 968, 78, true) + +WriteLiteral("

    Some random memory stream will be disposed after rendering this block

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +EndContext("~/Instrumented.vbhtml", 968, 78, true) + + +#ExternalSource("Instrumented.vbhtml",53) +End Using + +#End ExternalSource +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/LayoutDirective.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/LayoutDirective.vb new file mode 100644 index 0000000000..fb7bdbdd02 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/LayoutDirective.vb @@ -0,0 +1,25 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class LayoutDirective +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() +Layout = "~/Foo/Bar/Baz" +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/MarkupInCodeBlock.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/MarkupInCodeBlock.vb new file mode 100644 index 0000000000..c70fd0bf37 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/MarkupInCodeBlock.vb @@ -0,0 +1,51 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class MarkupInCodeBlock +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() + +#ExternalSource("MarkupInCodeBlock.vbhtml",1) + + For i = 1 To 10 + + +#End ExternalSource +WriteLiteral(" ") + +WriteLiteral("

    Hello from VB.Net, #") + + +#ExternalSource("MarkupInCodeBlock.vbhtml",3) + Write(i.ToString()) + + +#End ExternalSource +WriteLiteral("

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("MarkupInCodeBlock.vbhtml",4) + Next i + + +#End ExternalSource +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/NestedCodeBlocks.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/NestedCodeBlocks.vb new file mode 100644 index 0000000000..1ea3d48b2f --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/NestedCodeBlocks.vb @@ -0,0 +1,42 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class NestedCodeBlocks +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() + +#ExternalSource("NestedCodeBlocks.vbhtml",1) + If True Then + + +#End ExternalSource + +#ExternalSource("NestedCodeBlocks.vbhtml",2) + If True Then + End If + + +#End ExternalSource + +#ExternalSource("NestedCodeBlocks.vbhtml",4) +End If + +#End ExternalSource +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/NestedHelpers.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/NestedHelpers.vb new file mode 100644 index 0000000000..071708e84a --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/NestedHelpers.vb @@ -0,0 +1,103 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class NestedHelpers + +#ExternalSource("NestedHelpers.vbhtml", 3) +Public Shared Function Bold(s As String) As Template + +#End ExternalSource +Return New Template(Sub (__razor_helper_writer) + +#ExternalSource("NestedHelpers.vbhtml", 3) + + s = s.ToUpper() + + +#End ExternalSource +WriteLiteralTo(__razor_helper_writer, " ") + +WriteLiteralTo(__razor_helper_writer, "") + + +#ExternalSource("NestedHelpers.vbhtml", 5) +WriteTo(__razor_helper_writer, s) + + +#End ExternalSource +WriteLiteralTo(__razor_helper_writer, ""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("NestedHelpers.vbhtml", 6) + + +#End ExternalSource +End Sub) +End Function + +#ExternalSource("NestedHelpers.vbhtml", 1) +Public Shared Function Italic(s As String) As Template + +#End ExternalSource +Return New Template(Sub (__razor_helper_writer) + +#ExternalSource("NestedHelpers.vbhtml", 1) + + s = s.ToUpper() + + +#End ExternalSource + +#ExternalSource("NestedHelpers.vbhtml", 6) + + + +#End ExternalSource +WriteLiteralTo(__razor_helper_writer, " ") + +WriteLiteralTo(__razor_helper_writer, "") + + +#ExternalSource("NestedHelpers.vbhtml", 7) +WriteTo(__razor_helper_writer, Bold(s)) + + +#End ExternalSource +WriteLiteralTo(__razor_helper_writer, ""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("NestedHelpers.vbhtml", 8) + + +#End ExternalSource +End Sub) +End Function + +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("NestedHelpers.vbhtml",10) +Write(Italic("Hello")) + + +#End ExternalSource +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/NoLinePragmas.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/NoLinePragmas.vb new file mode 100644 index 0000000000..fea6867646 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/NoLinePragmas.vb @@ -0,0 +1,143 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class NoLinePragmas +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() + + Dim i as Integer = 1 + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + While i <= 10 + +WriteLiteral(" ") + +WriteLiteral("

    Hello from VB.Net, #") + + Write(i) + +WriteLiteral("

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + i += 1 + 'End While +End While + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + If i = 11 Then + +WriteLiteral(" ") + +WriteLiteral("

    We wrote 10 lines!

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + Dim s = "End If" +End If + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + Select Case i + Case 11 + +WriteLiteral(" ") + +WriteLiteral("

    No really, we wrote 10 lines!

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + Case Else + +WriteLiteral(" ") + +WriteLiteral("

    Actually, we didn't...

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +End Select + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + For j as Integer = 1 to 10 Step 2 + +WriteLiteral(" ") + +WriteLiteral("

    Hello again from VB.Net, #") + + Write(j) + +WriteLiteral("

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +Next + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + Try + +WriteLiteral(" ") + +WriteLiteral("

    That time, we wrote 5 lines!

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +Catch ex as Exception + +WriteLiteral(" ") + +WriteLiteral("

    Oh no! An error occurred: ") + + Write(ex.Message) + +WriteLiteral("

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +End Try + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + With i + +WriteLiteral(" ") + +WriteLiteral("

    i is now ") + + Write(.ToString()) + +WriteLiteral("

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +End With + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + SyncLock New Object() + +WriteLiteral(" ") + +WriteLiteral("

    This block is locked, for your security!

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +End SyncLock + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + Using New System.IO.MemoryStream() + +WriteLiteral(" ") + +WriteLiteral("

    Some random memory stream will be disposed after rendering this block

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +End Using + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +Write(SyntaxSampleHelpers.CodeForLink(Me)) + +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Options.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Options.vb new file mode 100644 index 0000000000..7f472d2020 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Options.vb @@ -0,0 +1,28 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit Off + +Imports System + +Namespace TestOutput +Public Class Options +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&"Hello, World!") + +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ParserError.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ParserError.vb new file mode 100644 index 0000000000..dc1c177b07 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ParserError.vb @@ -0,0 +1,31 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class ParserError +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() + +#ExternalSource("ParserError.vbhtml",1) + +Foo +'End Code + +#End ExternalSource +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/RazorComments.DesignTime.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/RazorComments.DesignTime.vb new file mode 100644 index 0000000000..925fc5bb62 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/RazorComments.DesignTime.vb @@ -0,0 +1,59 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class RazorComments +Private Shared __o As Object +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() + +#ExternalSource("RazorComments.vbhtml",1) + + + +#End ExternalSource + +#ExternalSource("RazorComments.vbhtml",2) + + + +#End ExternalSource + +#ExternalSource("RazorComments.vbhtml",3) + Dim bar As String = "@* bar *@" + +#End ExternalSource + +#ExternalSource("RazorComments.vbhtml",4) + __o = bar + + +#End ExternalSource + +#ExternalSource("RazorComments.vbhtml",5) +__o = a _ + +#End ExternalSource + +#ExternalSource("RazorComments.vbhtml",6) + b + + +#End ExternalSource +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/RazorComments.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/RazorComments.vb new file mode 100644 index 0000000000..909c96fe9f --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/RazorComments.vb @@ -0,0 +1,72 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class RazorComments +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&"

    This should ") + +WriteLiteral(" be shown

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("RazorComments.vbhtml",4) + + + +#End ExternalSource + +#ExternalSource("RazorComments.vbhtml",5) + + + +#End ExternalSource +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("RazorComments.vbhtml",8) + Dim bar As String = "@* bar *@" + +#End ExternalSource +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&"

    But this should show the comment syntax: ") + + +#ExternalSource("RazorComments.vbhtml",9) + Write(bar) + + +#End ExternalSource +WriteLiteral("

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&"

    So should this: ") + +WriteLiteral("@* bar *") + +WriteLiteral("@

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("RazorComments.vbhtml",12) +Write(a _ + +#End ExternalSource + +#ExternalSource("RazorComments.vbhtml",12) + b) + + +#End ExternalSource +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ResolveUrl.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ResolveUrl.vb new file mode 100644 index 0000000000..6e876b1dfc --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/ResolveUrl.vb @@ -0,0 +1,183 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class ResolveUrl +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() +WriteLiteral("Foo"&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&"") + + +#ExternalSource("ResolveUrl.vbhtml",2) + Write(product.Name) + + +#End ExternalSource +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&"Details"&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&"Crazy Url!"&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("ResolveUrl.vbhtml",6) + + + +#End ExternalSource +WriteLiteral(" ") + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&" Foo"&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&" ") + + +#ExternalSource("ResolveUrl.vbhtml",9) + Write(product.Name) + + +#End ExternalSource +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&" Details"&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&" Crazy Url!"&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&" ") + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("ResolveUrl.vbhtml",13) + + +#End ExternalSource +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +DefineSection("Foo", Sub () + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&" Foo"&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&" ") + + +#ExternalSource("ResolveUrl.vbhtml",17) + Write(product.Name) + + +#End ExternalSource +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&" Details"&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&" Crazy Url!"&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +End Sub) + +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Sections.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Sections.vb new file mode 100644 index 0000000000..8b9b34d0ff --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Sections.vb @@ -0,0 +1,47 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class Sections +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() + +#ExternalSource("Sections.vbhtml",1) + + Layout = "_SectionTestLayout.vbhtml" + + +#End ExternalSource +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&"
    This is in the Body>"&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +DefineSection("Section2", Sub () + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&"
    This is in Section 2
    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +End Sub) + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +DefineSection("Section1", Sub () + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&"
    This is in Section 1
    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +End Sub) + +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Templates.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Templates.vb new file mode 100644 index 0000000000..8d4bcdd3b2 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/Templates.vb @@ -0,0 +1,169 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System +Imports System.Web.Helpers + + +Namespace TestOutput +Public Class Templates + +#ExternalSource("Templates.vbhtml",3) + + Public Function Repeat(times As Integer, template As Func(Of Integer, object)) As HelperResult + Return New HelperResult(Sub(writer) + For i = 0 to times + DirectCast(template(i), HelperResult).WriteTo(writer) + Next i + End Sub) + End Function + +#End ExternalSource + +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Templates.vbhtml",13) + + Dim foo As Func(Of Object, Object) = + +#End ExternalSource +WriteLiteral(" ") + +WriteLiteral("This works ") + + +#ExternalSource("Templates.vbhtml",14) + Write(item) + + +#End ExternalSource +WriteLiteral("!") + +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Templates.vbhtml",15) + + +#End ExternalSource + +#ExternalSource("Templates.vbhtml",15) +Write(foo("too")) + + +#End ExternalSource + +#ExternalSource("Templates.vbhtml",15) + + + +#End ExternalSource +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&"
      "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Templates.vbhtml",19) +Write(Repeat(10, Function (item) New Template(Sub (__razor_template_writer) + + +#End ExternalSource +WriteLiteralTo(__razor_template_writer, "
    • Item #") + + +#ExternalSource("Templates.vbhtml",19) +WriteTo(__razor_template_writer, item) + + +#End ExternalSource +WriteLiteralTo(__razor_template_writer, "
    • ") + + +#ExternalSource("Templates.vbhtml",19) + End Sub))) + + +#End ExternalSource +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&"
    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&"

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Templates.vbhtml",23) +Write(Repeat(10, + Function (item) New Template(Sub (__razor_template_writer) + + +#End ExternalSource +WriteLiteralTo(__razor_template_writer, " This is line#") + + +#ExternalSource("Templates.vbhtml",24) +WriteTo(__razor_template_writer, item) + + +#End ExternalSource +WriteLiteralTo(__razor_template_writer, " of markup
    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Templates.vbhtml",25) +End Sub))) + + +#End ExternalSource +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&"

    "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&"
      "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + +WriteLiteral(" ") + + +#ExternalSource("Templates.vbhtml",29) +Write(Repeat(10, Function (item) New Template(Sub (__razor_template_writer) + + +#End ExternalSource +WriteLiteralTo(__razor_template_writer, "
    • "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&" Item #") + + +#ExternalSource("Templates.vbhtml",30) +WriteTo(__razor_template_writer, item) + + +#End ExternalSource +WriteLiteralTo(__razor_template_writer, ""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)) + + +#ExternalSource("Templates.vbhtml",31) + + +#End ExternalSource + +#ExternalSource("Templates.vbhtml",31) + Dim parent = item + +#End ExternalSource +WriteLiteralTo(__razor_template_writer, ""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&"
        "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&"
      • Child Items... ?
      • "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&"
      "&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&"
    • ") + + +#ExternalSource("Templates.vbhtml",35) + End Sub))) + + +#End ExternalSource +WriteLiteral(""&Global.Microsoft.VisualBasic.ChrW(13)&Global.Microsoft.VisualBasic.ChrW(10)&"
    ") + +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/UnfinishedExpressionInCode.vb b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/UnfinishedExpressionInCode.vb new file mode 100644 index 0000000000..a2cccc0fe8 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Output/UnfinishedExpressionInCode.vb @@ -0,0 +1,43 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:N.N.NNNNN.N +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict Off +Option Explicit On + +Imports System + +Namespace TestOutput +Public Class UnfinishedExpressionInCode +Private Shared __o As Object +Public Sub New() +MyBase.New +End Sub +Public Overrides Sub Execute() + +#ExternalSource("UnfinishedExpressionInCode.vbhtml",1) + + + +#End ExternalSource + +#ExternalSource("UnfinishedExpressionInCode.vbhtml",2) +__o = DateTime. + + +#End ExternalSource + +#ExternalSource("UnfinishedExpressionInCode.vbhtml",3) + + + +#End ExternalSource +End Sub +End Class +End Namespace diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Blocks.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Blocks.vbhtml new file mode 100644 index 0000000000..e2e02a6943 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Blocks.vbhtml @@ -0,0 +1,46 @@ +@Code + Dim i as Integer = 1 +End Code + +@While i <= 10 + @

    Hello from VB.Net, #@(i)

    + i += 1 +End While + +@If i = 11 Then + @

    We wrote 10 lines!

    +End If + +@Do + @

    Hello again: @i

    + i -= 1 +Loop While i > 0 + +@Select Case i + Case 11 + @

    No really, we wrote 10 lines!

    + Case Else + @

    We wrote a bunch more lines too!

    +End Select + +@For j as Integer = 1 to 10 Step 2 + @

    Hello again from VB.Net, #@(j)

    +Next j + +@Try + @

    That time, we wrote 5 lines!

    +Catch ex as Exception + @

    Oh no! An error occurred: @(ex.Message)

    +End Try + +@With i + @

    i is now @(.ToString())

    +End With + +@SyncLock New Object() + @

    This block is locked, for your security!

    +End SyncLock + +@Using New System.IO.MemoryStream() + @

    Some random memory stream will be disposed after rendering this block

    +End Using \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/CodeBlock.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/CodeBlock.vbhtml new file mode 100644 index 0000000000..c710eaf1cc --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/CodeBlock.vbhtml @@ -0,0 +1,3 @@ +@Code + Test() +End Code \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/CodeBlockAtEOF.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/CodeBlockAtEOF.vbhtml new file mode 100644 index 0000000000..011d09a1f4 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/CodeBlockAtEOF.vbhtml @@ -0,0 +1,3 @@ +This is markup + +@Code \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ConditionalAttributes.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ConditionalAttributes.vbhtml new file mode 100644 index 0000000000..ca77d2d792 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ConditionalAttributes.vbhtml @@ -0,0 +1,16 @@ +@Helper Link(ByVal url As String, text As String) + @@text +End Helper + +@Code + Dim ch = True + Dim cls = "bar" + @ + @

    + @

    + @

    + @ + @ + @

    + @ +End Code \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/DesignTime.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/DesignTime.vbhtml new file mode 100644 index 0000000000..cfd33e714d --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/DesignTime.vbhtml @@ -0,0 +1,21 @@ +

    + +

    +@(Foo(Bar.Baz)) +@Foo(@@

    Bar @baz Biz

    ) +

    + +@Section Footer +

    Foo

    + @bar +End Section + +@Helper Foo() + If True Then + @

    Foo

    + End If +End Helper \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/EmptyExplicitExpression.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/EmptyExplicitExpression.vbhtml new file mode 100644 index 0000000000..6790c7eba2 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/EmptyExplicitExpression.vbhtml @@ -0,0 +1,3 @@ +This is markup + +@() \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/EmptyImplicitExpression.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/EmptyImplicitExpression.vbhtml new file mode 100644 index 0000000000..021306da6b --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/EmptyImplicitExpression.vbhtml @@ -0,0 +1,3 @@ +This is markup + +@! \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/EmptyImplicitExpressionInCode.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/EmptyImplicitExpressionInCode.vbhtml new file mode 100644 index 0000000000..deb98d53ec --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/EmptyImplicitExpressionInCode.vbhtml @@ -0,0 +1,3 @@ +@Code + @ +End Code \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/EmptySection.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/EmptySection.vbhtml new file mode 100644 index 0000000000..c63cd4666a --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/EmptySection.vbhtml @@ -0,0 +1 @@ +@section anything End \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ExplicitExpression.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ExplicitExpression.vbhtml new file mode 100644 index 0000000000..b65eee5641 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ExplicitExpression.vbhtml @@ -0,0 +1 @@ +@(Foo(Bar.Baz)) \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ExplicitExpressionAtEOF.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ExplicitExpressionAtEOF.vbhtml new file mode 100644 index 0000000000..a0fdfc9a21 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ExplicitExpressionAtEOF.vbhtml @@ -0,0 +1,3 @@ +This is markup + +@( \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ExpressionsInCode.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ExpressionsInCode.vbhtml new file mode 100644 index 0000000000..3416ee9adf --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ExpressionsInCode.vbhtml @@ -0,0 +1,16 @@ +@Code + Dim foo As Object = Nothing + Dim bar as String = "Foo" +End Code + +@If foo IsNot Nothing Then + @foo +Else + @

    Foo is Null!

    +End If + +

    +@If Not String.IsNullOrEmpty(bar) Then + @(bar.Replace("F", "B")) +End If +

    \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/FunctionsBlock.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/FunctionsBlock.vbhtml new file mode 100644 index 0000000000..bcb78211ee --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/FunctionsBlock.vbhtml @@ -0,0 +1,12 @@ +@Functions + +End Functions + +@Functions + Private _rand as New Random() + Private Function RandomInt() as Integer + Return _rand.Next() + End Function +End Functions + +Here's a random number: @RandomInt() \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Helpers.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Helpers.vbhtml new file mode 100644 index 0000000000..6fead0cc77 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Helpers.vbhtml @@ -0,0 +1,11 @@ +@Helper Bold(s as String) + s = s.ToUpper() + @@s +End Helper + +@Helper Italic(s as String) + s = s.ToUpper() + @@s +End Helper + +@Bold("Hello") \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/HelpersMissingCloseParen.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/HelpersMissingCloseParen.vbhtml new file mode 100644 index 0000000000..d81d24b29e --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/HelpersMissingCloseParen.vbhtml @@ -0,0 +1,8 @@ +@Helper Bold(s as String) + s = s.ToUpper() + @@s +End Helper + +@Helper Italic(s as String + +@Bold("Hello") \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/HelpersMissingName.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/HelpersMissingName.vbhtml new file mode 100644 index 0000000000..36ec5a7ece --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/HelpersMissingName.vbhtml @@ -0,0 +1 @@ +@Helper \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/HelpersMissingOpenParen.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/HelpersMissingOpenParen.vbhtml new file mode 100644 index 0000000000..145bda6855 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/HelpersMissingOpenParen.vbhtml @@ -0,0 +1,8 @@ +@Helper Bold(s as String) + s = s.ToUpper() + @@s +End Helper + +@Helper Italic + +@Bold("Hello") \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ImplicitExpression.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ImplicitExpression.vbhtml new file mode 100644 index 0000000000..fb6f52816d --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ImplicitExpression.vbhtml @@ -0,0 +1,5 @@ +@For i = 1 To 10 + @

    This is item #@i

    +Next + +@SyntaxSampleHelpers.CodeForLink(Me) \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ImplicitExpressionAtEOF.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ImplicitExpressionAtEOF.vbhtml new file mode 100644 index 0000000000..365d20e003 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ImplicitExpressionAtEOF.vbhtml @@ -0,0 +1,3 @@ +This is markup + +@ \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Imports.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Imports.vbhtml new file mode 100644 index 0000000000..da3f0ec179 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Imports.vbhtml @@ -0,0 +1,6 @@ +@Imports System.IO +@Imports Foo = System.Text.Encoding +@Imports System + +

    Path's full type name is @GetType(Path).FullName

    +

    Foo's actual full type name is @GetType(Foo).FullName

    \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Inherits.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Inherits.vbhtml new file mode 100644 index 0000000000..cc0a2d2aa1 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Inherits.vbhtml @@ -0,0 +1 @@ +@Inherits System.Web.WebPages.WebPage diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Instrumented.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Instrumented.vbhtml new file mode 100644 index 0000000000..510aae4ba5 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Instrumented.vbhtml @@ -0,0 +1,53 @@ +@Helper Strong(s As String) + @@s +End Helper + +@Code + Dim i As Integer = 1 + Dim foo = @@

    Foo

    + @:Hello, World! + @

    Hello, World!

    +End Code + +@While i <= 10 + @

    Hello from VB.Net, #@(i)

    + i += 1 +End While + +@If i = 11 Then + @

    We wrote 10 lines!

    +End If + +@Do + @

    Hello again: @i

    + i -= 1 +Loop While i > 0 + +@Select Case i + Case 11 + @

    No really, we wrote 10 lines!

    + Case Else + @

    We wrote a bunch more lines too!

    +End Select + +@For j as Integer = 1 to 10 Step 2 + @

    Hello again from VB.Net, #@(j)

    +Next j + +@Try + @

    That time, we wrote 5 lines!

    +Catch ex as Exception + @

    Oh no! An error occurred: @(ex.Message)

    +End Try + +@With i + @

    i is now @(.ToString())

    +End With + +@SyncLock New Object() + @

    This block is locked, for your security!

    +End SyncLock + +@Using New System.IO.MemoryStream() + @

    Some random memory stream will be disposed after rendering this block

    +End Using \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/LayoutDirective.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/LayoutDirective.vbhtml new file mode 100644 index 0000000000..58904f11db --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/LayoutDirective.vbhtml @@ -0,0 +1 @@ +@Layout ~/Foo/Bar/Baz \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/MarkupInCodeBlock.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/MarkupInCodeBlock.vbhtml new file mode 100644 index 0000000000..832891345b --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/MarkupInCodeBlock.vbhtml @@ -0,0 +1,5 @@ +@Code + For i = 1 To 10 + @

    Hello from VB.Net, #@(i.ToString())

    + Next i +End Code diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/NestedCodeBlocks.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/NestedCodeBlocks.vbhtml new file mode 100644 index 0000000000..83f518ac19 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/NestedCodeBlocks.vbhtml @@ -0,0 +1,4 @@ +@If True Then + @If True Then + End If +End If \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/NestedHelpers.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/NestedHelpers.vbhtml new file mode 100644 index 0000000000..4c52502b91 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/NestedHelpers.vbhtml @@ -0,0 +1,10 @@ +@Helper Italic(s As String) + s = s.ToUpper() + @Helper Bold(s As String) + s = s.ToUpper() + @@s + End Helper + @@Bold(s) +End Helper + +@Italic("Hello") \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/NoLinePragmas.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/NoLinePragmas.vbhtml new file mode 100644 index 0000000000..4e9df78849 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/NoLinePragmas.vbhtml @@ -0,0 +1,45 @@ +@Code + Dim i as Integer = 1 +End Code + +@While i <= 10 + @

    Hello from VB.Net, #@(i)

    + i += 1 + 'End While +End While + +@If i = 11 Then + @

    We wrote 10 lines!

    + Dim s = "End If" +End If + +@Select Case i + Case 11 + @

    No really, we wrote 10 lines!

    + Case Else + @

    Actually, we didn't...

    +End Select + +@For j as Integer = 1 to 10 Step 2 + @

    Hello again from VB.Net, #@(j)

    +Next + +@Try + @

    That time, we wrote 5 lines!

    +Catch ex as Exception + @

    Oh no! An error occurred: @(ex.Message)

    +End Try + +@With i + @

    i is now @(.ToString())

    +End With + +@SyncLock New Object() + @

    This block is locked, for your security!

    +End SyncLock + +@Using New System.IO.MemoryStream() + @

    Some random memory stream will be disposed after rendering this block

    +End Using + +@SyntaxSampleHelpers.CodeForLink(Me) \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Options.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Options.vbhtml new file mode 100644 index 0000000000..0cd49aa337 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Options.vbhtml @@ -0,0 +1,4 @@ +@Option Strict On +@Option Explicit Off + +Hello, World! \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ParserError.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ParserError.vbhtml new file mode 100644 index 0000000000..2d2e4227db --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ParserError.vbhtml @@ -0,0 +1,3 @@ +@Code +Foo +'End Code \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/RazorComments.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/RazorComments.vbhtml new file mode 100644 index 0000000000..7c638d63b7 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/RazorComments.vbhtml @@ -0,0 +1,12 @@ +@*This is not going to be rendered*@ +

    This should @* not *@ be shown

    + +@Code + @* Throw new Exception("Oh no!") *@ +End Code + +@Code Dim bar As String = "@* bar *@" End Code +

    But this should show the comment syntax: @bar

    +

    So should this: @@* bar *@@

    + +@(a@**@b) \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ResolveUrl.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ResolveUrl.vbhtml new file mode 100644 index 0000000000..dc20f64488 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/ResolveUrl.vbhtml @@ -0,0 +1,20 @@ +
    Foo +@product.Name +Details +Crazy Url! + +@Code + @ + Foo + @product.Name + Details + Crazy Url! + +End Code + +@Section Foo + Foo + @product.Name + Details + Crazy Url! +End Section \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Sections.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Sections.vbhtml new file mode 100644 index 0000000000..ad05f5cd28 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Sections.vbhtml @@ -0,0 +1,13 @@ +@Code + Layout = "_SectionTestLayout.vbhtml" +End Code + +
    This is in the Body> + +@Section Section2 +
    This is in Section 2
    +End Section + +@Section Section1 +
    This is in Section 1
    +End Section \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Templates.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Templates.vbhtml new file mode 100644 index 0000000000..80e17703e5 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/Templates.vbhtml @@ -0,0 +1,36 @@ +@Imports System.Web.Helpers + +@Functions + Public Function Repeat(times As Integer, template As Func(Of Integer, object)) As HelperResult + Return New HelperResult(Sub(writer) + For i = 0 to times + DirectCast(template(i), HelperResult).WriteTo(writer) + Next i + End Sub) + End Function +End Functions + +@Code + Dim foo As Func(Of Object, Object) = @This works @item! + @foo("too") +End Code + +
      +@(Repeat(10, @@
    • Item #@item
    • )) +
    + +

    +@Repeat(10, + @@: This is line#@item of markup
    +) +

    + +
      + @Repeat(10, @@
    • + Item #@item + @Code Dim parent = item End Code +
        +
      • Child Items... ?
      • +
      +
    • ) +
    \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/UnfinishedExpressionInCode.vbhtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/UnfinishedExpressionInCode.vbhtml new file mode 100644 index 0000000000..4b4acb3b88 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/VB/Source/UnfinishedExpressionInCode.vbhtml @@ -0,0 +1,3 @@ +@Code +@DateTime. +End Code \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/DesignTime/Simple.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/DesignTime/Simple.cshtml new file mode 100644 index 0000000000..b50db22f77 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/DesignTime/Simple.cshtml @@ -0,0 +1,16 @@ +@{ + string hello = "Hello, World"; +} + + + + Simple Page + + +

    Simple Page

    +

    @hello

    +

    + @foreach(char c in hello) {@c} +

    + + \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/DesignTime/Simple.txt b/test/Microsoft.AspNet.Razor.Test/TestFiles/DesignTime/Simple.txt new file mode 100644 index 0000000000..4eaa232121 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/DesignTime/Simple.txt @@ -0,0 +1,60 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:N.N.NNNNN.N +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Razor { + + + public class @__CompiledTemplate { + + private static object @__o; + +#line hidden + + public @__CompiledTemplate() { + } + + public override void Execute() { + + #line 1 "C:\This\Path\Is\Just\For\Line\Pragmas.cshtml" + + string hello = "Hello, World"; + + + #line default + #line hidden + + #line 2 "C:\This\Path\Is\Just\For\Line\Pragmas.cshtml" + __o = hello; + + + #line default + #line hidden + + #line 3 "C:\This\Path\Is\Just\For\Line\Pragmas.cshtml" + foreach(char c in hello) { + + #line default + #line hidden + + #line 4 "C:\This\Path\Is\Just\For\Line\Pragmas.cshtml" + __o = c; + + + #line default + #line hidden + + #line 5 "C:\This\Path\Is\Just\For\Line\Pragmas.cshtml" + } + + #line default + #line hidden + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/nested-1000.html b/test/Microsoft.AspNet.Razor.Test/TestFiles/nested-1000.html new file mode 100644 index 0000000000..3c35bdbcbe --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/nested-1000.html @@ -0,0 +1,2002 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/Text/BufferingTextReaderTest.cs b/test/Microsoft.AspNet.Razor.Test/Text/BufferingTextReaderTest.cs new file mode 100644 index 0000000000..7af4f72f43 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Text/BufferingTextReaderTest.cs @@ -0,0 +1,267 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.IO; +using Microsoft.AspNet.Razor.Text; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Text +{ + public class BufferingTextReaderTest : LookaheadTextReaderTestBase + { + private const string TestString = "abcdefg"; + + private class DisposeTestMockTextReader : TextReader + { + public bool Disposed { get; set; } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + Disposed = true; + } + } + + protected override LookaheadTextReader CreateReader(string testString) + { + return new BufferingTextReader(new StringReader(testString)); + } + + [Fact] + public void ConstructorRequiresNonNullSourceReader() + { + Assert.ThrowsArgumentNull(() => new BufferingTextReader(null), "source"); + } + + [Fact] + public void PeekReturnsCurrentCharacterWithoutAdvancingPosition() + { + RunPeekTest("abc", peekAt: 2); + } + + [Fact] + public void PeekReturnsNegativeOneAtEndOfSourceReader() + { + RunPeekTest("abc", peekAt: 3); + } + + [Fact] + public void ReadReturnsCurrentCharacterAndAdvancesToNextCharacter() + { + RunReadTest("abc", readAt: 2); + } + + [Fact] + public void EndingLookaheadReturnsReaderToPreviousLocation() + { + RunLookaheadTest("abcdefg", "abcb", + Read, + Lookahead( + Read, + Read), + Read); + } + + [Fact] + public void MultipleLookaheadsCanBePerformed() + { + RunLookaheadTest("abcdefg", "abcbcdc", + Read, + Lookahead( + Read, + Read), + Read, + Lookahead( + Read, + Read), + Read); + } + + [Fact] + public void LookaheadsCanBeNested() + { + RunLookaheadTest("abcdefg", "abcdefebc", + Read, // Appended: "a" Reader: "bcdefg" + Lookahead( // Reader: "bcdefg" + Read, // Appended: "b" Reader: "cdefg"; + Read, // Appended: "c" Reader: "defg"; + Read, // Appended: "d" Reader: "efg"; + Lookahead( // Reader: "efg" + Read, // Appended: "e" Reader: "fg"; + Read // Appended: "f" Reader: "g"; + ), // Reader: "efg" + Read // Appended: "e" Reader: "fg"; + ), // Reader: "bcdefg" + Read, // Appended: "b" Reader: "cdefg"; + Read); // Appended: "c" Reader: "defg"; + } + + [Fact] + public void SourceLocationIsZeroWhenInitialized() + { + RunSourceLocationTest("abcdefg", SourceLocation.Zero, checkAt: 0); + } + + [Fact] + public void CharacterAndAbsoluteIndicesIncreaseAsCharactersAreRead() + { + RunSourceLocationTest("abcdefg", new SourceLocation(4, 0, 4), checkAt: 4); + } + + [Fact] + public void CharacterAndAbsoluteIndicesIncreaseAsSlashRInTwoCharacterNewlineIsRead() + { + RunSourceLocationTest("f\r\nb", new SourceLocation(2, 0, 2), checkAt: 2); + } + + [Fact] + public void CharacterIndexResetsToZeroAndLineIndexIncrementsWhenSlashNInTwoCharacterNewlineIsRead() + { + RunSourceLocationTest("f\r\nb", new SourceLocation(3, 1, 0), checkAt: 3); + } + + [Fact] + public void CharacterIndexResetsToZeroAndLineIndexIncrementsWhenSlashRInSingleCharacterNewlineIsRead() + { + RunSourceLocationTest("f\rb", new SourceLocation(2, 1, 0), checkAt: 2); + } + + [Fact] + public void CharacterIndexResetsToZeroAndLineIndexIncrementsWhenSlashNInSingleCharacterNewlineIsRead() + { + RunSourceLocationTest("f\nb", new SourceLocation(2, 1, 0), checkAt: 2); + } + + [Fact] + public void EndingLookaheadResetsRawCharacterAndLineIndexToValuesWhenLookaheadBegan() + { + RunEndLookaheadUpdatesSourceLocationTest(); + } + + [Fact] + public void OnceBufferingBeginsReadsCanContinuePastEndOfBuffer() + { + RunLookaheadTest("abcdefg", "abcbcdefg", + Read, + Lookahead(Read(2)), + Read(2), + ReadToEnd); + } + + [Fact] + public void DisposeDisposesSourceReader() + { + RunDisposeTest(r => r.Dispose()); + } + + [Fact] + public void CloseDisposesSourceReader() + { + RunDisposeTest(r => r.Close()); + } + + [Fact] + public void ReadWithBufferSupportsLookahead() + { + RunBufferReadTest((reader, buffer, index, count) => reader.Read(buffer, index, count)); + } + + [Fact] + public void ReadBlockSupportsLookahead() + { + RunBufferReadTest((reader, buffer, index, count) => reader.ReadBlock(buffer, index, count)); + } + + [Fact] + public void ReadLineSupportsLookahead() + { + RunReadUntilTest(r => r.ReadLine(), expectedRaw: 8, expectedChar: 0, expectedLine: 2); + } + + [Fact] + public void ReadToEndSupportsLookahead() + { + RunReadUntilTest(r => r.ReadToEnd(), expectedRaw: 11, expectedChar: 3, expectedLine: 2); + } + + [Fact] + public void ReadLineMaintainsCorrectCharacterPosition() + { + RunSourceLocationTest("abc\r\ndef", new SourceLocation(5, 1, 0), r => r.ReadLine()); + } + + [Fact] + public void ReadToEndWorksAsInNormalTextReader() + { + RunReadToEndTest(); + } + + [Fact] + public void CancelBacktrackStopsNextEndLookaheadFromBacktracking() + { + RunLookaheadTest("abcdefg", "abcdefg", + Lookahead( + Read(2), + CancelBacktrack + ), + ReadToEnd); + } + + [Fact] + public void CancelBacktrackThrowsInvalidOperationExceptionIfCalledOutsideOfLookahead() + { + RunCancelBacktrackOutsideLookaheadTest(); + } + + [Fact] + public void CancelBacktrackOnlyCancelsBacktrackingForInnermostNestedLookahead() + { + RunLookaheadTest("abcdefg", "abcdabcdefg", + Lookahead( + Read(2), + Lookahead( + Read, + CancelBacktrack + ), + Read + ), + ReadToEnd); + } + + [Fact] + public void BacktrackBufferIsClearedWhenEndReachedAndNoCurrentLookaheads() + { + // Arrange + StringReader source = new StringReader(TestString); + BufferingTextReader reader = new BufferingTextReader(source); + + reader.Read(); // Reader: "bcdefg" + using (reader.BeginLookahead()) + { + reader.Read(); // Reader: "cdefg" + } // Reader: "bcdefg" + reader.Read(); // Reader: "cdefg" + Assert.NotNull(reader.Buffer); // Verify our assumption that the buffer still exists + + // Act + reader.Read(); + + // Assert + Assert.False(reader.Buffering, "The buffer was not reset when the end was reached"); + Assert.Equal(0, reader.Buffer.Length); + } + + private static void RunDisposeTest(Action triggerAction) + { + // Arrange + DisposeTestMockTextReader source = new DisposeTestMockTextReader(); + LookaheadTextReader reader = new BufferingTextReader(source); + + // Act + triggerAction(reader); + + // Assert + Assert.True(source.Disposed); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Text/LineTrackingStringBufferTest.cs b/test/Microsoft.AspNet.Razor.Test/Text/LineTrackingStringBufferTest.cs new file mode 100644 index 0000000000..7b5b14e295 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Text/LineTrackingStringBufferTest.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Text; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Text +{ + public class LineTrackingStringBufferTest + { + [Fact] + public void CtorInitializesProperties() + { + LineTrackingStringBuffer buffer = new LineTrackingStringBuffer(); + Assert.Equal(0, buffer.Length); + } + + [Fact] + public void CharAtCorrectlyReturnsLocation() + { + LineTrackingStringBuffer buffer = new LineTrackingStringBuffer(); + buffer.Append("foo\rbar\nbaz\r\nbiz"); + LineTrackingStringBuffer.CharacterReference chr = buffer.CharAt(14); + Assert.Equal('i', chr.Character); + Assert.Equal(new SourceLocation(14, 3, 1), chr.Location); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Text/LookaheadTextReaderTestBase.cs b/test/Microsoft.AspNet.Razor.Test/Text/LookaheadTextReaderTestBase.cs new file mode 100644 index 0000000000..8fcea28ebb --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Text/LookaheadTextReaderTestBase.cs @@ -0,0 +1,255 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Text; +using Microsoft.AspNet.Razor.Resources; +using Microsoft.AspNet.Razor.Text; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Text +{ + public abstract class LookaheadTextReaderTestBase + { + protected abstract LookaheadTextReader CreateReader(string testString); + + protected void RunPeekTest(string input, int peekAt = 0) + { + RunPeekOrReadTest(input, peekAt, false); + } + + protected void RunReadTest(string input, int readAt = 0) + { + RunPeekOrReadTest(input, readAt, true); + } + + protected void RunSourceLocationTest(string input, SourceLocation expected, int checkAt = 0) + { + RunSourceLocationTest(input, expected, r => AdvanceReader(checkAt, r)); + } + + protected void RunSourceLocationTest(string input, SourceLocation expected, Action readerAction) + { + // Arrange + LookaheadTextReader reader = CreateReader(input); + readerAction(reader); + + // Act + SourceLocation actual = reader.CurrentLocation; + + // Assert + Assert.Equal(expected, actual); + } + + protected void RunEndLookaheadUpdatesSourceLocationTest() + { + SourceLocation? expectedLocation = null; + SourceLocation? actualLocation = null; + + RunLookaheadTest("abc\r\ndef\r\nghi", null, + Read(6), + CaptureSourceLocation(s => expectedLocation = s), + Lookahead(Read(6)), + CaptureSourceLocation(s => actualLocation = s)); + // Assert + Assert.Equal(expectedLocation.Value.AbsoluteIndex, actualLocation.Value.AbsoluteIndex); + Assert.Equal(expectedLocation.Value.CharacterIndex, actualLocation.Value.CharacterIndex); + Assert.Equal(expectedLocation.Value.LineIndex, actualLocation.Value.LineIndex); + } + + protected void RunReadToEndTest() + { + // Arrange + LookaheadTextReader reader = CreateReader("abcdefg"); + + // Act + string str = reader.ReadToEnd(); + + // Assert + Assert.Equal("abcdefg", str); + } + + protected void RunCancelBacktrackOutsideLookaheadTest() + { + // Arrange + LookaheadTextReader reader = CreateReader("abcdefg"); + + // Act and Assert + Assert.Throws(() => reader.CancelBacktrack(), RazorResources.CancelBacktrack_Must_Be_Called_Within_Lookahead); + } + + protected Action CaptureSourceLocation(Action capture) + { + return (_, reader) => { capture(reader.CurrentLocation); }; + } + + protected Action Read(int count) + { + return (builder, reader) => + { + for (int i = 0; i < count; i++) + { + Read(builder, reader); + } + }; + } + + protected void Read(StringBuilder builder, LookaheadTextReader reader) + { + builder.Append((char)reader.Read()); + } + + protected void ReadToEnd(StringBuilder builder, LookaheadTextReader reader) + { + builder.Append(reader.ReadToEnd()); + } + + protected void CancelBacktrack(StringBuilder builder, LookaheadTextReader reader) + { + reader.CancelBacktrack(); + } + + protected Action Lookahead(params Action[] readerCommands) + { + return (builder, reader) => + { + using (reader.BeginLookahead()) + { + RunAll(readerCommands, builder, reader); + } + }; + } + + protected void RunLookaheadTest(string input, string expected, params Action[] readerCommands) + { + // Arrange + StringBuilder builder = new StringBuilder(); + using (LookaheadTextReader reader = CreateReader(input)) + { + RunAll(readerCommands, builder, reader); + } + + if (expected != null) + { + Assert.Equal(expected, builder.ToString()); + } + } + + protected void RunReadUntilTest(Func readMethod, int expectedRaw, int expectedChar, int expectedLine) + { + // Arrange + LookaheadTextReader reader = CreateReader("a\r\nbcd\r\nefg"); + + reader.Read(); // Reader: "\r\nbcd\r\nefg" + reader.Read(); // Reader: "\nbcd\r\nefg" + reader.Read(); // Reader: "bcd\r\nefg" + + // Act + string read = null; + SourceLocation actualLocation; + using (reader.BeginLookahead()) + { + read = readMethod(reader); + actualLocation = reader.CurrentLocation; + } + + // Assert + Assert.Equal(3, reader.CurrentLocation.AbsoluteIndex); + Assert.Equal(0, reader.CurrentLocation.CharacterIndex); + Assert.Equal(1, reader.CurrentLocation.LineIndex); + Assert.Equal(expectedRaw, actualLocation.AbsoluteIndex); + Assert.Equal(expectedChar, actualLocation.CharacterIndex); + Assert.Equal(expectedLine, actualLocation.LineIndex); + Assert.Equal('b', reader.Peek()); + Assert.Equal(read, readMethod(reader)); + } + + protected void RunBufferReadTest(Func readMethod) + { + // Arrange + LookaheadTextReader reader = CreateReader("abcdefg"); + + reader.Read(); // Reader: "bcdefg" + + // Act + char[] buffer = new char[4]; + int read = -1; + SourceLocation actualLocation; + using (reader.BeginLookahead()) + { + read = readMethod(reader, buffer, 0, 4); + actualLocation = reader.CurrentLocation; + } + + // Assert + Assert.Equal("bcde", new String(buffer)); + Assert.Equal(4, read); + Assert.Equal(5, actualLocation.AbsoluteIndex); + Assert.Equal(5, actualLocation.CharacterIndex); + Assert.Equal(0, actualLocation.LineIndex); + Assert.Equal(1, reader.CurrentLocation.CharacterIndex); + Assert.Equal(0, reader.CurrentLocation.LineIndex); + Assert.Equal('b', reader.Peek()); + } + + private static void RunAll(Action[] readerCommands, StringBuilder builder, LookaheadTextReader reader) + { + foreach (Action readerCommand in readerCommands) + { + readerCommand(builder, reader); + } + } + + private void RunPeekOrReadTest(string input, int offset, bool isRead) + { + using (LookaheadTextReader reader = CreateReader(input)) + { + AdvanceReader(offset, reader); + + // Act + int? actual = null; + if (isRead) + { + actual = reader.Read(); + } + else + { + actual = reader.Peek(); + } + + Assert.NotNull(actual); + + // Asserts + AssertReaderValueCorrect(actual.Value, input, offset, "Peek"); + + if (isRead) + { + AssertReaderValueCorrect(reader.Peek(), input, offset + 1, "Read"); + } + else + { + Assert.Equal(actual, reader.Peek()); + } + } + } + + private static void AdvanceReader(int offset, LookaheadTextReader reader) + { + for (int i = 0; i < offset; i++) + { + reader.Read(); + } + } + + private void AssertReaderValueCorrect(int actual, string input, int expectedOffset, string methodName) + { + if (expectedOffset < input.Length) + { + Assert.Equal(input[expectedOffset], actual); + } + else + { + Assert.Equal(-1, actual); + } + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Text/SourceLocationTest.cs b/test/Microsoft.AspNet.Razor.Test/Text/SourceLocationTest.cs new file mode 100644 index 0000000000..07823f8d90 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Text/SourceLocationTest.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Text; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Text +{ + public class SourceLocationTest + { + [Fact] + public void ConstructorWithLineAndCharacterIndexSetsAssociatedProperties() + { + // Act + SourceLocation loc = new SourceLocation(0, 42, 24); + + // Assert + Assert.Equal(0, loc.AbsoluteIndex); + Assert.Equal(42, loc.LineIndex); + Assert.Equal(24, loc.CharacterIndex); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Text/SourceLocationTrackerTest.cs b/test/Microsoft.AspNet.Razor.Test/Text/SourceLocationTrackerTest.cs new file mode 100644 index 0000000000..010de8638f --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Text/SourceLocationTrackerTest.cs @@ -0,0 +1,196 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Text; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Text +{ + public class SourceLocationTrackerTest + { + private static readonly SourceLocation TestStartLocation = new SourceLocation(10, 42, 45); + + [Fact] + public void ConstructorSetsCurrentLocationToZero() + { + Assert.Equal(SourceLocation.Zero, new SourceLocationTracker().CurrentLocation); + } + + [Fact] + public void ConstructorWithSourceLocationSetsCurrentLocationToSpecifiedValue() + { + SourceLocation loc = new SourceLocation(10, 42, 4); + Assert.Equal(loc, new SourceLocationTracker(loc).CurrentLocation); + } + + [Fact] + public void UpdateLocationAdvancesCorrectlyForMultiLineString() + { + // Arrange + SourceLocationTracker tracker = new SourceLocationTracker(TestStartLocation); + + // Act + tracker.UpdateLocation("foo\nbar\rbaz\r\nbox"); + + // Assert + Assert.Equal(26, tracker.CurrentLocation.AbsoluteIndex); + Assert.Equal(45, tracker.CurrentLocation.LineIndex); + Assert.Equal(3, tracker.CurrentLocation.CharacterIndex); + } + + [Fact] + public void UpdateLocationAdvancesAbsoluteIndexOnNonNewlineCharacter() + { + // Arrange + SourceLocationTracker tracker = new SourceLocationTracker(TestStartLocation); + + // Act + tracker.UpdateLocation('f', 'o'); + + // Assert + Assert.Equal(11, tracker.CurrentLocation.AbsoluteIndex); + } + + [Fact] + public void UpdateLocationAdvancesCharacterIndexOnNonNewlineCharacter() + { + // Arrange + SourceLocationTracker tracker = new SourceLocationTracker(TestStartLocation); + + // Act + tracker.UpdateLocation('f', 'o'); + + // Assert + Assert.Equal(46, tracker.CurrentLocation.CharacterIndex); + } + + [Fact] + public void UpdateLocationDoesNotAdvanceLineIndexOnNonNewlineCharacter() + { + // Arrange + SourceLocationTracker tracker = new SourceLocationTracker(TestStartLocation); + + // Act + tracker.UpdateLocation('f', 'o'); + + // Assert + Assert.Equal(42, tracker.CurrentLocation.LineIndex); + } + + [Fact] + public void UpdateLocationAdvancesLineIndexOnSlashN() + { + // Arrange + SourceLocationTracker tracker = new SourceLocationTracker(TestStartLocation); + + // Act + tracker.UpdateLocation('\n', 'o'); + + // Assert + Assert.Equal(43, tracker.CurrentLocation.LineIndex); + } + + [Fact] + public void UpdateLocationAdvancesAbsoluteIndexOnSlashN() + { + // Arrange + SourceLocationTracker tracker = new SourceLocationTracker(TestStartLocation); + + // Act + tracker.UpdateLocation('\n', 'o'); + + // Assert + Assert.Equal(11, tracker.CurrentLocation.AbsoluteIndex); + } + + [Fact] + public void UpdateLocationResetsCharacterIndexOnSlashN() + { + // Arrange + SourceLocationTracker tracker = new SourceLocationTracker(TestStartLocation); + + // Act + tracker.UpdateLocation('\n', 'o'); + + // Assert + Assert.Equal(0, tracker.CurrentLocation.CharacterIndex); + } + + [Fact] + public void UpdateLocationAdvancesLineIndexOnSlashRFollowedByNonNewlineCharacter() + { + // Arrange + SourceLocationTracker tracker = new SourceLocationTracker(TestStartLocation); + + // Act + tracker.UpdateLocation('\r', 'o'); + + // Assert + Assert.Equal(43, tracker.CurrentLocation.LineIndex); + } + + [Fact] + public void UpdateLocationAdvancesAbsoluteIndexOnSlashRFollowedByNonNewlineCharacter() + { + // Arrange + SourceLocationTracker tracker = new SourceLocationTracker(TestStartLocation); + + // Act + tracker.UpdateLocation('\r', 'o'); + + // Assert + Assert.Equal(11, tracker.CurrentLocation.AbsoluteIndex); + } + + [Fact] + public void UpdateLocationResetsCharacterIndexOnSlashRFollowedByNonNewlineCharacter() + { + // Arrange + SourceLocationTracker tracker = new SourceLocationTracker(TestStartLocation); + + // Act + tracker.UpdateLocation('\r', 'o'); + + // Assert + Assert.Equal(0, tracker.CurrentLocation.CharacterIndex); + } + + [Fact] + public void UpdateLocationDoesNotAdvanceLineIndexOnSlashRFollowedBySlashN() + { + // Arrange + SourceLocationTracker tracker = new SourceLocationTracker(TestStartLocation); + + // Act + tracker.UpdateLocation('\r', '\n'); + + // Assert + Assert.Equal(42, tracker.CurrentLocation.LineIndex); + } + + [Fact] + public void UpdateLocationAdvancesAbsoluteIndexOnSlashRFollowedBySlashN() + { + // Arrange + SourceLocationTracker tracker = new SourceLocationTracker(TestStartLocation); + + // Act + tracker.UpdateLocation('\r', '\n'); + + // Assert + Assert.Equal(11, tracker.CurrentLocation.AbsoluteIndex); + } + + [Fact] + public void UpdateLocationAdvancesCharacterIndexOnSlashRFollowedBySlashN() + { + // Arrange + SourceLocationTracker tracker = new SourceLocationTracker(TestStartLocation); + + // Act + tracker.UpdateLocation('\r', '\n'); + + // Assert + Assert.Equal(46, tracker.CurrentLocation.CharacterIndex); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Text/TextBufferReaderTest.cs b/test/Microsoft.AspNet.Razor.Test/Text/TextBufferReaderTest.cs new file mode 100644 index 0000000000..f43464eff3 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Text/TextBufferReaderTest.cs @@ -0,0 +1,231 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Text; +using System.Web.WebPages.TestUtils; +using Microsoft.TestCommon; +using System; + +namespace Microsoft.AspNet.Razor.Test.Text +{ + public class TextBufferReaderTest : LookaheadTextReaderTestBase + { + protected override LookaheadTextReader CreateReader(string testString) + { + return new TextBufferReader(new StringTextBuffer(testString)); + } + + [Fact] + public void ConstructorRequiresNonNullTextBuffer() + { + Assert.ThrowsArgumentNull(() => new TextBufferReader(null), "buffer"); + } + + [Fact] + public void PeekReturnsCurrentCharacterWithoutAdvancingPosition() + { + RunPeekTest("abc", peekAt: 2); + } + + [Fact] + public void PeekReturnsNegativeOneAtEndOfSourceReader() + { + RunPeekTest("abc", peekAt: 3); + } + + [Fact] + public void ReadReturnsCurrentCharacterAndAdvancesToNextCharacter() + { + RunReadTest("abc", readAt: 2); + } + + [Fact] + public void EndingLookaheadReturnsReaderToPreviousLocation() + { + RunLookaheadTest("abcdefg", "abcb", + Read, + Lookahead( + Read, + Read), + Read); + } + + [Fact] + public void MultipleLookaheadsCanBePerformed() + { + RunLookaheadTest("abcdefg", "abcbcdc", + Read, + Lookahead( + Read, + Read), + Read, + Lookahead( + Read, + Read), + Read); + } + + [Fact] + public void LookaheadsCanBeNested() + { + RunLookaheadTest("abcdefg", "abcdefebc", + Read, // Appended: "a" Reader: "bcdefg" + Lookahead( // Reader: "bcdefg" + Read, // Appended: "b" Reader: "cdefg"; + Read, // Appended: "c" Reader: "defg"; + Read, // Appended: "d" Reader: "efg"; + Lookahead( // Reader: "efg" + Read, // Appended: "e" Reader: "fg"; + Read // Appended: "f" Reader: "g"; + ), // Reader: "efg" + Read // Appended: "e" Reader: "fg"; + ), // Reader: "bcdefg" + Read, // Appended: "b" Reader: "cdefg"; + Read); // Appended: "c" Reader: "defg"; + } + + [Fact] + public void SourceLocationIsZeroWhenInitialized() + { + RunSourceLocationTest("abcdefg", SourceLocation.Zero, checkAt: 0); + } + + [Fact] + public void CharacterAndAbsoluteIndicesIncreaseAsCharactersAreRead() + { + RunSourceLocationTest("abcdefg", new SourceLocation(4, 0, 4), checkAt: 4); + } + + [Fact] + public void CharacterAndAbsoluteIndicesIncreaseAsSlashRInTwoCharacterNewlineIsRead() + { + RunSourceLocationTest("f\r\nb", new SourceLocation(2, 0, 2), checkAt: 2); + } + + [Fact] + public void CharacterIndexResetsToZeroAndLineIndexIncrementsWhenSlashNInTwoCharacterNewlineIsRead() + { + RunSourceLocationTest("f\r\nb", new SourceLocation(3, 1, 0), checkAt: 3); + } + + [Fact] + public void CharacterIndexResetsToZeroAndLineIndexIncrementsWhenSlashRInSingleCharacterNewlineIsRead() + { + RunSourceLocationTest("f\rb", new SourceLocation(2, 1, 0), checkAt: 2); + } + + [Fact] + public void CharacterIndexResetsToZeroAndLineIndexIncrementsWhenSlashNInSingleCharacterNewlineIsRead() + { + RunSourceLocationTest("f\nb", new SourceLocation(2, 1, 0), checkAt: 2); + } + + [Fact] + public void EndingLookaheadResetsRawCharacterAndLineIndexToValuesWhenLookaheadBegan() + { + RunEndLookaheadUpdatesSourceLocationTest(); + } + + [Fact] + public void OnceBufferingBeginsReadsCanContinuePastEndOfBuffer() + { + RunLookaheadTest("abcdefg", "abcbcdefg", + Read, + Lookahead(Read(2)), + Read(2), + ReadToEnd); + } + + [Fact] + public void DisposeDisposesSourceReader() + { + RunDisposeTest(r => r.Dispose()); + } + + [Fact] + public void CloseDisposesSourceReader() + { + RunDisposeTest(r => r.Close()); + } + + [Fact] + public void ReadWithBufferSupportsLookahead() + { + RunBufferReadTest((reader, buffer, index, count) => reader.Read(buffer, index, count)); + } + + [Fact] + public void ReadBlockSupportsLookahead() + { + RunBufferReadTest((reader, buffer, index, count) => reader.ReadBlock(buffer, index, count)); + } + + [Fact] + public void ReadLineSupportsLookahead() + { + RunReadUntilTest(r => r.ReadLine(), expectedRaw: 8, expectedChar: 0, expectedLine: 2); + } + + [Fact] + public void ReadToEndSupportsLookahead() + { + RunReadUntilTest(r => r.ReadToEnd(), expectedRaw: 11, expectedChar: 3, expectedLine: 2); + } + + [Fact] + public void ReadLineMaintainsCorrectCharacterPosition() + { + RunSourceLocationTest("abc\r\ndef", new SourceLocation(5, 1, 0), r => r.ReadLine()); + } + + [Fact] + public void ReadToEndWorksAsInNormalTextReader() + { + RunReadToEndTest(); + } + + [Fact] + public void CancelBacktrackStopsNextEndLookaheadFromBacktracking() + { + RunLookaheadTest("abcdefg", "abcdefg", + Lookahead( + Read(2), + CancelBacktrack + ), + ReadToEnd); + } + + [Fact] + public void CancelBacktrackThrowsInvalidOperationExceptionIfCalledOutsideOfLookahead() + { + RunCancelBacktrackOutsideLookaheadTest(); + } + + [Fact] + public void CancelBacktrackOnlyCancelsBacktrackingForInnermostNestedLookahead() + { + RunLookaheadTest("abcdefg", "abcdabcdefg", + Lookahead( + Read(2), + Lookahead( + Read, + CancelBacktrack + ), + Read + ), + ReadToEnd); + } + + private static void RunDisposeTest(Action triggerAction) + { + // Arrange + StringTextBuffer source = new StringTextBuffer("abcdefg"); + LookaheadTextReader reader = new TextBufferReader(source); + + // Act + triggerAction(reader); + + // Assert + Assert.True(source.Disposed); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Text/TextChangeTest.cs b/test/Microsoft.AspNet.Razor.Test/Text/TextChangeTest.cs new file mode 100644 index 0000000000..3f974673af --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Text/TextChangeTest.cs @@ -0,0 +1,322 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Text; +using System.Web.WebPages.TestUtils; +using Microsoft.TestCommon; +using Moq; +using System; + +namespace Microsoft.AspNet.Razor.Test.Text +{ + public class TextChangeTest + { + [Fact] + public void ConstructorRequiresNonNegativeOldPosition() + { + Assert.ThrowsArgumentOutOfRange(() => new TextChange(-1, 0, new Mock().Object, 0, 0, new Mock().Object), "oldPosition", "Value must be greater than or equal to 0."); + } + + [Fact] + public void ConstructorRequiresNonNegativeNewPosition() + { + Assert.ThrowsArgumentOutOfRange(() => new TextChange(0, 0, new Mock().Object, -1, 0, new Mock().Object), "newPosition", "Value must be greater than or equal to 0."); + } + + [Fact] + public void ConstructorRequiresNonNegativeOldLength() + { + Assert.ThrowsArgumentOutOfRange(() => new TextChange(0, -1, new Mock().Object, 0, 0, new Mock().Object), "oldLength", "Value must be greater than or equal to 0."); + } + + [Fact] + public void ConstructorRequiresNonNegativeNewLength() + { + Assert.ThrowsArgumentOutOfRange(() => new TextChange(0, 0, new Mock().Object, 0, -1, new Mock().Object), "newLength", "Value must be greater than or equal to 0."); + } + + [Fact] + public void ConstructorRequiresNonNullOldBuffer() + { + Assert.ThrowsArgumentNull(() => new TextChange(0, 0, null, 0, 0, new Mock().Object), "oldBuffer"); + } + + [Fact] + public void ConstructorRequiresNonNullNewBuffer() + { + Assert.ThrowsArgumentNull(() => new TextChange(0, 0, new Mock().Object, 0, 0, null), "newBuffer"); + } + + [Fact] + public void ConstructorInitializesProperties() + { + // Act + ITextBuffer oldBuffer = new Mock().Object; + ITextBuffer newBuffer = new Mock().Object; + TextChange change = new TextChange(42, 24, oldBuffer, 1337, newBuffer); + + // Assert + Assert.Equal(42, change.OldPosition); + Assert.Equal(24, change.OldLength); + Assert.Equal(1337, change.NewLength); + Assert.Same(newBuffer, change.NewBuffer); + Assert.Same(oldBuffer, change.OldBuffer); + } + + [Fact] + public void TestIsDelete() + { + // Arrange + ITextBuffer oldBuffer = new Mock().Object; + ITextBuffer newBuffer = new Mock().Object; + TextChange change = new TextChange(0, 1, oldBuffer, 0, newBuffer); + + // Assert + Assert.True(change.IsDelete); + } + + [Fact] + public void TestDeleteCreatesTheRightSizeChange() + { + // Arrange + ITextBuffer oldBuffer = new Mock().Object; + ITextBuffer newBuffer = new Mock().Object; + TextChange change = new TextChange(0, 1, oldBuffer, 0, newBuffer); + + // Assert + Assert.Equal(0, change.NewText.Length); + Assert.Equal(1, change.OldText.Length); + } + + [Fact] + public void TestIsInsert() + { + // Arrange + ITextBuffer oldBuffer = new Mock().Object; + ITextBuffer newBuffer = new Mock().Object; + TextChange change = new TextChange(0, 0, oldBuffer, 35, newBuffer); + + // Assert + Assert.True(change.IsInsert); + } + + [Fact] + public void TestInsertCreateTheRightSizeChange() + { + // Arrange + ITextBuffer oldBuffer = new Mock().Object; + ITextBuffer newBuffer = new Mock().Object; + TextChange change = new TextChange(0, 0, oldBuffer, 1, newBuffer); + + // Assert + Assert.Equal(1, change.NewText.Length); + Assert.Equal(0, change.OldText.Length); + } + + [Fact] + public void TestIsReplace() + { + // Arrange + ITextBuffer oldBuffer = new Mock().Object; + ITextBuffer newBuffer = new Mock().Object; + TextChange change = new TextChange(0, 5, oldBuffer, 10, newBuffer); + + // Assert + Assert.True(change.IsReplace); + } + + [Fact] + public void ReplaceCreatesTheRightSizeChange() + { + // Arrange + ITextBuffer oldBuffer = new Mock().Object; + ITextBuffer newBuffer = new Mock().Object; + TextChange change = new TextChange(0, 5, oldBuffer, 10, newBuffer); + + // Assert + Assert.Equal(10, change.NewText.Length); + Assert.Equal(5, change.OldText.Length); + } + + [Fact] + public void ReplaceCreatesTheRightSizeChange1() + { + // Arrange + ITextBuffer oldBuffer = new Mock().Object; + ITextBuffer newBuffer = new Mock().Object; + TextChange change = new TextChange(0, 5, oldBuffer, 1, newBuffer); + + // Assert + Assert.Equal(1, change.NewText.Length); + Assert.Equal(5, change.OldText.Length); + } + + [Fact] + public void OldTextReturnsOldSpanFromOldBuffer() + { + // Arrange + var newBuffer = new StringTextBuffer("test"); + var oldBuffer = new StringTextBuffer("text"); + var textChange = new TextChange(2, 1, oldBuffer, 1, newBuffer); + + // Act + string text = textChange.OldText; + + // Assert + Assert.Equal("x", text); + } + + [Fact] + public void OldTextReturnsOldSpanFromOldBuffer2() + { + // Arrange + var newBuffer = new StringTextBuffer("test"); + var oldBuffer = new StringTextBuffer("text"); + var textChange = new TextChange(2, 2, oldBuffer, 1, newBuffer); + + // Act + string text = textChange.OldText; + + // Assert + Assert.Equal("xt", text); + } + + [Fact] + public void NewTextWithInsertReturnsChangedTextFromBuffer() + { + // Arrange + var newBuffer = new StringTextBuffer("test"); + var oldBuffer = new StringTextBuffer(""); + var textChange = new TextChange(0, 0, oldBuffer, 3, newBuffer); + + // Act + string text = textChange.NewText; + string oldText = textChange.OldText; + + // Assert + Assert.Equal("tes", text); + Assert.Equal("", oldText); + } + + [Fact] + public void NewTextWithDeleteReturnsEmptyString() + { + // Arrange + var newBuffer = new StringTextBuffer("test"); + var oldBuffer = new StringTextBuffer(""); + var textChange = new TextChange(1, 1, oldBuffer, 0, newBuffer); + + // Act + string text = textChange.NewText; + + // Assert + Assert.Equal(String.Empty, text); + } + + [Fact] + public void NewTextWithReplaceReturnsChangedTextFromBuffer() + { + // Arrange + var newBuffer = new StringTextBuffer("test"); + var oldBuffer = new StringTextBuffer("tebb"); + var textChange = new TextChange(2, 2, oldBuffer, 1, newBuffer); + + // Act + string newText = textChange.NewText; + string oldText = textChange.OldText; + + // Assert + Assert.Equal("s", newText); + Assert.Equal("bb", oldText); + } + + [Fact] + public void ApplyChangeWithInsertedTextReturnsNewContentWithChangeApplied() + { + // Arrange + var newBuffer = new StringTextBuffer("test"); + var oldBuffer = new StringTextBuffer(""); + var textChange = new TextChange(0, 0, oldBuffer, 3, newBuffer); + + // Act + string text = textChange.ApplyChange("abcd", 0); + + // Assert + Assert.Equal("tesabcd", text); + } + + [Fact] + public void ApplyChangeWithRemovedTextReturnsNewContentWithChangeApplied() + { + // Arrange + var newBuffer = new StringTextBuffer("abcdefg"); + var oldBuffer = new StringTextBuffer(""); + var textChange = new TextChange(1, 1, oldBuffer, 0, newBuffer); + + // Act + string text = textChange.ApplyChange("abcdefg", 1); + + // Assert + Assert.Equal("bcdefg", text); + } + + [Fact] + public void ApplyChangeWithReplacedTextReturnsNewContentWithChangeApplied() + { + // Arrange + var newBuffer = new StringTextBuffer("abcdefg"); + var oldBuffer = new StringTextBuffer(""); + var textChange = new TextChange(1, 1, oldBuffer, 2, newBuffer); + + // Act + string text = textChange.ApplyChange("abcdefg", 1); + + // Assert + Assert.Equal("bcbcdefg", text); + } + + [Fact] + public void NormalizeFixesUpIntelliSenseStyleReplacements() + { + // Arrange + var newBuffer = new StringTextBuffer("Date."); + var oldBuffer = new StringTextBuffer("Date"); + var original = new TextChange(0, 4, oldBuffer, 5, newBuffer); + + // Act + TextChange normalized = original.Normalize(); + + // Assert + Assert.Equal(new TextChange(4, 0, oldBuffer, 1, newBuffer), normalized); + } + + [Fact] + public void NormalizeDoesntAffectChangesWithoutCommonPrefixes() + { + // Arrange + var newBuffer = new StringTextBuffer("DateTime."); + var oldBuffer = new StringTextBuffer("Date."); + var original = new TextChange(0, 5, oldBuffer, 9, newBuffer); + + // Act + TextChange normalized = original.Normalize(); + + // Assert + Assert.Equal(original, normalized); + } + + [Fact] + public void NormalizeDoesntAffectShrinkingReplacements() + { + // Arrange + var newBuffer = new StringTextBuffer("D"); + var oldBuffer = new StringTextBuffer("DateTime"); + var original = new TextChange(0, 8, oldBuffer, 1, newBuffer); + + // Act + TextChange normalized = original.Normalize(); + + // Assert + Assert.Equal(original, normalized); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Text/TextReaderExtensionsTest.cs b/test/Microsoft.AspNet.Razor.Test/Text/TextReaderExtensionsTest.cs new file mode 100644 index 0000000000..a28dfbe680 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Text/TextReaderExtensionsTest.cs @@ -0,0 +1,186 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.IO; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Text +{ + public class TextReaderExtensionsTest + { + [Fact] + public void ReadUntilWithCharThrowsArgNullIfReaderNull() + { + Assert.ThrowsArgumentNull(() => TextReaderExtensions.ReadUntil(null, '@'), "reader"); + } + + [Fact] + public void ReadUntilInclusiveWithCharThrowsArgNullIfReaderNull() + { + Assert.ThrowsArgumentNull(() => TextReaderExtensions.ReadUntil(null, '@', inclusive: true), "reader"); + } + + [Fact] + public void ReadUntilWithMultipleTerminatorsThrowsArgNullIfReaderNull() + { + Assert.ThrowsArgumentNull(() => TextReaderExtensions.ReadUntil(null, '/', '>'), "reader"); + } + + [Fact] + public void ReadUntilInclusiveWithMultipleTerminatorsThrowsArgNullIfReaderNull() + { + // NOTE: Using named parameters would be difficult here, hence the inline comment + Assert.ThrowsArgumentNull(() => TextReaderExtensions.ReadUntil(null, /* inclusive */ true, '/', '>'), "reader"); + } + + [Fact] + public void ReadUntilWithPredicateThrowsArgNullIfReaderNull() + { + Assert.ThrowsArgumentNull(() => TextReaderExtensions.ReadUntil(null, c => true), "reader"); + } + + [Fact] + public void ReadUntilInclusiveWithPredicateThrowsArgNullIfReaderNull() + { + Assert.ThrowsArgumentNull(() => TextReaderExtensions.ReadUntil(null, c => true, inclusive: true), "reader"); + } + + [Fact] + public void ReadUntilWithPredicateThrowsArgExceptionIfPredicateNull() + { + Assert.ThrowsArgumentNull(() => TextReaderExtensions.ReadUntil(new StringReader("Foo"), (Predicate)null), "condition"); + } + + [Fact] + public void ReadUntilInclusiveWithPredicateThrowsArgExceptionIfPredicateNull() + { + Assert.ThrowsArgumentNull(() => TextReaderExtensions.ReadUntil(new StringReader("Foo"), (Predicate)null, inclusive: true), "condition"); + } + + [Fact] + public void ReadWhileWithPredicateThrowsArgNullIfReaderNull() + { + Assert.ThrowsArgumentNull(() => TextReaderExtensions.ReadWhile(null, c => true), "reader"); + } + + [Fact] + public void ReadWhileInclusiveWithPredicateThrowsArgNullIfReaderNull() + { + Assert.ThrowsArgumentNull(() => TextReaderExtensions.ReadWhile(null, c => true, inclusive: true), "reader"); + } + + [Fact] + public void ReadWhileWithPredicateThrowsArgNullIfPredicateNull() + { + Assert.ThrowsArgumentNull(() => TextReaderExtensions.ReadWhile(new StringReader("Foo"), (Predicate)null), "condition"); + } + + [Fact] + public void ReadWhileInclusiveWithPredicateThrowsArgNullIfPredicateNull() + { + Assert.ThrowsArgumentNull(() => TextReaderExtensions.ReadWhile(new StringReader("Foo"), (Predicate)null, inclusive: true), "condition"); + } + + [Fact] + public void ReadUntilWithCharReadsAllTextUpToSpecifiedCharacterButNotPast() + { + RunReaderTest("foo bar baz @biz", "foo bar baz ", '@', r => r.ReadUntil('@')); + } + + [Fact] + public void ReadUntilWithCharWithInclusiveFlagReadsAllTextUpToSpecifiedCharacterButNotPastIfInclusiveFalse() + { + RunReaderTest("foo bar baz @biz", "foo bar baz ", '@', r => r.ReadUntil('@', inclusive: false)); + } + + [Fact] + public void ReadUntilWithCharWithInclusiveFlagReadsAllTextUpToAndIncludingSpecifiedCharacterIfInclusiveTrue() + { + RunReaderTest("foo bar baz @biz", "foo bar baz @", 'b', r => r.ReadUntil('@', inclusive: true)); + } + + [Fact] + public void ReadUntilWithCharReadsToEndIfSpecifiedCharacterNotFound() + { + RunReaderTest("foo bar baz", "foo bar baz", -1, r => r.ReadUntil('@')); + } + + [Fact] + public void ReadUntilWithMultipleTerminatorsReadsUntilAnyTerminatorIsFound() + { + RunReaderTest("", " r.ReadUntil('/', '>')); + } + + [Fact] + public void ReadUntilWithMultipleTerminatorsHonorsInclusiveFlagWhenFalse() + { + // NOTE: Using named parameters would be difficult here, hence the inline comment + RunReaderTest("", " r.ReadUntil(/* inclusive */ false, '/', '>')); + } + + [Fact] + public void ReadUntilWithMultipleTerminatorsHonorsInclusiveFlagWhenTrue() + { + // NOTE: Using named parameters would be difficult here, hence the inline comment + RunReaderTest("", "', r => r.ReadUntil(/* inclusive */ true, '/', '>')); + } + + [Fact] + public void ReadUntilWithPredicateStopsWhenPredicateIsTrue() + { + RunReaderTest("foo bar baz 0 zoop zork zoink", "foo bar baz ", '0', r => r.ReadUntil(c => Char.IsDigit(c))); + } + + [Fact] + public void ReadUntilWithPredicateHonorsInclusiveFlagWhenFalse() + { + RunReaderTest("foo bar baz 0 zoop zork zoink", "foo bar baz ", '0', r => r.ReadUntil(c => Char.IsDigit(c), inclusive: false)); + } + + [Fact] + public void ReadUntilWithPredicateHonorsInclusiveFlagWhenTrue() + { + RunReaderTest("foo bar baz 0 zoop zork zoink", "foo bar baz 0", ' ', r => r.ReadUntil(c => Char.IsDigit(c), inclusive: true)); + } + + [Fact] + public void ReadWhileWithPredicateStopsWhenPredicateIsFalse() + { + RunReaderTest("012345a67890", "012345", 'a', r => r.ReadWhile(c => Char.IsDigit(c))); + } + + [Fact] + public void ReadWhileWithPredicateHonorsInclusiveFlagWhenFalse() + { + RunReaderTest("012345a67890", "012345", 'a', r => r.ReadWhile(c => Char.IsDigit(c), inclusive: false)); + } + + [Fact] + public void ReadWhileWithPredicateHonorsInclusiveFlagWhenTrue() + { + RunReaderTest("012345a67890", "012345a", '6', r => r.ReadWhile(c => Char.IsDigit(c), inclusive: true)); + } + + private static void RunReaderTest(string testString, string expectedOutput, int expectedPeek, Func action) + { + // Arrange + StringReader reader = new StringReader(testString); + + // Act + string read = action(reader); + + // Assert + Assert.Equal(expectedOutput, read); + + if (expectedPeek == -1) + { + Assert.True(reader.Peek() == -1, "Expected that the reader would be positioned at the end of the input stream"); + } + else + { + Assert.Equal((char)expectedPeek, (char)reader.Peek()); + } + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Tokenizer/CSharpTokenizerCommentTest.cs b/test/Microsoft.AspNet.Razor.Test/Tokenizer/CSharpTokenizerCommentTest.cs new file mode 100644 index 0000000000..a942ead5b3 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Tokenizer/CSharpTokenizerCommentTest.cs @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Tokenizer +{ + public class CSharpTokenizerCommentTest : CSharpTokenizerTestBase + { + [Fact] + public void Next_Ignores_Star_At_EOF_In_RazorComment() + { + TestTokenizer("@* Foo * Bar * Baz *", + new CSharpSymbol(0, 0, 0, "@", CSharpSymbolType.RazorCommentTransition), + new CSharpSymbol(1, 0, 1, "*", CSharpSymbolType.RazorCommentStar), + new CSharpSymbol(2, 0, 2, " Foo * Bar * Baz *", CSharpSymbolType.RazorComment)); + } + + [Fact] + public void Next_Ignores_Star_Without_Trailing_At() + { + TestTokenizer("@* Foo * Bar * Baz *@", + new CSharpSymbol(0, 0, 0, "@", CSharpSymbolType.RazorCommentTransition), + new CSharpSymbol(1, 0, 1, "*", CSharpSymbolType.RazorCommentStar), + new CSharpSymbol(2, 0, 2, " Foo * Bar * Baz ", CSharpSymbolType.RazorComment), + new CSharpSymbol(19, 0, 19, "*", CSharpSymbolType.RazorCommentStar), + new CSharpSymbol(20, 0, 20, "@", CSharpSymbolType.RazorCommentTransition)); + } + + [Fact] + public void Next_Returns_RazorComment_Token_For_Entire_Razor_Comment() + { + TestTokenizer("@* Foo Bar Baz *@", + new CSharpSymbol(0, 0, 0, "@", CSharpSymbolType.RazorCommentTransition), + new CSharpSymbol(1, 0, 1, "*", CSharpSymbolType.RazorCommentStar), + new CSharpSymbol(2, 0, 2, " Foo Bar Baz ", CSharpSymbolType.RazorComment), + new CSharpSymbol(15, 0, 15, "*", CSharpSymbolType.RazorCommentStar), + new CSharpSymbol(16, 0, 16, "@", CSharpSymbolType.RazorCommentTransition)); + } + + [Fact] + public void Next_Returns_Comment_Token_For_Entire_Single_Line_Comment() + { + TestTokenizer("// Foo Bar Baz", new CSharpSymbol(0, 0, 0, "// Foo Bar Baz", CSharpSymbolType.Comment)); + } + + [Fact] + public void Single_Line_Comment_Is_Terminated_By_Newline() + { + TestTokenizer("// Foo Bar Baz\na", new CSharpSymbol(0, 0, 0, "// Foo Bar Baz", CSharpSymbolType.Comment), IgnoreRemaining); + } + + [Fact] + public void Multi_Line_Comment_In_Single_Line_Comment_Has_No_Effect() + { + TestTokenizer("// Foo/*Bar*/ Baz\na", new CSharpSymbol(0, 0, 0, "// Foo/*Bar*/ Baz", CSharpSymbolType.Comment), IgnoreRemaining); + } + + [Fact] + public void Next_Returns_Comment_Token_For_Entire_Multi_Line_Comment() + { + TestTokenizer("/* Foo\nBar\nBaz */", new CSharpSymbol(0, 0, 0, "/* Foo\nBar\nBaz */", CSharpSymbolType.Comment)); + } + + [Fact] + public void Multi_Line_Comment_Is_Terminated_By_End_Sequence() + { + TestTokenizer("/* Foo\nBar\nBaz */a", new CSharpSymbol(0, 0, 0, "/* Foo\nBar\nBaz */", CSharpSymbolType.Comment), IgnoreRemaining); + } + + [Fact] + public void Unterminated_Multi_Line_Comment_Captures_To_EOF() + { + TestTokenizer("/* Foo\nBar\nBaz", new CSharpSymbol(0, 0, 0, "/* Foo\nBar\nBaz", CSharpSymbolType.Comment), IgnoreRemaining); + } + + [Fact] + public void Nested_Multi_Line_Comments_Terminated_At_First_End_Sequence() + { + TestTokenizer("/* Foo/*\nBar\nBaz*/ */", new CSharpSymbol(0, 0, 0, "/* Foo/*\nBar\nBaz*/", CSharpSymbolType.Comment), IgnoreRemaining); + } + + [Fact] + public void Nested_Multi_Line_Comments_Terminated_At_Full_End_Sequence() + { + TestTokenizer("/* Foo\nBar\nBaz* */", new CSharpSymbol(0, 0, 0, "/* Foo\nBar\nBaz* */", CSharpSymbolType.Comment), IgnoreRemaining); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Tokenizer/CSharpTokenizerIdentifierTest.cs b/test/Microsoft.AspNet.Razor.Test/Tokenizer/CSharpTokenizerIdentifierTest.cs new file mode 100644 index 0000000000..087faac951 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Tokenizer/CSharpTokenizerIdentifierTest.cs @@ -0,0 +1,169 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Tokenizer +{ + public class CSharpTokenizerIdentifierTest : CSharpTokenizerTestBase + { + [Fact] + public void Simple_Identifier_Is_Recognized() + { + TestTokenizer("foo", new CSharpSymbol(0, 0, 0, "foo", CSharpSymbolType.Identifier)); + } + + [Fact] + public void Identifier_Starting_With_Underscore_Is_Recognized() + { + TestTokenizer("_foo", new CSharpSymbol(0, 0, 0, "_foo", CSharpSymbolType.Identifier)); + } + + [Fact] + public void Identifier_Can_Contain_Digits() + { + TestTokenizer("foo4", new CSharpSymbol(0, 0, 0, "foo4", CSharpSymbolType.Identifier)); + } + + [Fact] + public void Identifier_Can_Start_With_Titlecase_Letter() + { + TestTokenizer("ῼfoo", new CSharpSymbol(0, 0, 0, "ῼfoo", CSharpSymbolType.Identifier)); + } + + [Fact] + public void Identifier_Can_Start_With_Letter_Modifier() + { + TestTokenizer("ᵊfoo", new CSharpSymbol(0, 0, 0, "ᵊfoo", CSharpSymbolType.Identifier)); + } + + [Fact] + public void Identifier_Can_Start_With_Other_Letter() + { + TestTokenizer("ƻfoo", new CSharpSymbol(0, 0, 0, "ƻfoo", CSharpSymbolType.Identifier)); + } + + [Fact] + public void Identifier_Can_Start_With_Number_Letter() + { + TestTokenizer("Ⅽool", new CSharpSymbol(0, 0, 0, "Ⅽool", CSharpSymbolType.Identifier)); + } + + [Fact] + public void Identifier_Can_Contain_Non_Spacing_Mark() + { + TestTokenizer("foo\u0300", new CSharpSymbol(0, 0, 0, "foo\u0300", CSharpSymbolType.Identifier)); + } + + [Fact] + public void Identifier_Can_Contain_Spacing_Combining_Mark() + { + TestTokenizer("fooः", new CSharpSymbol(0, 0, 0, "fooः", CSharpSymbolType.Identifier)); + } + + [Fact] + public void Identifier_Can_Contain_Non_English_Digit() + { + TestTokenizer("foo١", new CSharpSymbol(0, 0, 0, "foo١", CSharpSymbolType.Identifier)); + } + + [Fact] + public void Identifier_Can_Contain_Connector_Punctuation() + { + TestTokenizer("foo‿bar", new CSharpSymbol(0, 0, 0, "foo‿bar", CSharpSymbolType.Identifier)); + } + + [Fact] + public void Identifier_Can_Contain_Format_Character() + { + TestTokenizer("foo؃bar", new CSharpSymbol(0, 0, 0, "foo؃bar", CSharpSymbolType.Identifier)); + } + + [Fact] + public void Keywords_Are_Recognized_As_Keyword_Tokens() + { + TestKeyword("abstract", CSharpKeyword.Abstract); + TestKeyword("byte", CSharpKeyword.Byte); + TestKeyword("class", CSharpKeyword.Class); + TestKeyword("delegate", CSharpKeyword.Delegate); + TestKeyword("event", CSharpKeyword.Event); + TestKeyword("fixed", CSharpKeyword.Fixed); + TestKeyword("if", CSharpKeyword.If); + TestKeyword("internal", CSharpKeyword.Internal); + TestKeyword("new", CSharpKeyword.New); + TestKeyword("override", CSharpKeyword.Override); + TestKeyword("readonly", CSharpKeyword.Readonly); + TestKeyword("short", CSharpKeyword.Short); + TestKeyword("struct", CSharpKeyword.Struct); + TestKeyword("try", CSharpKeyword.Try); + TestKeyword("unsafe", CSharpKeyword.Unsafe); + TestKeyword("volatile", CSharpKeyword.Volatile); + TestKeyword("as", CSharpKeyword.As); + TestKeyword("do", CSharpKeyword.Do); + TestKeyword("is", CSharpKeyword.Is); + TestKeyword("params", CSharpKeyword.Params); + TestKeyword("ref", CSharpKeyword.Ref); + TestKeyword("switch", CSharpKeyword.Switch); + TestKeyword("ushort", CSharpKeyword.Ushort); + TestKeyword("while", CSharpKeyword.While); + TestKeyword("case", CSharpKeyword.Case); + TestKeyword("const", CSharpKeyword.Const); + TestKeyword("explicit", CSharpKeyword.Explicit); + TestKeyword("float", CSharpKeyword.Float); + TestKeyword("null", CSharpKeyword.Null); + TestKeyword("sizeof", CSharpKeyword.Sizeof); + TestKeyword("typeof", CSharpKeyword.Typeof); + TestKeyword("implicit", CSharpKeyword.Implicit); + TestKeyword("private", CSharpKeyword.Private); + TestKeyword("this", CSharpKeyword.This); + TestKeyword("using", CSharpKeyword.Using); + TestKeyword("extern", CSharpKeyword.Extern); + TestKeyword("return", CSharpKeyword.Return); + TestKeyword("stackalloc", CSharpKeyword.Stackalloc); + TestKeyword("uint", CSharpKeyword.Uint); + TestKeyword("base", CSharpKeyword.Base); + TestKeyword("catch", CSharpKeyword.Catch); + TestKeyword("continue", CSharpKeyword.Continue); + TestKeyword("double", CSharpKeyword.Double); + TestKeyword("for", CSharpKeyword.For); + TestKeyword("in", CSharpKeyword.In); + TestKeyword("lock", CSharpKeyword.Lock); + TestKeyword("object", CSharpKeyword.Object); + TestKeyword("protected", CSharpKeyword.Protected); + TestKeyword("static", CSharpKeyword.Static); + TestKeyword("false", CSharpKeyword.False); + TestKeyword("public", CSharpKeyword.Public); + TestKeyword("sbyte", CSharpKeyword.Sbyte); + TestKeyword("throw", CSharpKeyword.Throw); + TestKeyword("virtual", CSharpKeyword.Virtual); + TestKeyword("decimal", CSharpKeyword.Decimal); + TestKeyword("else", CSharpKeyword.Else); + TestKeyword("operator", CSharpKeyword.Operator); + TestKeyword("string", CSharpKeyword.String); + TestKeyword("ulong", CSharpKeyword.Ulong); + TestKeyword("bool", CSharpKeyword.Bool); + TestKeyword("char", CSharpKeyword.Char); + TestKeyword("default", CSharpKeyword.Default); + TestKeyword("foreach", CSharpKeyword.Foreach); + TestKeyword("long", CSharpKeyword.Long); + TestKeyword("void", CSharpKeyword.Void); + TestKeyword("enum", CSharpKeyword.Enum); + TestKeyword("finally", CSharpKeyword.Finally); + TestKeyword("int", CSharpKeyword.Int); + TestKeyword("out", CSharpKeyword.Out); + TestKeyword("sealed", CSharpKeyword.Sealed); + TestKeyword("true", CSharpKeyword.True); + TestKeyword("goto", CSharpKeyword.Goto); + TestKeyword("unchecked", CSharpKeyword.Unchecked); + TestKeyword("interface", CSharpKeyword.Interface); + TestKeyword("break", CSharpKeyword.Break); + TestKeyword("checked", CSharpKeyword.Checked); + TestKeyword("namespace", CSharpKeyword.Namespace); + } + + private void TestKeyword(string keyword, CSharpKeyword keywordType) + { + TestTokenizer(keyword, new CSharpSymbol(0, 0, 0, keyword, CSharpSymbolType.Keyword) { Keyword = keywordType }); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Tokenizer/CSharpTokenizerLiteralTest.cs b/test/Microsoft.AspNet.Razor.Test/Tokenizer/CSharpTokenizerLiteralTest.cs new file mode 100644 index 0000000000..f9d949f06e --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Tokenizer/CSharpTokenizerLiteralTest.cs @@ -0,0 +1,284 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Tokenizer +{ + public class CSharpTokenizerLiteralTest : CSharpTokenizerTestBase + { + [Fact] + public void Simple_Integer_Literal_Is_Recognized() + { + TestSingleToken("01189998819991197253", CSharpSymbolType.IntegerLiteral); + } + + [Fact] + public void Integer_Type_Suffix_Is_Recognized() + { + TestSingleToken("42U", CSharpSymbolType.IntegerLiteral); + TestSingleToken("42u", CSharpSymbolType.IntegerLiteral); + + TestSingleToken("42L", CSharpSymbolType.IntegerLiteral); + TestSingleToken("42l", CSharpSymbolType.IntegerLiteral); + + TestSingleToken("42UL", CSharpSymbolType.IntegerLiteral); + TestSingleToken("42Ul", CSharpSymbolType.IntegerLiteral); + + TestSingleToken("42uL", CSharpSymbolType.IntegerLiteral); + TestSingleToken("42ul", CSharpSymbolType.IntegerLiteral); + + TestSingleToken("42LU", CSharpSymbolType.IntegerLiteral); + TestSingleToken("42Lu", CSharpSymbolType.IntegerLiteral); + + TestSingleToken("42lU", CSharpSymbolType.IntegerLiteral); + TestSingleToken("42lu", CSharpSymbolType.IntegerLiteral); + } + + [Fact] + public void Trailing_Letter_Is_Not_Part_Of_Integer_Literal_If_Not_Type_Sufix() + { + TestTokenizer("42a", new CSharpSymbol(0, 0, 0, "42", CSharpSymbolType.IntegerLiteral), IgnoreRemaining); + } + + [Fact] + public void Simple_Hex_Literal_Is_Recognized() + { + TestSingleToken("0x0123456789ABCDEF", CSharpSymbolType.IntegerLiteral); + } + + [Fact] + public void Integer_Type_Suffix_Is_Recognized_In_Hex_Literal() + { + TestSingleToken("0xDEADBEEFU", CSharpSymbolType.IntegerLiteral); + TestSingleToken("0xDEADBEEFu", CSharpSymbolType.IntegerLiteral); + + TestSingleToken("0xDEADBEEFL", CSharpSymbolType.IntegerLiteral); + TestSingleToken("0xDEADBEEFl", CSharpSymbolType.IntegerLiteral); + + TestSingleToken("0xDEADBEEFUL", CSharpSymbolType.IntegerLiteral); + TestSingleToken("0xDEADBEEFUl", CSharpSymbolType.IntegerLiteral); + + TestSingleToken("0xDEADBEEFuL", CSharpSymbolType.IntegerLiteral); + TestSingleToken("0xDEADBEEFul", CSharpSymbolType.IntegerLiteral); + + TestSingleToken("0xDEADBEEFLU", CSharpSymbolType.IntegerLiteral); + TestSingleToken("0xDEADBEEFLu", CSharpSymbolType.IntegerLiteral); + + TestSingleToken("0xDEADBEEFlU", CSharpSymbolType.IntegerLiteral); + TestSingleToken("0xDEADBEEFlu", CSharpSymbolType.IntegerLiteral); + } + + [Fact] + public void Trailing_Letter_Is_Not_Part_Of_Hex_Literal_If_Not_Type_Sufix() + { + TestTokenizer("0xDEADBEEFz", new CSharpSymbol(0, 0, 0, "0xDEADBEEF", CSharpSymbolType.IntegerLiteral), IgnoreRemaining); + } + + [Fact] + public void Dot_Followed_By_Non_Digit_Is_Not_Part_Of_Real_Literal() + { + TestTokenizer("3.a", new CSharpSymbol(0, 0, 0, "3", CSharpSymbolType.IntegerLiteral), IgnoreRemaining); + } + + [Fact] + public void Simple_Real_Literal_Is_Recognized() + { + TestTokenizer("3.14159", new CSharpSymbol(0, 0, 0, "3.14159", CSharpSymbolType.RealLiteral)); + } + + [Fact] + public void Real_Literal_Between_Zero_And_One_Is_Recognized() + { + TestTokenizer(".14159", new CSharpSymbol(0, 0, 0, ".14159", CSharpSymbolType.RealLiteral)); + } + + [Fact] + public void Integer_With_Real_Type_Suffix_Is_Recognized() + { + TestSingleToken("42F", CSharpSymbolType.RealLiteral); + TestSingleToken("42f", CSharpSymbolType.RealLiteral); + TestSingleToken("42D", CSharpSymbolType.RealLiteral); + TestSingleToken("42d", CSharpSymbolType.RealLiteral); + TestSingleToken("42M", CSharpSymbolType.RealLiteral); + TestSingleToken("42m", CSharpSymbolType.RealLiteral); + } + + [Fact] + public void Integer_With_Exponent_Is_Recognized() + { + TestSingleToken("1e10", CSharpSymbolType.RealLiteral); + TestSingleToken("1E10", CSharpSymbolType.RealLiteral); + TestSingleToken("1e+10", CSharpSymbolType.RealLiteral); + TestSingleToken("1E+10", CSharpSymbolType.RealLiteral); + TestSingleToken("1e-10", CSharpSymbolType.RealLiteral); + TestSingleToken("1E-10", CSharpSymbolType.RealLiteral); + } + + [Fact] + public void Real_Number_With_Type_Suffix_Is_Recognized() + { + TestSingleToken("3.14F", CSharpSymbolType.RealLiteral); + TestSingleToken("3.14f", CSharpSymbolType.RealLiteral); + TestSingleToken("3.14D", CSharpSymbolType.RealLiteral); + TestSingleToken("3.14d", CSharpSymbolType.RealLiteral); + TestSingleToken("3.14M", CSharpSymbolType.RealLiteral); + TestSingleToken("3.14m", CSharpSymbolType.RealLiteral); + } + + [Fact] + public void Real_Number_With_Exponent_Is_Recognized() + { + TestSingleToken("3.14E10", CSharpSymbolType.RealLiteral); + TestSingleToken("3.14e10", CSharpSymbolType.RealLiteral); + TestSingleToken("3.14E+10", CSharpSymbolType.RealLiteral); + TestSingleToken("3.14e+10", CSharpSymbolType.RealLiteral); + TestSingleToken("3.14E-10", CSharpSymbolType.RealLiteral); + TestSingleToken("3.14e-10", CSharpSymbolType.RealLiteral); + } + + [Fact] + public void Real_Number_With_Exponent_And_Type_Suffix_Is_Recognized() + { + TestSingleToken("3.14E+10F", CSharpSymbolType.RealLiteral); + } + + [Fact] + public void Single_Character_Literal_Is_Recognized() + { + TestSingleToken("'f'", CSharpSymbolType.CharacterLiteral); + } + + [Fact] + public void Multi_Character_Literal_Is_Recognized() + { + TestSingleToken("'foo'", CSharpSymbolType.CharacterLiteral); + } + + [Fact] + public void Character_Literal_Is_Terminated_By_EOF_If_Unterminated() + { + TestSingleToken("'foo bar", CSharpSymbolType.CharacterLiteral); + } + + [Fact] + public void Character_Literal_Not_Terminated_By_Escaped_Quote() + { + TestSingleToken("'foo\\'bar'", CSharpSymbolType.CharacterLiteral); + } + + [Fact] + public void Character_Literal_Is_Terminated_By_EOL_If_Unterminated() + { + TestTokenizer("'foo\n", new CSharpSymbol(0, 0, 0, "'foo", CSharpSymbolType.CharacterLiteral), IgnoreRemaining); + } + + [Fact] + public void Character_Literal_Terminated_By_EOL_Even_When_Last_Char_Is_Slash() + { + TestTokenizer("'foo\\\n", new CSharpSymbol(0, 0, 0, "'foo\\", CSharpSymbolType.CharacterLiteral), IgnoreRemaining); + } + + [Fact] + public void Character_Literal_Terminated_By_EOL_Even_When_Last_Char_Is_Slash_And_Followed_By_Stuff() + { + TestTokenizer("'foo\\\nflarg", new CSharpSymbol(0, 0, 0, "'foo\\", CSharpSymbolType.CharacterLiteral), IgnoreRemaining); + } + + [Fact] + public void Character_Literal_Terminated_By_CRLF_Even_When_Last_Char_Is_Slash() + { + TestTokenizer("'foo\\\r\n", new CSharpSymbol(0, 0, 0, "'foo\\", CSharpSymbolType.CharacterLiteral), IgnoreRemaining); + } + + [Fact] + public void Character_Literal_Terminated_By_CRLF_Even_When_Last_Char_Is_Slash_And_Followed_By_Stuff() + { + TestTokenizer("'foo\\\r\nflarg", new CSharpSymbol(0, 0, 0, "'foo\\", CSharpSymbolType.CharacterLiteral), IgnoreRemaining); + } + + [Fact] + public void Character_Literal_Allows_Escaped_Escape() + { + TestTokenizer("'foo\\\\'blah", new CSharpSymbol(0, 0, 0, "'foo\\\\'", CSharpSymbolType.CharacterLiteral), IgnoreRemaining); + } + + [Fact] + public void String_Literal_Is_Recognized() + { + TestSingleToken("\"foo\"", CSharpSymbolType.StringLiteral); + } + + [Fact] + public void String_Literal_Is_Terminated_By_EOF_If_Unterminated() + { + TestSingleToken("\"foo bar", CSharpSymbolType.StringLiteral); + } + + [Fact] + public void String_Literal_Not_Terminated_By_Escaped_Quote() + { + TestSingleToken("\"foo\\\"bar\"", CSharpSymbolType.StringLiteral); + } + + [Fact] + public void String_Literal_Is_Terminated_By_EOL_If_Unterminated() + { + TestTokenizer("\"foo\n", new CSharpSymbol(0, 0, 0, "\"foo", CSharpSymbolType.StringLiteral), IgnoreRemaining); + } + + [Fact] + public void String_Literal_Terminated_By_EOL_Even_When_Last_Char_Is_Slash() + { + TestTokenizer("\"foo\\\n", new CSharpSymbol(0, 0, 0, "\"foo\\", CSharpSymbolType.StringLiteral), IgnoreRemaining); + } + + [Fact] + public void String_Literal_Terminated_By_EOL_Even_When_Last_Char_Is_Slash_And_Followed_By_Stuff() + { + TestTokenizer("\"foo\\\nflarg", new CSharpSymbol(0, 0, 0, "\"foo\\", CSharpSymbolType.StringLiteral), IgnoreRemaining); + } + + [Fact] + public void String_Literal_Terminated_By_CRLF_Even_When_Last_Char_Is_Slash() + { + TestTokenizer("\"foo\\\r\n", new CSharpSymbol(0, 0, 0, "\"foo\\", CSharpSymbolType.StringLiteral), IgnoreRemaining); + } + + [Fact] + public void String_Literal_Terminated_By_CRLF_Even_When_Last_Char_Is_Slash_And_Followed_By_Stuff() + { + TestTokenizer("\"foo\\\r\nflarg", new CSharpSymbol(0, 0, 0, "\"foo\\", CSharpSymbolType.StringLiteral), IgnoreRemaining); + } + + [Fact] + public void String_Literal_Allows_Escaped_Escape() + { + TestTokenizer("\"foo\\\\\"blah", new CSharpSymbol(0, 0, 0, "\"foo\\\\\"", CSharpSymbolType.StringLiteral), IgnoreRemaining); + } + + [Fact] + public void Verbatim_String_Literal_Can_Contain_Newlines() + { + TestSingleToken("@\"foo\nbar\nbaz\"", CSharpSymbolType.StringLiteral); + } + + [Fact] + public void Verbatim_String_Literal_Not_Terminated_By_Escaped_Double_Quote() + { + TestSingleToken("@\"foo\"\"bar\"", CSharpSymbolType.StringLiteral); + } + + [Fact] + public void Verbatim_String_Literal_Is_Terminated_By_Slash_Double_Quote() + { + TestTokenizer("@\"foo\\\"bar\"", new CSharpSymbol(0, 0, 0, "@\"foo\\\"", CSharpSymbolType.StringLiteral), IgnoreRemaining); + } + + [Fact] + public void Verbatim_String_Literal_Is_Terminated_By_EOF() + { + TestSingleToken("@\"foo", CSharpSymbolType.StringLiteral); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Tokenizer/CSharpTokenizerOperatorsTest.cs b/test/Microsoft.AspNet.Razor.Test/Tokenizer/CSharpTokenizerOperatorsTest.cs new file mode 100644 index 0000000000..86cfa9fbf7 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Tokenizer/CSharpTokenizerOperatorsTest.cs @@ -0,0 +1,296 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Tokenizer +{ + public class CSharpTokenizerOperatorsTest : CSharpTokenizerTestBase + { + [Fact] + public void LeftBrace_Is_Recognized() + { + TestSingleToken("{", CSharpSymbolType.LeftBrace); + } + + [Fact] + public void Plus_Is_Recognized() + { + TestSingleToken("+", CSharpSymbolType.Plus); + } + + [Fact] + public void Assign_Is_Recognized() + { + TestSingleToken("=", CSharpSymbolType.Assign); + } + + [Fact] + public void Arrow_Is_Recognized() + { + TestSingleToken("->", CSharpSymbolType.Arrow); + } + + [Fact] + public void AndAssign_Is_Recognized() + { + TestSingleToken("&=", CSharpSymbolType.AndAssign); + } + + [Fact] + public void RightBrace_Is_Recognized() + { + TestSingleToken("}", CSharpSymbolType.RightBrace); + } + + [Fact] + public void Minus_Is_Recognized() + { + TestSingleToken("-", CSharpSymbolType.Minus); + } + + [Fact] + public void LessThan_Is_Recognized() + { + TestSingleToken("<", CSharpSymbolType.LessThan); + } + + [Fact] + public void Equals_Is_Recognized() + { + TestSingleToken("==", CSharpSymbolType.Equals); + } + + [Fact] + public void OrAssign_Is_Recognized() + { + TestSingleToken("|=", CSharpSymbolType.OrAssign); + } + + [Fact] + public void LeftBracket_Is_Recognized() + { + TestSingleToken("[", CSharpSymbolType.LeftBracket); + } + + [Fact] + public void Star_Is_Recognized() + { + TestSingleToken("*", CSharpSymbolType.Star); + } + + [Fact] + public void GreaterThan_Is_Recognized() + { + TestSingleToken(">", CSharpSymbolType.GreaterThan); + } + + [Fact] + public void NotEqual_Is_Recognized() + { + TestSingleToken("!=", CSharpSymbolType.NotEqual); + } + + [Fact] + public void XorAssign_Is_Recognized() + { + TestSingleToken("^=", CSharpSymbolType.XorAssign); + } + + [Fact] + public void RightBracket_Is_Recognized() + { + TestSingleToken("]", CSharpSymbolType.RightBracket); + } + + [Fact] + public void Slash_Is_Recognized() + { + TestSingleToken("/", CSharpSymbolType.Slash); + } + + [Fact] + public void QuestionMark_Is_Recognized() + { + TestSingleToken("?", CSharpSymbolType.QuestionMark); + } + + [Fact] + public void LessThanEqual_Is_Recognized() + { + TestSingleToken("<=", CSharpSymbolType.LessThanEqual); + } + + [Fact] + public void LeftShift_Is_Not_Specially_Recognized() + { + TestTokenizer("<<", + new CSharpSymbol(0, 0, 0, "<", CSharpSymbolType.LessThan), + new CSharpSymbol(1, 0, 1, "<", CSharpSymbolType.LessThan)); + } + + [Fact] + public void LeftParen_Is_Recognized() + { + TestSingleToken("(", CSharpSymbolType.LeftParenthesis); + } + + [Fact] + public void Modulo_Is_Recognized() + { + TestSingleToken("%", CSharpSymbolType.Modulo); + } + + [Fact] + public void NullCoalesce_Is_Recognized() + { + TestSingleToken("??", CSharpSymbolType.NullCoalesce); + } + + [Fact] + public void GreaterThanEqual_Is_Recognized() + { + TestSingleToken(">=", CSharpSymbolType.GreaterThanEqual); + } + + [Fact] + public void EqualGreaterThan_Is_Recognized() + { + TestSingleToken("=>", CSharpSymbolType.GreaterThanEqual); + } + + [Fact] + public void RightParen_Is_Recognized() + { + TestSingleToken(")", CSharpSymbolType.RightParenthesis); + } + + [Fact] + public void And_Is_Recognized() + { + TestSingleToken("&", CSharpSymbolType.And); + } + + [Fact] + public void DoubleColon_Is_Recognized() + { + TestSingleToken("::", CSharpSymbolType.DoubleColon); + } + + [Fact] + public void PlusAssign_Is_Recognized() + { + TestSingleToken("+=", CSharpSymbolType.PlusAssign); + } + + [Fact] + public void Semicolon_Is_Recognized() + { + TestSingleToken(";", CSharpSymbolType.Semicolon); + } + + [Fact] + public void Tilde_Is_Recognized() + { + TestSingleToken("~", CSharpSymbolType.Tilde); + } + + [Fact] + public void DoubleOr_Is_Recognized() + { + TestSingleToken("||", CSharpSymbolType.DoubleOr); + } + + [Fact] + public void ModuloAssign_Is_Recognized() + { + TestSingleToken("%=", CSharpSymbolType.ModuloAssign); + } + + [Fact] + public void Colon_Is_Recognized() + { + TestSingleToken(":", CSharpSymbolType.Colon); + } + + [Fact] + public void Not_Is_Recognized() + { + TestSingleToken("!", CSharpSymbolType.Not); + } + + [Fact] + public void DoubleAnd_Is_Recognized() + { + TestSingleToken("&&", CSharpSymbolType.DoubleAnd); + } + + [Fact] + public void DivideAssign_Is_Recognized() + { + TestSingleToken("/=", CSharpSymbolType.DivideAssign); + } + + [Fact] + public void Comma_Is_Recognized() + { + TestSingleToken(",", CSharpSymbolType.Comma); + } + + [Fact] + public void Xor_Is_Recognized() + { + TestSingleToken("^", CSharpSymbolType.Xor); + } + + [Fact] + public void Decrement_Is_Recognized() + { + TestSingleToken("--", CSharpSymbolType.Decrement); + } + + [Fact] + public void MultiplyAssign_Is_Recognized() + { + TestSingleToken("*=", CSharpSymbolType.MultiplyAssign); + } + + [Fact] + public void Dot_Is_Recognized() + { + TestSingleToken(".", CSharpSymbolType.Dot); + } + + [Fact] + public void Or_Is_Recognized() + { + TestSingleToken("|", CSharpSymbolType.Or); + } + + [Fact] + public void Increment_Is_Recognized() + { + TestSingleToken("++", CSharpSymbolType.Increment); + } + + [Fact] + public void MinusAssign_Is_Recognized() + { + TestSingleToken("-=", CSharpSymbolType.MinusAssign); + } + + [Fact] + public void RightShift_Is_Not_Specially_Recognized() + { + TestTokenizer(">>", + new CSharpSymbol(0, 0, 0, ">", CSharpSymbolType.GreaterThan), + new CSharpSymbol(1, 0, 1, ">", CSharpSymbolType.GreaterThan)); + } + + [Fact] + public void Hash_Is_Recognized() + { + TestSingleToken("#", CSharpSymbolType.Hash); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Tokenizer/CSharpTokenizerTest.cs b/test/Microsoft.AspNet.Razor.Test/Tokenizer/CSharpTokenizerTest.cs new file mode 100644 index 0000000000..6e65ca87b7 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Tokenizer/CSharpTokenizerTest.cs @@ -0,0 +1,103 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Tokenizer; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Tokenizer +{ + public class CSharpTokenizerTest : CSharpTokenizerTestBase + { + [Fact] + public void Constructor_Throws_ArgNull_If_Null_Source_Provided() + { + Assert.ThrowsArgumentNull(() => new CSharpTokenizer(null), "source"); + } + + [Fact] + public void Next_Returns_Null_When_EOF_Reached() + { + TestTokenizer(""); + } + + [Fact] + public void Next_Returns_Newline_Token_For_Single_CR() + { + TestTokenizer("\r\ra", + new CSharpSymbol(0, 0, 0, "\r", CSharpSymbolType.NewLine), + new CSharpSymbol(1, 1, 0, "\r", CSharpSymbolType.NewLine), + IgnoreRemaining); + } + + [Fact] + public void Next_Returns_Newline_Token_For_Single_LF() + { + TestTokenizer("\n\na", + new CSharpSymbol(0, 0, 0, "\n", CSharpSymbolType.NewLine), + new CSharpSymbol(1, 1, 0, "\n", CSharpSymbolType.NewLine), + IgnoreRemaining); + } + + [Fact] + public void Next_Returns_Newline_Token_For_Single_NEL() + { + // NEL: Unicode "Next Line" U+0085 + TestTokenizer("\u0085\u0085a", + new CSharpSymbol(0, 0, 0, "\u0085", CSharpSymbolType.NewLine), + new CSharpSymbol(1, 1, 0, "\u0085", CSharpSymbolType.NewLine), + IgnoreRemaining); + } + + [Fact] + public void Next_Returns_Newline_Token_For_Single_Line_Separator() + { + // Unicode "Line Separator" U+2028 + TestTokenizer("\u2028\u2028a", + new CSharpSymbol(0, 0, 0, "\u2028", CSharpSymbolType.NewLine), + new CSharpSymbol(1, 1, 0, "\u2028", CSharpSymbolType.NewLine), + IgnoreRemaining); + } + + [Fact] + public void Next_Returns_Newline_Token_For_Single_Paragraph_Separator() + { + // Unicode "Paragraph Separator" U+2029 + TestTokenizer("\u2029\u2029a", + new CSharpSymbol(0, 0, 0, "\u2029", CSharpSymbolType.NewLine), + new CSharpSymbol(1, 1, 0, "\u2029", CSharpSymbolType.NewLine), + IgnoreRemaining); + } + + [Fact] + public void Next_Returns_Single_Newline_Token_For_CRLF() + { + TestTokenizer("\r\n\r\na", + new CSharpSymbol(0, 0, 0, "\r\n", CSharpSymbolType.NewLine), + new CSharpSymbol(2, 1, 0, "\r\n", CSharpSymbolType.NewLine), + IgnoreRemaining); + } + + [Fact] + public void Next_Returns_Token_For_Whitespace_Characters() + { + TestTokenizer(" \f\t\u000B \n ", + new CSharpSymbol(0, 0, 0, " \f\t\u000B ", CSharpSymbolType.WhiteSpace), + new CSharpSymbol(5, 0, 5, "\n", CSharpSymbolType.NewLine), + new CSharpSymbol(6, 1, 0, " ", CSharpSymbolType.WhiteSpace)); + } + + [Fact] + public void Transition_Is_Recognized() + { + TestSingleToken("@", CSharpSymbolType.Transition); + } + + [Fact] + public void Transition_Is_Recognized_As_SingleCharacter() + { + TestTokenizer("@(", + new CSharpSymbol(0, 0, 0, "@", CSharpSymbolType.Transition), + new CSharpSymbol(1, 0, 1, "(", CSharpSymbolType.LeftParenthesis)); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Tokenizer/CSharpTokenizerTestBase.cs b/test/Microsoft.AspNet.Razor.Test/Tokenizer/CSharpTokenizerTestBase.cs new file mode 100644 index 0000000000..76d74a8064 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Tokenizer/CSharpTokenizerTestBase.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Text; +using Microsoft.AspNet.Razor.Tokenizer; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; + +namespace Microsoft.AspNet.Razor.Test.Tokenizer +{ + public abstract class CSharpTokenizerTestBase : TokenizerTestBase + { + private static CSharpSymbol _ignoreRemaining = new CSharpSymbol(0, 0, 0, String.Empty, CSharpSymbolType.Unknown); + + protected override CSharpSymbol IgnoreRemaining + { + get { return _ignoreRemaining; } + } + + protected override Tokenizer CreateTokenizer(ITextDocument source) + { + return new CSharpTokenizer(source); + } + + protected void TestSingleToken(string text, CSharpSymbolType expectedSymbolType) + { + TestTokenizer(text, new CSharpSymbol(0, 0, 0, text, expectedSymbolType)); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Tokenizer/HtmlTokenizerTest.cs b/test/Microsoft.AspNet.Razor.Test/Tokenizer/HtmlTokenizerTest.cs new file mode 100644 index 0000000000..c763b6647b --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Tokenizer/HtmlTokenizerTest.cs @@ -0,0 +1,167 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Tokenizer; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Tokenizer +{ + public class HtmlTokenizerTest : HtmlTokenizerTestBase + { + [Fact] + public void Constructor_Throws_ArgNull_If_Null_Source_Provided() + { + Assert.ThrowsArgumentNull(() => new HtmlTokenizer(null), "source"); + } + + [Fact] + public void Next_Returns_Null_When_EOF_Reached() + { + TestTokenizer(""); + } + + [Fact] + public void Text_Is_Recognized() + { + TestTokenizer("foo-9309&smlkmb;::-3029022,.sdkq92384", + new HtmlSymbol(0, 0, 0, "foo-9309&smlkmb;::-3029022,.sdkq92384", HtmlSymbolType.Text)); + } + + [Fact] + public void Whitespace_Is_Recognized() + { + TestTokenizer(" \t\f ", + new HtmlSymbol(0, 0, 0, " \t\f ", HtmlSymbolType.WhiteSpace)); + } + + [Fact] + public void Newline_Is_Recognized() + { + TestTokenizer("\n\r\r\n", + new HtmlSymbol(0, 0, 0, "\n", HtmlSymbolType.NewLine), + new HtmlSymbol(1, 1, 0, "\r", HtmlSymbolType.NewLine), + new HtmlSymbol(2, 2, 0, "\r\n", HtmlSymbolType.NewLine)); + } + + [Fact] + public void Transition_Is_Not_Recognized_Mid_Text_If_Surrounded_By_Alphanumeric_Characters() + { + TestSingleToken("foo@bar", HtmlSymbolType.Text); + } + + [Fact] + public void OpenAngle_Is_Recognized() + { + TestSingleToken("<", HtmlSymbolType.OpenAngle); + } + + [Fact] + public void Bang_Is_Recognized() + { + TestSingleToken("!", HtmlSymbolType.Bang); + } + + [Fact] + public void Solidus_Is_Recognized() + { + TestSingleToken("/", HtmlSymbolType.Solidus); + } + + [Fact] + public void QuestionMark_Is_Recognized() + { + TestSingleToken("?", HtmlSymbolType.QuestionMark); + } + + [Fact] + public void LeftBracket_Is_Recognized() + { + TestSingleToken("[", HtmlSymbolType.LeftBracket); + } + + [Fact] + public void CloseAngle_Is_Recognized() + { + TestSingleToken(">", HtmlSymbolType.CloseAngle); + } + + [Fact] + public void RightBracket_Is_Recognized() + { + TestSingleToken("]", HtmlSymbolType.RightBracket); + } + + [Fact] + public void Equals_Is_Recognized() + { + TestSingleToken("=", HtmlSymbolType.Equals); + } + + [Fact] + public void DoubleQuote_Is_Recognized() + { + TestSingleToken("\"", HtmlSymbolType.DoubleQuote); + } + + [Fact] + public void SingleQuote_Is_Recognized() + { + TestSingleToken("'", HtmlSymbolType.SingleQuote); + } + + [Fact] + public void Transition_Is_Recognized() + { + TestSingleToken("@", HtmlSymbolType.Transition); + } + + [Fact] + public void DoubleHyphen_Is_Recognized() + { + TestSingleToken("--", HtmlSymbolType.DoubleHyphen); + } + + [Fact] + public void SingleHyphen_Is_Not_Recognized() + { + TestSingleToken("-", HtmlSymbolType.Text); + } + + [Fact] + public void SingleHyphen_Mid_Text_Is_Not_Recognized_As_Separate_Token() + { + TestSingleToken("foo-bar", HtmlSymbolType.Text); + } + + [Fact] + public void Next_Ignores_Star_At_EOF_In_RazorComment() + { + TestTokenizer("@* Foo * Bar * Baz *", + new HtmlSymbol(0, 0, 0, "@", HtmlSymbolType.RazorCommentTransition), + new HtmlSymbol(1, 0, 1, "*", HtmlSymbolType.RazorCommentStar), + new HtmlSymbol(2, 0, 2, " Foo * Bar * Baz *", HtmlSymbolType.RazorComment)); + } + + [Fact] + public void Next_Ignores_Star_Without_Trailing_At() + { + TestTokenizer("@* Foo * Bar * Baz *@", + new HtmlSymbol(0, 0, 0, "@", HtmlSymbolType.RazorCommentTransition), + new HtmlSymbol(1, 0, 1, "*", HtmlSymbolType.RazorCommentStar), + new HtmlSymbol(2, 0, 2, " Foo * Bar * Baz ", HtmlSymbolType.RazorComment), + new HtmlSymbol(19, 0, 19, "*", HtmlSymbolType.RazorCommentStar), + new HtmlSymbol(20, 0, 20, "@", HtmlSymbolType.RazorCommentTransition)); + } + + [Fact] + public void Next_Returns_RazorComment_Token_For_Entire_Razor_Comment() + { + TestTokenizer("@* Foo Bar Baz *@", + new HtmlSymbol(0, 0, 0, "@", HtmlSymbolType.RazorCommentTransition), + new HtmlSymbol(1, 0, 1, "*", HtmlSymbolType.RazorCommentStar), + new HtmlSymbol(2, 0, 2, " Foo Bar Baz ", HtmlSymbolType.RazorComment), + new HtmlSymbol(15, 0, 15, "*", HtmlSymbolType.RazorCommentStar), + new HtmlSymbol(16, 0, 16, "@", HtmlSymbolType.RazorCommentTransition)); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Tokenizer/HtmlTokenizerTestBase.cs b/test/Microsoft.AspNet.Razor.Test/Tokenizer/HtmlTokenizerTestBase.cs new file mode 100644 index 0000000000..923021d051 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Tokenizer/HtmlTokenizerTestBase.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Text; +using Microsoft.AspNet.Razor.Tokenizer; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; + +namespace Microsoft.AspNet.Razor.Test.Tokenizer +{ + public abstract class HtmlTokenizerTestBase : TokenizerTestBase + { + private static HtmlSymbol _ignoreRemaining = new HtmlSymbol(0, 0, 0, String.Empty, HtmlSymbolType.Unknown); + + protected override HtmlSymbol IgnoreRemaining + { + get { return _ignoreRemaining; } + } + + protected override Tokenizer CreateTokenizer(ITextDocument source) + { + return new HtmlTokenizer(source); + } + + protected void TestSingleToken(string text, HtmlSymbolType expectedSymbolType) + { + TestTokenizer(text, new HtmlSymbol(0, 0, 0, text, expectedSymbolType)); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Tokenizer/TokenizerLookaheadTest.cs b/test/Microsoft.AspNet.Razor.Test/Tokenizer/TokenizerLookaheadTest.cs new file mode 100644 index 0000000000..4549843549 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Tokenizer/TokenizerLookaheadTest.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.IO; +using Microsoft.AspNet.Razor.Text; +using Microsoft.AspNet.Razor.Tokenizer; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Tokenizer +{ + public class TokenizerLookaheadTest : HtmlTokenizerTestBase + { + [Fact] + public void After_Cancelling_Lookahead_Tokenizer_Returns_Same_Tokens_As_It_Did_Before_Lookahead() + { + HtmlTokenizer tokenizer = new HtmlTokenizer(new SeekableTextReader(new StringReader(""))); + using (tokenizer.Source.BeginLookahead()) + { + Assert.Equal(new HtmlSymbol(0, 0, 0, "<", HtmlSymbolType.OpenAngle), tokenizer.NextSymbol()); + Assert.Equal(new HtmlSymbol(1, 0, 1, "foo", HtmlSymbolType.Text), tokenizer.NextSymbol()); + Assert.Equal(new HtmlSymbol(4, 0, 4, ">", HtmlSymbolType.CloseAngle), tokenizer.NextSymbol()); + } + Assert.Equal(new HtmlSymbol(0, 0, 0, "<", HtmlSymbolType.OpenAngle), tokenizer.NextSymbol()); + Assert.Equal(new HtmlSymbol(1, 0, 1, "foo", HtmlSymbolType.Text), tokenizer.NextSymbol()); + Assert.Equal(new HtmlSymbol(4, 0, 4, ">", HtmlSymbolType.CloseAngle), tokenizer.NextSymbol()); + } + + [Fact] + public void After_Accepting_Lookahead_Tokenizer_Returns_Next_Token() + { + HtmlTokenizer tokenizer = new HtmlTokenizer(new SeekableTextReader(new StringReader(""))); + using (LookaheadToken lookahead = tokenizer.Source.BeginLookahead()) + { + Assert.Equal(new HtmlSymbol(0, 0, 0, "<", HtmlSymbolType.OpenAngle), tokenizer.NextSymbol()); + Assert.Equal(new HtmlSymbol(1, 0, 1, "foo", HtmlSymbolType.Text), tokenizer.NextSymbol()); + lookahead.Accept(); + } + Assert.Equal(new HtmlSymbol(4, 0, 4, ">", HtmlSymbolType.CloseAngle), tokenizer.NextSymbol()); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Tokenizer/TokenizerTestBase.cs b/test/Microsoft.AspNet.Razor.Test/Tokenizer/TokenizerTestBase.cs new file mode 100644 index 0000000000..0158bed6da --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Tokenizer/TokenizerTestBase.cs @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using System.IO; +using System.Text; +using Microsoft.AspNet.Razor.Text; +using Microsoft.AspNet.Razor.Tokenizer; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Tokenizer +{ + public abstract class TokenizerTestBase + where TSymbol : SymbolBase + { + protected abstract TSymbol IgnoreRemaining { get; } + protected abstract Tokenizer CreateTokenizer(ITextDocument source); + + protected void TestTokenizer(string input, params TSymbol[] expectedSymbols) + { + // Arrange + bool success = true; + StringBuilder output = new StringBuilder(); + using (StringReader reader = new StringReader(input)) + { + using (SeekableTextReader source = new SeekableTextReader(reader)) + { + Tokenizer tokenizer = CreateTokenizer(source); + int counter = 0; + TSymbol current = null; + while ((current = tokenizer.NextSymbol()) != null) + { + if (counter >= expectedSymbols.Length) + { + output.AppendLine(String.Format("F: Expected: << Nothing >>; Actual: {0}", current)); + success = false; + } + else if (ReferenceEquals(expectedSymbols[counter], IgnoreRemaining)) + { + output.AppendLine(String.Format("P: Ignored {0}", current)); + } + else + { + if (!Equals(expectedSymbols[counter], current)) + { + output.AppendLine(String.Format("F: Expected: {0}; Actual: {1}", expectedSymbols[counter], current)); + success = false; + } + else + { + output.AppendLine(String.Format("P: Expected: {0}", expectedSymbols[counter])); + } + counter++; + } + } + if (counter < expectedSymbols.Length && !ReferenceEquals(expectedSymbols[counter], IgnoreRemaining)) + { + success = false; + for (; counter < expectedSymbols.Length; counter++) + { + output.AppendLine(String.Format("F: Expected: {0}; Actual: << None >>", expectedSymbols[counter])); + } + } + } + } + Assert.True(success, "\r\n" + output.ToString()); + WriteTraceLine(output.Replace("{", "{{").Replace("}", "}}").ToString()); + } + + [Conditional("PARSER_TRACE")] + private static void WriteTraceLine(string format, params object[] args) + { + Trace.WriteLine(String.Format(format, args)); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Tokenizer/VBTokenizerCommentTest.cs b/test/Microsoft.AspNet.Razor.Test/Tokenizer/VBTokenizerCommentTest.cs new file mode 100644 index 0000000000..046fc36485 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Tokenizer/VBTokenizerCommentTest.cs @@ -0,0 +1,93 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Tokenizer +{ + public class VBTokenizerCommentTest : VBTokenizerTestBase + { + [Fact] + public void Next_Ignores_Star_At_EOF_In_RazorComment() + { + TestTokenizer("@* Foo * Bar * Baz *", + new VBSymbol(0, 0, 0, "@", VBSymbolType.RazorCommentTransition), + new VBSymbol(1, 0, 1, "*", VBSymbolType.RazorCommentStar), + new VBSymbol(2, 0, 2, " Foo * Bar * Baz *", VBSymbolType.RazorComment)); + } + + [Fact] + public void Next_Ignores_Star_Without_Trailing_At() + { + TestTokenizer("@* Foo * Bar * Baz *@", + new VBSymbol(0, 0, 0, "@", VBSymbolType.RazorCommentTransition), + new VBSymbol(1, 0, 1, "*", VBSymbolType.RazorCommentStar), + new VBSymbol(2, 0, 2, " Foo * Bar * Baz ", VBSymbolType.RazorComment), + new VBSymbol(19, 0, 19, "*", VBSymbolType.RazorCommentStar), + new VBSymbol(20, 0, 20, "@", VBSymbolType.RazorCommentTransition)); + } + + [Fact] + public void Next_Returns_RazorComment_Token_For_Entire_Razor_Comment() + { + TestTokenizer("@* Foo Bar Baz *@", + new VBSymbol(0, 0, 0, "@", VBSymbolType.RazorCommentTransition), + new VBSymbol(1, 0, 1, "*", VBSymbolType.RazorCommentStar), + new VBSymbol(2, 0, 2, " Foo Bar Baz ", VBSymbolType.RazorComment), + new VBSymbol(15, 0, 15, "*", VBSymbolType.RazorCommentStar), + new VBSymbol(16, 0, 16, "@", VBSymbolType.RazorCommentTransition)); + } + + [Fact] + public void Tick_Comment_Is_Recognized() + { + TestTokenizer("' Foo Bar Baz", new VBSymbol(0, 0, 0, "' Foo Bar Baz", VBSymbolType.Comment)); + } + + [Fact] + public void Tick_Comment_Is_Terminated_By_Newline() + { + TestTokenizer("' Foo Bar Baz\na", new VBSymbol(0, 0, 0, "' Foo Bar Baz", VBSymbolType.Comment), IgnoreRemaining); + } + + [Fact] + public void LeftQuote_Comment_Is_Recognized() + { + // U+2018 - Left Quote: ‘ + TestTokenizer("‘ Foo Bar Baz", new VBSymbol(0, 0, 0, "‘ Foo Bar Baz", VBSymbolType.Comment)); + } + + [Fact] + public void LeftQuote_Comment_Is_Terminated_By_Newline() + { + // U+2018 - Left Quote: ‘ + TestTokenizer("‘ Foo Bar Baz\na", new VBSymbol(0, 0, 0, "‘ Foo Bar Baz", VBSymbolType.Comment), IgnoreRemaining); + } + + [Fact] + public void RightQuote_Comment_Is_Recognized() + { + // U+2019 - Right Quote: ’ + TestTokenizer("’ Foo Bar Baz", new VBSymbol(0, 0, 0, "’ Foo Bar Baz", VBSymbolType.Comment)); + } + + [Fact] + public void RightQuote_Comment_Is_Terminated_By_Newline() + { + // U+2019 - Right Quote: ’ + TestTokenizer("’ Foo Bar Baz\na", new VBSymbol(0, 0, 0, "’ Foo Bar Baz", VBSymbolType.Comment), IgnoreRemaining); + } + + [Fact] + public void Rem_Comment_Is_Recognized() + { + TestTokenizer("REM Foo Bar Baz", new VBSymbol(0, 0, 0, "REM Foo Bar Baz", VBSymbolType.Comment)); + } + + [Fact] + public void Rem_Comment_Is_Terminated_By_Newline() + { + TestTokenizer("REM Foo Bar Baz\na", new VBSymbol(0, 0, 0, "REM Foo Bar Baz", VBSymbolType.Comment), IgnoreRemaining); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Tokenizer/VBTokenizerIdentifierTest.cs b/test/Microsoft.AspNet.Razor.Test/Tokenizer/VBTokenizerIdentifierTest.cs new file mode 100644 index 0000000000..9593989a9a --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Tokenizer/VBTokenizerIdentifierTest.cs @@ -0,0 +1,267 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Tokenizer +{ + public class VBTokenizerIdentifierTest : VBTokenizerTestBase + { + [Fact] + public void Simple_Identifier_Is_Recognized() + { + TestTokenizer("foo", new VBSymbol(0, 0, 0, "foo", VBSymbolType.Identifier)); + } + + [Fact] + public void Escaped_Identifier_Terminates_At_EOF() + { + TestTokenizer("[foo", new VBSymbol(0, 0, 0, "[foo", VBSymbolType.Identifier)); + } + + [Fact] + public void Escaped_Identifier_Terminates_At_Whitespace() + { + TestTokenizer("[foo ", new VBSymbol(0, 0, 0, "[foo", VBSymbolType.Identifier), IgnoreRemaining); + } + + [Fact] + public void Escaped_Identifier_Terminates_At_RightBracket_And_Does_Not_Read_TypeCharacter() + { + TestTokenizer("[foo]&", new VBSymbol(0, 0, 0, "[foo]", VBSymbolType.Identifier), IgnoreRemaining); + } + + [Fact] + public void Identifier_Starting_With_Underscore_Is_Recognized() + { + TestTokenizer("_foo", new VBSymbol(0, 0, 0, "_foo", VBSymbolType.Identifier)); + } + + [Fact] + public void Identifier_Can_Contain_Digits() + { + TestTokenizer("foo4", new VBSymbol(0, 0, 0, "foo4", VBSymbolType.Identifier)); + } + + [Fact] + public void Identifier_Can_Start_With_Titlecase_Letter() + { + TestTokenizer("ῼfoo", new VBSymbol(0, 0, 0, "ῼfoo", VBSymbolType.Identifier)); + } + + [Fact] + public void Identifier_Can_Start_With_Letter_Modifier() + { + TestTokenizer("ᵊfoo", new VBSymbol(0, 0, 0, "ᵊfoo", VBSymbolType.Identifier)); + } + + [Fact] + public void Identifier_Can_Start_With_Other_Letter() + { + TestTokenizer("ƻfoo", new VBSymbol(0, 0, 0, "ƻfoo", VBSymbolType.Identifier)); + } + + [Fact] + public void Identifier_Can_Start_With_Number_Letter() + { + TestTokenizer("Ⅽool", new VBSymbol(0, 0, 0, "Ⅽool", VBSymbolType.Identifier)); + } + + [Fact] + public void Identifier_Can_Contain_Non_Spacing_Mark() + { + TestTokenizer("foo\u0300", new VBSymbol(0, 0, 0, "foo\u0300", VBSymbolType.Identifier)); + } + + [Fact] + public void Identifier_Can_Contain_Spacing_Combining_Mark() + { + TestTokenizer("fooः", new VBSymbol(0, 0, 0, "fooः", VBSymbolType.Identifier)); + } + + [Fact] + public void Identifier_Can_Contain_Non_English_Digit() + { + TestTokenizer("foo١", new VBSymbol(0, 0, 0, "foo١", VBSymbolType.Identifier)); + } + + [Fact] + public void Identifier_Can_Contain_Connector_Punctuation() + { + TestTokenizer("foo‿bar", new VBSymbol(0, 0, 0, "foo‿bar", VBSymbolType.Identifier)); + } + + [Fact] + public void Identifier_Can_Contain_Format_Character() + { + TestTokenizer("foo؃bar", new VBSymbol(0, 0, 0, "foo؃bar", VBSymbolType.Identifier)); + } + + [Fact] + public void Escaped_Keyword_Is_Recognized_As_Identifier() + { + TestSingleToken("[AddHandler]", VBSymbolType.Identifier); + } + + [Fact] + public void Keywords_Are_Recognized_As_Keyword_Tokens() + { + TestKeyword("AddHandler", VBKeyword.AddHandler); + TestKeyword("AndAlso", VBKeyword.AndAlso); + TestKeyword("Byte", VBKeyword.Byte); + TestKeyword("Catch", VBKeyword.Catch); + TestKeyword("CDate", VBKeyword.CDate); + TestKeyword("CInt", VBKeyword.CInt); + TestKeyword("Const", VBKeyword.Const); + TestKeyword("CSng", VBKeyword.CSng); + TestKeyword("CULng", VBKeyword.CULng); + TestKeyword("Declare", VBKeyword.Declare); + TestKeyword("DirectCast", VBKeyword.DirectCast); + TestKeyword("Else", VBKeyword.Else); + TestKeyword("Enum", VBKeyword.Enum); + TestKeyword("Exit", VBKeyword.Exit); + TestKeyword("Friend", VBKeyword.Friend); + TestKeyword("GetXmlNamespace", VBKeyword.GetXmlNamespace); + TestKeyword("Handles", VBKeyword.Handles); + TestKeyword("In", VBKeyword.In); + TestKeyword("Is", VBKeyword.Is); + TestKeyword("Like", VBKeyword.Like); + TestKeyword("Mod", VBKeyword.Mod); + TestKeyword("MyBase", VBKeyword.MyBase); + TestKeyword("New", VBKeyword.New); + TestKeyword("AddressOf", VBKeyword.AddressOf); + TestKeyword("As", VBKeyword.As); + TestKeyword("ByVal", VBKeyword.ByVal); + TestKeyword("CBool", VBKeyword.CBool); + TestKeyword("CDbl", VBKeyword.CDbl); + TestKeyword("Class", VBKeyword.Class); + TestKeyword("Continue", VBKeyword.Continue); + TestKeyword("CStr", VBKeyword.CStr); + TestKeyword("CUShort", VBKeyword.CUShort); + TestKeyword("Default", VBKeyword.Default); + TestKeyword("Do", VBKeyword.Do); + TestKeyword("ElseIf", VBKeyword.ElseIf); + TestKeyword("Erase", VBKeyword.Erase); + TestKeyword("False", VBKeyword.False); + TestKeyword("Function", VBKeyword.Function); + TestKeyword("Global", VBKeyword.Global); + TestKeyword("If", VBKeyword.If); + TestKeyword("Inherits", VBKeyword.Inherits); + TestKeyword("IsNot", VBKeyword.IsNot); + TestKeyword("Long", VBKeyword.Long); + TestKeyword("Module", VBKeyword.Module); + TestKeyword("MyClass", VBKeyword.MyClass); + TestKeyword("Next", VBKeyword.Next); + TestKeyword("Alias", VBKeyword.Alias); + TestKeyword("Boolean", VBKeyword.Boolean); + TestKeyword("Call", VBKeyword.Call); + TestKeyword("CByte", VBKeyword.CByte); + TestKeyword("CDec", VBKeyword.CDec); + TestKeyword("CLng", VBKeyword.CLng); + TestKeyword("CSByte", VBKeyword.CSByte); + TestKeyword("CType", VBKeyword.CType); + TestKeyword("Date", VBKeyword.Date); + TestKeyword("Delegate", VBKeyword.Delegate); + TestKeyword("Double", VBKeyword.Double); + TestKeyword("End", VBKeyword.End); + TestKeyword("Error", VBKeyword.Error); + TestKeyword("Finally", VBKeyword.Finally); + TestKeyword("Get", VBKeyword.Get); + TestKeyword("GoSub", VBKeyword.GoSub); + TestKeyword("Implements", VBKeyword.Implements); + TestKeyword("Integer", VBKeyword.Integer); + TestKeyword("Let", VBKeyword.Let); + TestKeyword("Loop", VBKeyword.Loop); + TestKeyword("MustInherit", VBKeyword.MustInherit); + TestKeyword("Namespace", VBKeyword.Namespace); + TestKeyword("Not", VBKeyword.Not); + TestKeyword("And", VBKeyword.And); + TestKeyword("ByRef", VBKeyword.ByRef); + TestKeyword("Case", VBKeyword.Case); + TestKeyword("CChar", VBKeyword.CChar); + TestKeyword("Char", VBKeyword.Char); + TestKeyword("CObj", VBKeyword.CObj); + TestKeyword("CShort", VBKeyword.CShort); + TestKeyword("CUInt", VBKeyword.CUInt); + TestKeyword("Decimal", VBKeyword.Decimal); + TestKeyword("Dim", VBKeyword.Dim); + TestKeyword("Each", VBKeyword.Each); + TestKeyword("EndIf", VBKeyword.EndIf); + TestKeyword("Event", VBKeyword.Event); + TestKeyword("For", VBKeyword.For); + TestKeyword("GetType", VBKeyword.GetType); + TestKeyword("GoTo", VBKeyword.GoTo); + TestKeyword("Imports", VBKeyword.Imports); + TestKeyword("Interface", VBKeyword.Interface); + TestKeyword("Lib", VBKeyword.Lib); + TestKeyword("Me", VBKeyword.Me); + TestKeyword("MustOverride", VBKeyword.MustOverride); + TestKeyword("Narrowing", VBKeyword.Narrowing); + TestKeyword("Nothing", VBKeyword.Nothing); + TestKeyword("NotInheritable", VBKeyword.NotInheritable); + TestKeyword("On", VBKeyword.On); + TestKeyword("Or", VBKeyword.Or); + TestKeyword("Overrides", VBKeyword.Overrides); + TestKeyword("Property", VBKeyword.Property); + TestKeyword("ReadOnly", VBKeyword.ReadOnly); + TestKeyword("Resume", VBKeyword.Resume); + TestKeyword("Set", VBKeyword.Set); + TestKeyword("Single", VBKeyword.Single); + TestKeyword("String", VBKeyword.String); + TestKeyword("Then", VBKeyword.Then); + TestKeyword("Try", VBKeyword.Try); + TestKeyword("ULong", VBKeyword.ULong); + TestKeyword("Wend", VBKeyword.Wend); + TestKeyword("With", VBKeyword.With); + TestKeyword("NotOverridable", VBKeyword.NotOverridable); + TestKeyword("Operator", VBKeyword.Operator); + TestKeyword("OrElse", VBKeyword.OrElse); + TestKeyword("ParamArray", VBKeyword.ParamArray); + TestKeyword("Protected", VBKeyword.Protected); + TestKeyword("ReDim", VBKeyword.ReDim); + TestKeyword("Return", VBKeyword.Return); + TestKeyword("Shadows", VBKeyword.Shadows); + TestKeyword("Static", VBKeyword.Static); + TestKeyword("Structure", VBKeyword.Structure); + TestKeyword("Throw", VBKeyword.Throw); + TestKeyword("TryCast", VBKeyword.TryCast); + TestKeyword("UShort", VBKeyword.UShort); + TestKeyword("When", VBKeyword.When); + TestKeyword("WithEvents", VBKeyword.WithEvents); + TestKeyword("Object", VBKeyword.Object); + TestKeyword("Option", VBKeyword.Option); + TestKeyword("Overloads", VBKeyword.Overloads); + TestKeyword("Partial", VBKeyword.Partial); + TestKeyword("Public", VBKeyword.Public); + TestKeyword("SByte", VBKeyword.SByte); + TestKeyword("Shared", VBKeyword.Shared); + TestKeyword("Step", VBKeyword.Step); + TestKeyword("Sub", VBKeyword.Sub); + TestKeyword("To", VBKeyword.To); + TestKeyword("TypeOf", VBKeyword.TypeOf); + TestKeyword("Using", VBKeyword.Using); + TestKeyword("While", VBKeyword.While); + TestKeyword("WriteOnly", VBKeyword.WriteOnly); + TestKeyword("Of", VBKeyword.Of); + TestKeyword("Optional", VBKeyword.Optional); + TestKeyword("Overridable", VBKeyword.Overridable); + TestKeyword("Private", VBKeyword.Private); + TestKeyword("RaiseEvent", VBKeyword.RaiseEvent); + TestKeyword("RemoveHandler", VBKeyword.RemoveHandler); + TestKeyword("Select", VBKeyword.Select); + TestKeyword("Short", VBKeyword.Short); + TestKeyword("Stop", VBKeyword.Stop); + TestKeyword("SyncLock", VBKeyword.SyncLock); + TestKeyword("True", VBKeyword.True); + TestKeyword("UInteger", VBKeyword.UInteger); + TestKeyword("Variant", VBKeyword.Variant); + TestKeyword("Widening", VBKeyword.Widening); + TestKeyword("Xor", VBKeyword.Xor); + } + + private void TestKeyword(string keyword, VBKeyword keywordType) + { + TestTokenizer(keyword, new VBSymbol(0, 0, 0, keyword, VBSymbolType.Keyword) { Keyword = keywordType }); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Tokenizer/VBTokenizerLiteralTest.cs b/test/Microsoft.AspNet.Razor.Test/Tokenizer/VBTokenizerLiteralTest.cs new file mode 100644 index 0000000000..9d321e3690 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Tokenizer/VBTokenizerLiteralTest.cs @@ -0,0 +1,182 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Tokenizer +{ + public class VBTokenizerLiteralTest : VBTokenizerTestBase + { + [Fact] + public void Decimal_Integer_Literal_Is_Recognized() + { + TestSingleToken("01189998819991197253", VBSymbolType.IntegerLiteral); + } + + [Fact] + public void Integer_Type_Suffixes_Are_Recognized_In_Decimal_Literal() + { + TestSingleToken("42S", VBSymbolType.IntegerLiteral); + TestSingleToken("42I", VBSymbolType.IntegerLiteral); + TestSingleToken("42L", VBSymbolType.IntegerLiteral); + TestSingleToken("42US", VBSymbolType.IntegerLiteral); + TestSingleToken("42UI", VBSymbolType.IntegerLiteral); + TestSingleToken("42UL", VBSymbolType.IntegerLiteral); + } + + [Fact] + public void Hex_Integer_Literal_Is_Recognized() + { + TestSingleToken("&HDeadBeef", VBSymbolType.IntegerLiteral); + } + + [Fact] + public void Integer_Type_Suffixes_Are_Recognized_In_Hex_Literal() + { + TestSingleToken("&HDeadBeefS", VBSymbolType.IntegerLiteral); + TestSingleToken("&HDeadBeefI", VBSymbolType.IntegerLiteral); + TestSingleToken("&HDeadBeefL", VBSymbolType.IntegerLiteral); + TestSingleToken("&HDeadBeefUS", VBSymbolType.IntegerLiteral); + TestSingleToken("&HDeadBeefUI", VBSymbolType.IntegerLiteral); + TestSingleToken("&HDeadBeefUL", VBSymbolType.IntegerLiteral); + } + + [Fact] + public void Octal_Integer_Literal_Is_Recognized() + { + TestSingleToken("&O77", VBSymbolType.IntegerLiteral); + } + + [Fact] + public void Integer_Type_Suffixes_Are_Recognized_In_Octal_Literal() + { + TestSingleToken("&O77S", VBSymbolType.IntegerLiteral); + TestSingleToken("&O77I", VBSymbolType.IntegerLiteral); + TestSingleToken("&O77L", VBSymbolType.IntegerLiteral); + TestSingleToken("&O77US", VBSymbolType.IntegerLiteral); + TestSingleToken("&O77UI", VBSymbolType.IntegerLiteral); + TestSingleToken("&O77UL", VBSymbolType.IntegerLiteral); + } + + [Fact] + public void Incomplete_Type_Suffix_Is_Recognized() + { + TestSingleToken("42U", VBSymbolType.IntegerLiteral); + TestSingleToken("&H42U", VBSymbolType.IntegerLiteral); + TestSingleToken("&O77U", VBSymbolType.IntegerLiteral); + } + + [Fact] + public void Integer_With_FloatingPoint_Type_Suffix_Is_Recognized_As_FloatingPointLiteral() + { + TestSingleToken("42F", VBSymbolType.FloatingPointLiteral); + TestSingleToken("42R", VBSymbolType.FloatingPointLiteral); + TestSingleToken("42D", VBSymbolType.FloatingPointLiteral); + } + + [Fact] + public void Simple_FloatingPoint_Is_Recognized() + { + TestSingleToken("3.14159", VBSymbolType.FloatingPointLiteral); + } + + [Fact] + public void Integer_With_Exponent_Is_Recognized() + { + TestSingleToken("1E10", VBSymbolType.FloatingPointLiteral); + TestSingleToken("1e10", VBSymbolType.FloatingPointLiteral); + TestSingleToken("1E+10", VBSymbolType.FloatingPointLiteral); + TestSingleToken("1e+10", VBSymbolType.FloatingPointLiteral); + TestSingleToken("1E-10", VBSymbolType.FloatingPointLiteral); + TestSingleToken("1e-10", VBSymbolType.FloatingPointLiteral); + } + + [Fact] + public void Simple_FloatingPoint_With_Exponent_Is_Recognized() + { + TestSingleToken("3.14159e10", VBSymbolType.FloatingPointLiteral); + } + + [Fact] + public void FloatingPoint_Between_Zero_And_One_Is_Recognized() + { + TestSingleToken(".314159e1", VBSymbolType.FloatingPointLiteral); + } + + [Fact] + public void Simple_String_Literal_Is_Recognized() + { + TestSingleToken("\"Foo Bar Baz\"", VBSymbolType.StringLiteral); + } + + [Fact] + public void Two_Double_Quotes_Are_Recognized_As_Escape_Sequence() + { + TestSingleToken("\"Foo \"\"Bar\"\" Baz\"", VBSymbolType.StringLiteral); + } + + [Fact] + public void String_Literal_Is_Terminated_At_EOF() + { + TestSingleToken("\"Foo", VBSymbolType.StringLiteral); + } + + [Fact] + public void String_Literal_Is_Terminated_At_EOL() + { + TestTokenizer("\"Foo\nBar", new VBSymbol(0, 0, 0, "\"Foo", VBSymbolType.StringLiteral), IgnoreRemaining); + } + + [Fact] + public void Character_Literal_Is_Recognized_By_Trailing_C_After_String_Literal() + { + TestSingleToken("\"abc\"c", VBSymbolType.CharacterLiteral); + } + + [Fact] + public void LeftDoubleQuote_Is_Valid_DoubleQuote() + { + // Repeat all the above tests with Unicode Left Double Quote Character U+201C: “ + TestSingleToken("“Foo Bar Baz“", VBSymbolType.StringLiteral); + TestSingleToken("“Foo ““Bar““ Baz“", VBSymbolType.StringLiteral); + TestSingleToken("“Foo", VBSymbolType.StringLiteral); + TestSingleToken("“abc“c", VBSymbolType.CharacterLiteral); + TestTokenizer("“Foo\nBar", new VBSymbol(0, 0, 0, "“Foo", VBSymbolType.StringLiteral), IgnoreRemaining); + } + + [Fact] + public void RightDoubleQuote_Is_Valid_DoubleQuote() + { + // Repeat all the above tests with Unicode Right Double Quote Character U+201D: ” + TestSingleToken("”Foo Bar Baz”", VBSymbolType.StringLiteral); + TestSingleToken("”Foo ””Bar”” Baz”", VBSymbolType.StringLiteral); + TestSingleToken("”Foo", VBSymbolType.StringLiteral); + TestSingleToken("”abc”c", VBSymbolType.CharacterLiteral); + TestTokenizer("”Foo\nBar", new VBSymbol(0, 0, 0, "”Foo", VBSymbolType.StringLiteral), IgnoreRemaining); + } + + [Fact] + public void DateLiteral_Is_Recognized() + { + TestSingleToken("# 8/23/1970 3:45:39AM #", VBSymbolType.DateLiteral); + } + + [Fact] + public void DateLiteral_Is_Terminated_At_EndHash() + { + TestTokenizer("# 8/23/1970 # 3:45:39AM", new VBSymbol(0, 0, 0, "# 8/23/1970 #", VBSymbolType.DateLiteral), IgnoreRemaining); + } + + [Fact] + public void DateLiteral_Is_Terminated_At_EOF() + { + TestSingleToken("# 8/23/1970 3:45:39AM", VBSymbolType.DateLiteral); + } + + [Fact] + public void DateLiteral_Is_Terminated_At_EOL() + { + TestTokenizer("# 8/23/1970\n3:45:39AM", new VBSymbol(0, 0, 0, "# 8/23/1970", VBSymbolType.DateLiteral), IgnoreRemaining); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Tokenizer/VBTokenizerOperatorsTest.cs b/test/Microsoft.AspNet.Razor.Test/Tokenizer/VBTokenizerOperatorsTest.cs new file mode 100644 index 0000000000..ed12976043 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Tokenizer/VBTokenizerOperatorsTest.cs @@ -0,0 +1,154 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Tokenizer +{ + public class VBTokenizerOperatorsTest : VBTokenizerTestBase + { + [Fact] + public void Line_Continuation_Character_Is_Recognized() + { + TestSingleToken("_", VBSymbolType.LineContinuation); + } + + [Fact] + public void LeftParen_Is_Recognized() + { + TestSingleToken("(", VBSymbolType.LeftParenthesis); + } + + [Fact] + public void RightParen_Is_Recognized() + { + TestSingleToken(")", VBSymbolType.RightParenthesis); + } + + [Fact] + public void LeftBracket_Is_Recognized() + { + TestSingleToken("[", VBSymbolType.LeftBracket); + } + + [Fact] + public void RightBracket_Is_Recognized() + { + TestSingleToken("]", VBSymbolType.RightBracket); + } + + [Fact] + public void LeftBrace_Is_Recognized() + { + TestSingleToken("{", VBSymbolType.LeftBrace); + } + + [Fact] + public void RightBrace_Is_Recognized() + { + TestSingleToken("}", VBSymbolType.RightBrace); + } + + [Fact] + public void Bang_Is_Recognized() + { + TestSingleToken("!", VBSymbolType.Bang); + } + + [Fact] + public void Hash_Is_Recognized() + { + TestSingleToken("#", VBSymbolType.Hash); + } + + [Fact] + public void Comma_Is_Recognized() + { + TestSingleToken(",", VBSymbolType.Comma); + } + + [Fact] + public void Dot_Is_Recognized() + { + TestSingleToken(".", VBSymbolType.Dot); + } + + [Fact] + public void Colon_Is_Recognized() + { + TestSingleToken(":", VBSymbolType.Colon); + } + + [Fact] + public void QuestionMark_Is_Recognized() + { + TestSingleToken("?", VBSymbolType.QuestionMark); + } + + [Fact] + public void Concatenation_Is_Recognized() + { + TestSingleToken("&", VBSymbolType.Concatenation); + } + + [Fact] + public void Multiply_Is_Recognized() + { + TestSingleToken("*", VBSymbolType.Multiply); + } + + [Fact] + public void Add_Is_Recognized() + { + TestSingleToken("+", VBSymbolType.Add); + } + + [Fact] + public void Subtract_Is_Recognized() + { + TestSingleToken("-", VBSymbolType.Subtract); + } + + [Fact] + public void Divide_Is_Recognized() + { + TestSingleToken("/", VBSymbolType.Divide); + } + + [Fact] + public void IntegerDivide_Is_Recognized() + { + TestSingleToken("\\", VBSymbolType.IntegerDivide); + } + + [Fact] + public void Exponentiation_Is_Recognized() + { + TestSingleToken("^", VBSymbolType.Exponentiation); + } + + [Fact] + public void Equal_Is_Recognized() + { + TestSingleToken("=", VBSymbolType.Equal); + } + + [Fact] + public void LessThan_Is_Recognized() + { + TestSingleToken("<", VBSymbolType.LessThan); + } + + [Fact] + public void GreaterThan_Is_Recognized() + { + TestSingleToken(">", VBSymbolType.GreaterThan); + } + + [Fact] + public void Dollar_Is_Recognized() + { + TestSingleToken("$", VBSymbolType.Dollar); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Tokenizer/VBTokenizerTest.cs b/test/Microsoft.AspNet.Razor.Test/Tokenizer/VBTokenizerTest.cs new file mode 100644 index 0000000000..a33ff2a768 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Tokenizer/VBTokenizerTest.cs @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Tokenizer; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Tokenizer +{ + public class VBTokenizerTest : VBTokenizerTestBase + { + [Fact] + public void Constructor_Throws_ArgNull_If_Null_Source_Provided() + { + Assert.ThrowsArgumentNull(() => new CSharpTokenizer(null), "source"); + } + + [Fact] + public void Next_Returns_Null_When_EOF_Reached() + { + TestTokenizer(""); + } + + [Fact] + public void Next_Returns_Newline_Token_For_Single_CR() + { + TestTokenizer("\r\ra", + new VBSymbol(0, 0, 0, "\r", VBSymbolType.NewLine), + new VBSymbol(1, 1, 0, "\r", VBSymbolType.NewLine), + IgnoreRemaining); + } + + [Fact] + public void Next_Returns_Newline_Token_For_Single_LF() + { + TestTokenizer("\n\na", + new VBSymbol(0, 0, 0, "\n", VBSymbolType.NewLine), + new VBSymbol(1, 1, 0, "\n", VBSymbolType.NewLine), + IgnoreRemaining); + } + + [Fact] + public void Next_Returns_Newline_Token_For_Single_NEL() + { + // NEL: Unicode "Next Line" U+0085 + TestTokenizer("\u0085\u0085a", + new VBSymbol(0, 0, 0, "\u0085", VBSymbolType.NewLine), + new VBSymbol(1, 1, 0, "\u0085", VBSymbolType.NewLine), + IgnoreRemaining); + } + + [Fact] + public void Next_Returns_Newline_Token_For_Single_Line_Separator() + { + // Unicode "Line Separator" U+2028 + TestTokenizer("\u2028\u2028a", + new VBSymbol(0, 0, 0, "\u2028", VBSymbolType.NewLine), + new VBSymbol(1, 1, 0, "\u2028", VBSymbolType.NewLine), + IgnoreRemaining); + } + + [Fact] + public void Next_Returns_Newline_Token_For_Single_Paragraph_Separator() + { + // Unicode "Paragraph Separator" U+2029 + TestTokenizer("\u2029\u2029a", + new VBSymbol(0, 0, 0, "\u2029", VBSymbolType.NewLine), + new VBSymbol(1, 1, 0, "\u2029", VBSymbolType.NewLine), + IgnoreRemaining); + } + + [Fact] + public void Next_Returns_Single_Newline_Token_For_CRLF() + { + TestTokenizer("\r\n\r\na", + new VBSymbol(0, 0, 0, "\r\n", VBSymbolType.NewLine), + new VBSymbol(2, 1, 0, "\r\n", VBSymbolType.NewLine), + IgnoreRemaining); + } + + [Fact] + public void Next_Returns_Token_For_Whitespace_Characters() + { + TestTokenizer(" \f\t\u000B \n ", + new VBSymbol(0, 0, 0, " \f\t\u000B ", VBSymbolType.WhiteSpace), + new VBSymbol(5, 0, 5, "\n", VBSymbolType.NewLine), + new VBSymbol(6, 1, 0, " ", VBSymbolType.WhiteSpace)); + } + + [Fact] + public void Transition_Is_Recognized() + { + TestSingleToken("@", VBSymbolType.Transition); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Tokenizer/VBTokenizerTestBase.cs b/test/Microsoft.AspNet.Razor.Test/Tokenizer/VBTokenizerTestBase.cs new file mode 100644 index 0000000000..d965cccb86 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Tokenizer/VBTokenizerTestBase.cs @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNet.Razor.Text; +using Microsoft.AspNet.Razor.Tokenizer; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; + +namespace Microsoft.AspNet.Razor.Test.Tokenizer +{ + public abstract class VBTokenizerTestBase : TokenizerTestBase + { + private static VBSymbol _ignoreRemaining = new VBSymbol(0, 0, 0, String.Empty, VBSymbolType.Unknown); + + protected override VBSymbol IgnoreRemaining + { + get { return _ignoreRemaining; } + } + + protected override Tokenizer CreateTokenizer(ITextDocument source) + { + return new VBTokenizer(source); + } + + protected void TestSingleToken(string text, VBSymbolType expectedSymbolType) + { + TestTokenizer(text, new VBSymbol(0, 0, 0, text, expectedSymbolType)); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Utils/DisposableActionTest.cs b/test/Microsoft.AspNet.Razor.Test/Utils/DisposableActionTest.cs new file mode 100644 index 0000000000..c6e24f5d3b --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Utils/DisposableActionTest.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Utils; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Utils +{ + public class DisposableActionTest + { + [Fact] + public void ConstructorRequiresNonNullAction() + { + Assert.ThrowsArgumentNull(() => new DisposableAction(null), "action"); + } + + [Fact] + public void ActionIsExecutedOnDispose() + { + // Arrange + bool called = false; + DisposableAction action = new DisposableAction(() => { called = true; }); + + // Act + action.Dispose(); + + // Assert + Assert.True(called, "The action was not run when the DisposableAction was disposed"); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Utils/EnumerableUtils.cs b/test/Microsoft.AspNet.Razor.Test/Utils/EnumerableUtils.cs new file mode 100644 index 0000000000..9edc3e26d8 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Utils/EnumerableUtils.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.AspNet.Razor.Test.Utils +{ + public static class EnumerableUtils + { + public static void RunPairwise(IEnumerable left, IEnumerable right, Action action) + { + IEnumerator leftEnum = left.GetEnumerator(); + IEnumerator rightEnum = right.GetEnumerator(); + while (leftEnum.MoveNext() && rightEnum.MoveNext()) + { + action(leftEnum.Current, rightEnum.Current); + } + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Utils/MiscAssert.cs b/test/Microsoft.AspNet.Razor.Test/Utils/MiscAssert.cs new file mode 100644 index 0000000000..efec9f268a --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Utils/MiscAssert.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Linq.Expressions; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Utils +{ + public static class MiscAssert + { + public static void AssertBothNullOrPropertyEqual(T expected, T actual, Expression> propertyExpr, string objectName) + { + // Unpack convert expressions + Expression expr = propertyExpr.Body; + while (expr.NodeType == ExpressionType.Convert) + { + expr = ((UnaryExpression)expr).Operand; + } + + string propertyName = ((MemberExpression)expr).Member.Name; + Func property = propertyExpr.Compile(); + + if (expected == null) + { + Assert.Null(actual); + } + else + { + Assert.NotNull(actual); + Assert.Equal(property(expected), property(actual)); + } + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Utils/MiscUtils.cs b/test/Microsoft.AspNet.Razor.Test/Utils/MiscUtils.cs new file mode 100644 index 0000000000..7e046962cd --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Utils/MiscUtils.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using System.Text.RegularExpressions; +using System.Threading; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Utils +{ + class MiscUtils + { + public const int TimeoutInSeconds = 1; + + public static string StripRuntimeVersion(string s) + { + return Regex.Replace(s, @"Runtime Version:[\d.]*", "Runtime Version:N.N.NNNNN.N"); + } + + public static void DoWithTimeoutIfNotDebugging(Func withTimeout) + { +#if DEBUG + if (Debugger.IsAttached) + { + withTimeout(Timeout.Infinite); + } + else + { +#endif + Assert.True(withTimeout((int)TimeSpan.FromSeconds(TimeoutInSeconds).TotalMilliseconds), "Timeout expired!"); +#if DEBUG + } +#endif + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/Utils/SpanAssert.cs b/test/Microsoft.AspNet.Razor.Test/Utils/SpanAssert.cs new file mode 100644 index 0000000000..7a801aabf5 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Utils/SpanAssert.cs @@ -0,0 +1,59 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.Text; +using Microsoft.TestCommon; + +namespace Microsoft.AspNet.Razor.Test.Utils +{ + public static class EventAssert + { + public static void NoMoreSpans(IEnumerator enumerator) + { + IList tokens = new List(); + while (enumerator.MoveNext()) + { + tokens.Add(enumerator.Current); + } + + Assert.False(tokens.Count > 0, String.Format(CultureInfo.InvariantCulture, @"There are more tokens available from the source: {0}", FormatList(tokens))); + } + + private static string FormatList(IList items) + { + StringBuilder tokenString = new StringBuilder(); + foreach (T item in items) + { + tokenString.AppendLine(item.ToString()); + } + return tokenString.ToString(); + } + + public static void NextSpanIs(IEnumerator enumerator, SpanKind type, string content, SourceLocation location) + { + Assert.True(enumerator.MoveNext(), "There is no next token!"); + IsSpan(enumerator.Current, type, content, location); + } + + public static void NextSpanIs(IEnumerator enumerator, SpanKind type, string content, int actualIndex, int lineIndex, int charIndex) + { + NextSpanIs(enumerator, type, content, new SourceLocation(actualIndex, lineIndex, charIndex)); + } + + public static void IsSpan(Span tok, SpanKind type, string content, int actualIndex, int lineIndex, int charIndex) + { + IsSpan(tok, type, content, new SourceLocation(actualIndex, lineIndex, charIndex)); + } + + public static void IsSpan(Span tok, SpanKind type, string content, SourceLocation location) + { + Assert.Equal(content, tok.Content); + Assert.Equal(type, tok.Kind); + Assert.Equal(location, tok.Start); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/VBRazorCodeLanguageTest.cs b/test/Microsoft.AspNet.Razor.Test/VBRazorCodeLanguageTest.cs new file mode 100644 index 0000000000..aad962e324 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/VBRazorCodeLanguageTest.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.TestCommon; +using Microsoft.VisualBasic; + +namespace Microsoft.AspNet.Razor.Test +{ + public class VBRazorCodeLanguageTest + { + [Fact] + public void CreateCodeParserReturnsNewVBCodeParser() + { + // Arrange + RazorCodeLanguage service = new VBRazorCodeLanguage(); + + // Act + ParserBase parser = service.CreateCodeParser(); + + // Assert + Assert.NotNull(parser); + Assert.IsType(parser); + } + + [Fact] + public void CreateCodeGeneratorParserListenerReturnsNewCSharpCodeGeneratorParserListener() + { + // Arrange + RazorCodeLanguage service = new VBRazorCodeLanguage(); + + // Act + RazorEngineHost host = new RazorEngineHost(new VBRazorCodeLanguage()); + RazorCodeGenerator generator = service.CreateCodeGenerator("Foo", "Bar", "Baz", host); + + // Assert + Assert.NotNull(generator); + Assert.IsType(generator); + Assert.Equal("Foo", generator.ClassName); + Assert.Equal("Bar", generator.RootNamespaceName); + Assert.Equal("Baz", generator.SourceFileName); + Assert.Same(host, generator.Host); + } + + [Fact] + public void CodeDomProviderTypeReturnsVBCodeProvider() + { + // Arrange + RazorCodeLanguage service = new VBRazorCodeLanguage(); + + // Assert + Assert.Equal(typeof(VBCodeProvider), service.CodeDomProviderType); + } + } +} diff --git a/test/Microsoft.AspNet.Razor.Test/packages.config b/test/Microsoft.AspNet.Razor.Test/packages.config new file mode 100644 index 0000000000..b19e59458c --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/test/Microsoft.TestCommon/Assert.cs b/test/Microsoft.TestCommon/Assert.cs new file mode 100644 index 0000000000..a33f4c5390 --- /dev/null +++ b/test/Microsoft.TestCommon/Assert.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +namespace Microsoft.TestCommon +{ + // This extends xUnit.net's Assert class, and makes it partial so that we can + // organize the extension points by logical functionality (rather than dumping them + // all into this single file). + // + // See files named XxxAssertions for root extensions to Assert. + public partial class Assert : Xunit.Assert + { + public static readonly ReflectionAssert Reflection = new ReflectionAssert(); + + public static readonly TypeAssert Type = new TypeAssert(); + + public static readonly HttpAssert Http = new HttpAssert(); + + public static readonly MediaTypeAssert MediaType = new MediaTypeAssert(); + + public static readonly GenericTypeAssert GenericType = new GenericTypeAssert(); + + public static readonly SerializerAssert Serializer = new SerializerAssert(); + + public static readonly StreamAssert Stream = new StreamAssert(); + + public static readonly TaskAssert Task = new TaskAssert(); + + public static readonly XmlAssert Xml = new XmlAssert(); + } +} diff --git a/test/Microsoft.TestCommon/CultureReplacer.cs b/test/Microsoft.TestCommon/CultureReplacer.cs new file mode 100644 index 0000000000..fb0e598537 --- /dev/null +++ b/test/Microsoft.TestCommon/CultureReplacer.cs @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Globalization; +using System.Threading; + +namespace Microsoft.TestCommon +{ + public class CultureReplacer : IDisposable + { + private const string _defaultCultureName = "en-GB"; + private const string _defaultUICultureName = "en-US"; + private static readonly CultureInfo _defaultCulture = CultureInfo.GetCultureInfo(_defaultCultureName); + private readonly CultureInfo _originalCulture; + private readonly CultureInfo _originalUICulture; + private readonly long _threadId; + + // Culture => Formatting of dates/times/money/etc, defaults to en-GB because en-US is the same as InvariantCulture + // We want to be able to find issues where the InvariantCulture is used, but a specific culture should be. + // + // UICulture => Language + public CultureReplacer(string culture = _defaultCultureName, string uiCulture = _defaultUICultureName) + { + _originalCulture = Thread.CurrentThread.CurrentCulture; + _originalUICulture = Thread.CurrentThread.CurrentUICulture; + _threadId = Thread.CurrentThread.ManagedThreadId; + + Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(culture); + Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(uiCulture); + } + + /// + /// The name of the culture that is used as the default value for Thread.CurrentCulture when CultureReplacer is used. + /// + public static string DefaultCultureName + { + get { return _defaultCultureName; } + } + + /// + /// The name of the culture that is used as the default value for Thread.UICurrentCulture when CultureReplacer is used. + /// + public static string DefaultUICultureName + { + get { return _defaultUICultureName; } + } + + /// + /// The culture that is used as the default value for Thread.CurrentCulture when CultureReplacer is used. + /// + public static CultureInfo DefaultCulture + { + get { return _defaultCulture; } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (disposing) + { + Assert.True(Thread.CurrentThread.ManagedThreadId == _threadId, "The current thread is not the same as the thread invoking the constructor. This should never happen."); + Thread.CurrentThread.CurrentCulture = _originalCulture; + Thread.CurrentThread.CurrentUICulture = _originalUICulture; + } + } + } +} diff --git a/test/Microsoft.TestCommon/DataAttribute.cs b/test/Microsoft.TestCommon/DataAttribute.cs new file mode 100644 index 0000000000..87494de3df --- /dev/null +++ b/test/Microsoft.TestCommon/DataAttribute.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.TestCommon +{ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)] + public abstract class DataAttribute : Xunit.Extensions.DataAttribute + { + } +} diff --git a/test/Microsoft.TestCommon/DictionaryEqualityComparer.cs b/test/Microsoft.TestCommon/DictionaryEqualityComparer.cs new file mode 100644 index 0000000000..76b2e85747 --- /dev/null +++ b/test/Microsoft.TestCommon/DictionaryEqualityComparer.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.Collections.Generic; + +namespace Microsoft.TestCommon +{ + public class DictionaryEqualityComparer : IEqualityComparer> + { + public bool Equals(IDictionary x, IDictionary y) + { + if (x.Count != y.Count) + { + return false; + } + + foreach (string key in x.Keys) + { + object xVal = x[key]; + object yVal; + if (!y.TryGetValue(key, out yVal)) + { + return false; + } + + if (xVal == null) + { + if (yVal == null) + { + continue; + } + + return false; + } + + if (!xVal.Equals(yVal)) + { + return false; + } + } + + return true; + } + + public int GetHashCode(IDictionary obj) + { + return 1; + } + } +} diff --git a/test/Microsoft.TestCommon/EnumHelperTestBase.cs b/test/Microsoft.TestCommon/EnumHelperTestBase.cs new file mode 100644 index 0000000000..d066cf309c --- /dev/null +++ b/test/Microsoft.TestCommon/EnumHelperTestBase.cs @@ -0,0 +1,101 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.TestCommon +{ + public abstract class EnumHelperTestBase where TEnum : IComparable, IFormattable, IConvertible + { + private Func _isDefined; + private Action _validate; + private TEnum _undefined; + + /// + /// Helper to verify that we validate enums correctly when passed as arguments etc. + /// + /// A Func used to validate that a value is defined. + /// A Func used to validate that a value is definded of throw an exception. + /// An undefined value. + protected EnumHelperTestBase(Func isDefined, Action validate, TEnum undefined) + { + _isDefined = isDefined; + _validate = validate; + _undefined = undefined; + } + + [Fact] + public void IsDefined_ReturnsTrueForDefinedValues() + { + Array values = Enum.GetValues(typeof(TEnum)); + foreach (object value in values) + { + if (ValueExistsForFramework((TEnum)value)) + { + Assert.True(_isDefined((TEnum)value)); + } + else + { + Assert.False(_isDefined((TEnum)value)); + } + } + } + + [Fact] + public void IsDefined_ReturnsFalseForUndefinedValues() + { + Assert.False(_isDefined(_undefined)); + } + + [Fact] + public void Validate_DoesNotThrowForDefinedValues() + { + Array values = Enum.GetValues(typeof(TEnum)); + foreach (object value in values) + { + if (ValueExistsForFramework((TEnum)value)) + { + _validate((TEnum)value, "parameter"); + } + } + } + + [Fact] + public void Validate_ThrowsForUndefinedValues() + { + AssertForUndefinedValue( + () => _validate(_undefined, "parameter"), + "parameter", + (int)Convert.ChangeType(_undefined, typeof(int)), + typeof(TEnum), + allowDerivedExceptions: false); + } + + /// + /// Override this if InvalidEnumArgument is not supported in the targetted platform + /// + /// A delegate to the code to be tested + /// The name of the parameter that should throw the exception + /// The expected invalid value that should appear in the message + /// The type of the enumeration + /// Pass true to allow exceptions which derive from TException; pass false, otherwise + protected virtual void AssertForUndefinedValue(Action testCode, string parameterName, int invalidValue, Type enumType, bool allowDerivedExceptions = false) + { + Assert.ThrowsInvalidEnumArgument( + testCode, + parameterName, + invalidValue, + enumType, + allowDerivedExceptions); + } + + /// + /// Override this to determine if a given enum value for an enum exists in a given framework + /// + /// + /// Wheter the value exists + protected virtual bool ValueExistsForFramework(TEnum value) + { + return true; + } + } +} diff --git a/test/Microsoft.TestCommon/ExceptionAssertions.cs b/test/Microsoft.TestCommon/ExceptionAssertions.cs new file mode 100644 index 0000000000..777ff01801 --- /dev/null +++ b/test/Microsoft.TestCommon/ExceptionAssertions.cs @@ -0,0 +1,521 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.ComponentModel; +using System.Reflection; +using System.Threading.Tasks; +using System.Web; + +namespace Microsoft.TestCommon +{ + public partial class Assert + { + /// + /// Verifies that the exact exception is thrown (and not a derived exception type). + /// + /// The type of the exception expected to be thrown + /// A delegate to the code to be tested + /// The exception that was thrown, when successful + /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown + public static T Throws(Action testCode) + where T : Exception + { + return (T)Throws(typeof(T), testCode); + } + + /// + /// Verifies that the exact exception is thrown (and not a derived exception type). + /// Generally used to test property accessors. + /// + /// The type of the exception expected to be thrown + /// A delegate to the code to be tested + /// The exception that was thrown, when successful + /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown + public static T Throws(Func testCode) + where T : Exception + { + return (T)Throws(typeof(T), testCode); + } + + /// + /// Verifies that the exact exception is thrown (and not a derived exception type). + /// + /// The type of the exception expected to be thrown + /// A delegate to the code to be tested + /// The exception that was thrown, when successful + /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown + public static Exception Throws(Type exceptionType, Action testCode) + { + Exception exception = RecordException(testCode); + return VerifyException(exceptionType, exception); + } + + /// + /// Verifies that the exact exception is thrown (and not a derived exception type). + /// Generally used to test property accessors. + /// + /// The type of the exception expected to be thrown + /// A delegate to the code to be tested + /// The exception that was thrown, when successful + /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown + public static Exception Throws(Type exceptionType, Func testCode) + { + return Throws(exceptionType, () => { testCode(); }); + } + + /// + /// Verifies that an exception of the given type (or optionally a derived type) is thrown. + /// + /// The type of the exception expected to be thrown + /// A delegate to the code to be tested + /// Pass true to allow exceptions which derive from TException; pass false, otherwise + /// The exception that was thrown, when successful + /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown + public static TException Throws(Action testCode, bool allowDerivedExceptions) + where TException : Exception + { + Type exceptionType = typeof(TException); + Exception exception = RecordException(testCode); + + TargetInvocationException tie = exception as TargetInvocationException; + if (tie != null) + { + exception = tie.InnerException; + } + + if (exception == null) + { + throw new ThrowsException(exceptionType); + } + + var typedException = exception as TException; + if (typedException == null || (!allowDerivedExceptions && typedException.GetType() != typeof(TException))) + { + throw new ThrowsException(exceptionType, exception); + } + + return typedException; + } + + /// + /// Verifies that an exception of the given type (or optionally a derived type) is thrown. + /// Generally used to test property accessors. + /// + /// The type of the exception expected to be thrown + /// A delegate to the code to be tested + /// Pass true to allow exceptions which derive from TException; pass false, otherwise + /// The exception that was thrown, when successful + /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown + public static TException Throws(Func testCode, bool allowDerivedExceptions) + where TException : Exception + { + return Throws(() => { testCode(); }, allowDerivedExceptions); + } + + /// + /// Verifies that an exception of the given type (or optionally a derived type) is thrown. + /// Also verifies that the exception message matches. + /// + /// The type of the exception expected to be thrown + /// A delegate to the code to be tested + /// The exception message to verify + /// Pass true to allow exceptions which derive from TException; pass false, otherwise + /// The exception that was thrown, when successful + /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown + public static TException Throws(Action testCode, string exceptionMessage, bool allowDerivedExceptions = false) + where TException : Exception + { + var ex = Throws(testCode, allowDerivedExceptions); + VerifyExceptionMessage(ex, exceptionMessage); + return ex; + } + + /// + /// Verifies that an exception of the given type (or optionally a derived type) is thrown. + /// Also verified that the exception message matches. + /// + /// The type of the exception expected to be thrown + /// A delegate to the code to be tested + /// The exception message to verify + /// Pass true to allow exceptions which derive from TException; pass false, otherwise + /// The exception that was thrown, when successful + /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown + public static TException Throws(Func testCode, string exceptionMessage, bool allowDerivedExceptions = false) + where TException : Exception + { + return Throws(() => { testCode(); }, exceptionMessage, allowDerivedExceptions); + } + + /// + /// Verifies that the code throws an (or optionally any exception which derives from it). + /// + /// A delegate to the code to be tested + /// The name of the parameter that should throw the exception + /// Pass true to allow exceptions which derive from TException; pass false, otherwise + /// The exception that was thrown, when successful + /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown + public static ArgumentException ThrowsArgument(Action testCode, string paramName, bool allowDerivedExceptions = false) + { + var ex = Throws(testCode, allowDerivedExceptions); + + if (paramName != null) + { + Equal(paramName, ex.ParamName); + } + + return ex; + } + + /// + /// Verifies that the code throws an (or optionally any exception which derives from it). + /// + /// A delegate to the code to be tested + /// The name of the parameter that should throw the exception + /// The exception message to verify + /// Pass true to allow exceptions which derive from TException; pass false, otherwise + /// The exception that was thrown, when successful + /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown + public static ArgumentException ThrowsArgument(Action testCode, string paramName, string exceptionMessage, bool allowDerivedExceptions = false) + { + var ex = Throws(testCode, allowDerivedExceptions); + + if (paramName != null) + { + Equal(paramName, ex.ParamName); + } + + VerifyExceptionMessage(ex, exceptionMessage, partialMatch: true); + + return ex; + } + + /// + /// Verifies that the code throws an ArgumentException (or optionally any exception which derives from it). + /// + /// A delegate to the code to be tested + /// The name of the parameter that should throw the exception + /// Pass true to allow exceptions which derive from TException; pass false, otherwise + /// The exception that was thrown, when successful + /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown + public static ArgumentException ThrowsArgument(Func testCode, string paramName, bool allowDerivedExceptions = false) + { + var ex = Throws(testCode, allowDerivedExceptions); + + if (paramName != null) + { + Equal(paramName, ex.ParamName); + } + + return ex; + } + + /// + /// Verifies that the code throws an ArgumentNullException (or optionally any exception which derives from it). + /// + /// A delegate to the code to be tested + /// The name of the parameter that should throw the exception + /// The exception that was thrown, when successful + /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown + public static ArgumentNullException ThrowsArgumentNull(Action testCode, string paramName) + { + var ex = Throws(testCode, allowDerivedExceptions: false); + + if (paramName != null) + { + Equal(paramName, ex.ParamName); + } + + return ex; + } + + /// + /// Verifies that the code throws an ArgumentNullException with the expected message that indicates that the value cannot + /// be null or empty. + /// + /// A delegate to the code to be tested + /// The name of the parameter that should throw the exception + /// The exception that was thrown, when successful + /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown + public static ArgumentException ThrowsArgumentNullOrEmpty(Action testCode, string paramName) + { + return Throws(testCode, "Value cannot be null or empty.\r\nParameter name: " + paramName, allowDerivedExceptions: false); + } + + /// + /// Verifies that the code throws an ArgumentNullException with the expected message that indicates that the value cannot + /// be null or empty string. + /// + /// A delegate to the code to be tested + /// The name of the parameter that should throw the exception + /// The exception that was thrown, when successful + /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown + public static ArgumentException ThrowsArgumentNullOrEmptyString(Action testCode, string paramName) + { + return ThrowsArgument(testCode, paramName, "Value cannot be null or an empty string.", allowDerivedExceptions: true); + } + + /// + /// Verifies that the code throws an ArgumentOutOfRangeException (or optionally any exception which derives from it). + /// + /// A delegate to the code to be tested + /// The name of the parameter that should throw the exception + /// The exception message to verify + /// Pass true to allow exceptions which derive from TException; pass false, otherwise + /// The actual value provided + /// The exception that was thrown, when successful + /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown + public static ArgumentOutOfRangeException ThrowsArgumentOutOfRange(Action testCode, string paramName, string exceptionMessage, bool allowDerivedExceptions = false, object actualValue = null) + { + if (exceptionMessage != null) + { + exceptionMessage = exceptionMessage + "\r\nParameter name: " + paramName; + if (actualValue != null) + { + exceptionMessage += String.Format(CultureReplacer.DefaultCulture, "\r\nActual value was {0}.", actualValue); + } + } + + var ex = Throws(testCode, exceptionMessage, allowDerivedExceptions); + + if (paramName != null) + { + Equal(paramName, ex.ParamName); + } + + return ex; + } + + /// + /// Verifies that the code throws an with the expected message that indicates that + /// the value must be greater than the given . + /// + /// A delegate to the code to be tested + /// The name of the parameter that should throw the exception + /// The actual value provided. + /// The expected limit value. + /// The exception that was thrown, when successful + /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown + public static ArgumentOutOfRangeException ThrowsArgumentGreaterThan(Action testCode, string paramName, string value, object actualValue = null) + { + return ThrowsArgumentOutOfRange( + testCode, + paramName, + String.Format(CultureReplacer.DefaultCulture, "Value must be greater than {0}.", value), false, actualValue); + } + + /// + /// Verifies that the code throws an with the expected message that indicates that + /// the value must be greater than or equal to the given value. + /// + /// A delegate to the code to be tested + /// The name of the parameter that should throw the exception + /// The expected limit value. + /// The exception that was thrown, when successful + /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown + public static ArgumentOutOfRangeException ThrowsArgumentGreaterThanOrEqualTo(Action testCode, string paramName, string value, object actualValue = null) + { + return ThrowsArgumentOutOfRange( + testCode, + paramName, + String.Format(CultureReplacer.DefaultCulture, "Value must be greater than or equal to {0}.", value), false, actualValue); + } + + /// + /// Verifies that the code throws an with the expected message that indicates that + /// the value must be less than the given . + /// + /// A delegate to the code to be tested + /// The name of the parameter that should throw the exception + /// The actual value provided. + /// The expected limit value. + /// The exception that was thrown, when successful + /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown + public static ArgumentOutOfRangeException ThrowsArgumentLessThan(Action testCode, string paramName, string maxValue, object actualValue = null) + { + return ThrowsArgumentOutOfRange( + testCode, + paramName, + String.Format(CultureReplacer.DefaultCulture, "Value must be less than {0}.", maxValue), false, actualValue); + } + + /// + /// Verifies that the code throws an with the expected message that indicates that + /// the value must be less than or equal to the given . + /// + /// A delegate to the code to be tested + /// The name of the parameter that should throw the exception + /// The actual value provided. + /// The expected limit value. + /// The exception that was thrown, when successful + /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown + public static ArgumentOutOfRangeException ThrowsArgumentLessThanOrEqualTo(Action testCode, string paramName, string maxValue, object actualValue = null) + { + return ThrowsArgumentOutOfRange( + testCode, + paramName, + String.Format(CultureReplacer.DefaultCulture, "Value must be less than or equal to {0}.", maxValue), false, actualValue); + } + + /// + /// Verifies that the code throws an HttpException (or optionally any exception which derives from it). + /// + /// A delegate to the code to be tested + /// The exception message to verify + /// The expected HTTP status code of the exception + /// Pass true to allow exceptions which derive from TException; pass false, otherwise + /// The exception that was thrown, when successful + /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown + public static HttpException ThrowsHttpException(Action testCode, string exceptionMessage, int httpCode, bool allowDerivedExceptions = false) + { + var ex = Throws(testCode, exceptionMessage, allowDerivedExceptions); + Equal(httpCode, ex.GetHttpCode()); + return ex; + } + + /// + /// Verifies that the code throws an InvalidEnumArgumentException (or optionally any exception which derives from it). + /// + /// A delegate to the code to be tested + /// The name of the parameter that should throw the exception + /// The expected invalid value that should appear in the message + /// The type of the enumeration + /// Pass true to allow exceptions which derive from TException; pass false, otherwise + /// The exception that was thrown, when successful + /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown + public static InvalidEnumArgumentException ThrowsInvalidEnumArgument(Action testCode, string paramName, int invalidValue, Type enumType, bool allowDerivedExceptions = false) + { + string message = String.Format(CultureReplacer.DefaultCulture, + "The value of argument '{0}' ({1}) is invalid for Enum type '{2}'.{3}Parameter name: {0}", + paramName, invalidValue, enumType.Name, Environment.NewLine); + return Throws(testCode, message, allowDerivedExceptions); + } + + /// + /// Verifies that the code throws an HttpException (or optionally any exception which derives from it). + /// + /// A delegate to the code to be tested + /// The name of the object that was dispose + /// Pass true to allow exceptions which derive from TException; pass false, otherwise + /// The exception that was thrown, when successful + /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown + public static ObjectDisposedException ThrowsObjectDisposed(Action testCode, string objectName, bool allowDerivedExceptions = false) + { + var ex = Throws(testCode, allowDerivedExceptions); + + if (objectName != null) + { + Equal(objectName, ex.ObjectName); + } + + return ex; + } + + /// + /// Verifies that an exception of the given type is thrown. + /// + /// The type of the exception expected to be thrown + /// A delegate to the code to be tested + /// The exception that was thrown, when successful + /// Thrown when an exception was not thrown, or when an exception of the incorrect type is thrown + /// + /// Unlike other Throws* methods, this method does not enforce running the exception delegate with a known Thread Culture. + /// + public static async Task ThrowsAsync(Func testCode) + where TException : Exception + { + Exception exception = null; + try + { + // The 'testCode' Task might execute asynchronously in a different thread making it hard to enforce the thread culture. + // The correct way to verify exception messages in such a scenario would be to run the task synchronously inside of a + // culture enforced block. + await testCode(); + } + catch (Exception ex) + { + exception = ex; + } + VerifyException(typeof(TException), exception); + return (TException)exception; + } + + // We've re-implemented all the xUnit.net Throws code so that we can get this + // updated implementation of RecordException which silently unwraps any instances + // of AggregateException. In addition to unwrapping exceptions, this method ensures + // that tests are executed in with a known set of Culture and UICulture. This prevents + // tests from failing when executed on a non-English machine. + private static Exception RecordException(Action testCode) + { + try + { + using (new CultureReplacer()) + { + testCode(); + } + return null; + } + catch (Exception exception) + { + return UnwrapException(exception); + } + } + + private static Exception UnwrapException(Exception exception) + { + AggregateException aggEx = exception as AggregateException; + if (aggEx != null) + { + return aggEx.GetBaseException(); + } + return exception; + } + + private static Exception VerifyException(Type exceptionType, Exception exception) + { + if (exception == null) + { + throw new ThrowsException(exceptionType); + } + else if (exceptionType != exception.GetType()) + { + throw new ThrowsException(exceptionType, exception); + } + + return exception; + } + + private static void VerifyExceptionMessage(Exception exception, string expectedMessage, bool partialMatch = false) + { + if (expectedMessage != null) + { + if (!partialMatch) + { + Equal(expectedMessage, exception.Message); + } + else + { + Contains(expectedMessage, exception.Message); + } + } + } + + // Custom ThrowsException so we can filter the stack trace. + [Serializable] + private class ThrowsException : Xunit.Sdk.ThrowsException + { + public ThrowsException(Type type) : base(type) { } + + public ThrowsException(Type type, Exception ex) : base(type, ex) { } + + protected override bool ExcludeStackFrame(string stackFrame) + { + if (stackFrame.StartsWith("at Microsoft.TestCommon.Assert.", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + return base.ExcludeStackFrame(stackFrame); + } + } + } +} diff --git a/test/Microsoft.TestCommon/FactAttribute.cs b/test/Microsoft.TestCommon/FactAttribute.cs new file mode 100644 index 0000000000..449d68269c --- /dev/null +++ b/test/Microsoft.TestCommon/FactAttribute.cs @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Xunit.Sdk; + +namespace Microsoft.TestCommon +{ + /// + /// An override of that provides extended capabilities. + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + public class FactAttribute : Xunit.FactAttribute + { + public FactAttribute() + { + Timeout = Debugger.IsAttached ? Int32.MaxValue : TimeoutConstant.DefaultTimeout; + Platforms = Platform.All; + PlatformJustification = "Unsupported platform (test runs on {0}, current platform is {1})"; + } + + /// + /// Gets the platform that the unit test is currently running on. + /// + protected Platform Platform + { + get { return PlatformInfo.Platform; } + } + + /// + /// Gets or set the platforms that the unit test is compatible with. Defaults to + /// . + /// + public Platform Platforms { get; set; } + + /// + /// Gets or sets the platform skipping justification. This message can receive + /// the supported platforms as {0}, and the current platform as {1}. + /// + public string PlatformJustification { get; set; } + + /// + protected override IEnumerable EnumerateTestCommands(IMethodInfo method) + { + if ((Platforms & Platform) == 0) + { + return new[] { + new SkipCommand( + method, + DisplayName, + String.Format(PlatformJustification, Platforms.ToString().Replace(", ", " | "), Platform) + ) + }; + } + + return base.EnumerateTestCommands(method); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.TestCommon/ForceGCAttribute.cs b/test/Microsoft.TestCommon/ForceGCAttribute.cs new file mode 100644 index 0000000000..03bc46c85e --- /dev/null +++ b/test/Microsoft.TestCommon/ForceGCAttribute.cs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Reflection; + +namespace Microsoft.TestCommon +{ + public class ForceGCAttribute : Xunit.BeforeAfterTestAttribute + { + public override void After(MethodInfo methodUnderTest) + { + GC.Collect(99); + GC.Collect(99); + GC.Collect(99); + } + } +} diff --git a/test/Microsoft.TestCommon/InlineDataAttribute.cs b/test/Microsoft.TestCommon/InlineDataAttribute.cs new file mode 100644 index 0000000000..cd04439919 --- /dev/null +++ b/test/Microsoft.TestCommon/InlineDataAttribute.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.TestCommon +{ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)] + public class InlineDataAttribute : Xunit.Extensions.InlineDataAttribute + { + public InlineDataAttribute(params object[] dataValues) + : base(dataValues) + { + } + } +} diff --git a/test/Microsoft.TestCommon/MatrixTheoryDataSet.cs b/test/Microsoft.TestCommon/MatrixTheoryDataSet.cs new file mode 100644 index 0000000000..8b304eee5e --- /dev/null +++ b/test/Microsoft.TestCommon/MatrixTheoryDataSet.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; + +namespace Microsoft.TestCommon +{ + public class MatrixTheoryDataSet : TheoryDataSet + { + public MatrixTheoryDataSet(IEnumerable data1, IEnumerable data2) + { + Contract.Assert(data1 != null && data1.Any()); + Contract.Assert(data2 != null && data2.Any()); + + foreach (T1 t1 in data1) + { + foreach (T2 t2 in data2) + { + Add(t1, t2); + } + } + } + } +} diff --git a/test/Microsoft.TestCommon/MemberHelper.cs b/test/Microsoft.TestCommon/MemberHelper.cs new file mode 100644 index 0000000000..56dadb16cd --- /dev/null +++ b/test/Microsoft.TestCommon/MemberHelper.cs @@ -0,0 +1,383 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.ComponentModel; +using System.Reflection; +using Microsoft.TestCommon; + +namespace System.Web.TestUtil +{ + public static class MemberHelper + { + private static ConstructorInfo GetConstructorInfo(object instance, Type[] parameterTypes) + { + if (instance == null) + { + throw new ArgumentNullException("instance"); + } + ConstructorInfo constructorInfo = instance.GetType().GetConstructor(parameterTypes); + if (constructorInfo == null) + { + throw new ArgumentException(String.Format( + "A matching constructor on type '{0}' could not be found.", + instance.GetType().FullName)); + } + return constructorInfo; + } + + private static EventInfo GetEventInfo(object instance, string eventName) + { + if (instance == null) + { + throw new ArgumentNullException("instance"); + } + if (String.IsNullOrEmpty(eventName)) + { + throw new ArgumentException("An event must be specified.", "eventName"); + } + EventInfo eventInfo = instance.GetType().GetEvent(eventName); + if (eventInfo == null) + { + throw new ArgumentException(String.Format( + "An event named '{0}' on type '{1}' could not be found.", + eventName, instance.GetType().FullName)); + } + return eventInfo; + } + + private static MethodInfo GetMethodInfo(object instance, string methodName, Type[] types = null, MethodAttributes attrs = MethodAttributes.Public) + { + if (instance == null) + { + throw new ArgumentNullException("instance"); + } + if (String.IsNullOrEmpty(methodName)) + { + throw new ArgumentException("A method must be specified.", "methodName"); + } + + MethodInfo methodInfo; + if (types != null) + { + methodInfo = instance.GetType().GetMethod(methodName, + BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, + null, + types, + null); + } + else + { + methodInfo = instance.GetType().GetMethod(methodName, + BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + } + + if (methodInfo == null) + { + throw new ArgumentException(String.Format( + "A method named '{0}' on type '{1}' could not be found.", + methodName, instance.GetType().FullName)); + } + + if ((methodInfo.Attributes & attrs) != attrs) + { + throw new ArgumentException(String.Format( + "Method '{0}' on type '{1}' with attributes '{2}' does not match the attributes '{3}'.", + methodName, instance.GetType().FullName, methodInfo.Attributes, attrs)); + } + + return methodInfo; + } + + private static PropertyInfo GetPropertyInfo(object instance, string propertyName) + { + if (instance == null) + { + throw new ArgumentNullException("instance"); + } + if (String.IsNullOrEmpty(propertyName)) + { + throw new ArgumentException("A property must be specified.", "propertyName"); + } + PropertyInfo propInfo = instance.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + if (propInfo == null) + { + throw new ArgumentException(String.Format( + "A property named '{0}' on type '{1}' could not be found.", + propertyName, instance.GetType().FullName)); + } + return propInfo; + } + + private static void TestAttribute(MemberInfo memberInfo, TAttribute attributeValue) + where TAttribute : Attribute + { + object[] attrs = memberInfo.GetCustomAttributes(typeof(TAttribute), true); + + if (attributeValue == null) + { + Assert.True(attrs.Length == 0, "Member should not have an attribute of type " + typeof(TAttribute)); + } + else + { + Assert.True(attrs != null && attrs.Length > 0, + "Member does not have an attribute of type " + typeof(TAttribute)); + Assert.Equal(attributeValue, attrs[0]); + } + } + + public static void TestBooleanProperty(object instance, string propertyName, bool initialValue, bool testDefaultValue) + { + // Assert initial value + TestGetPropertyValue(instance, propertyName, initialValue); + + if (testDefaultValue) + { + // Assert DefaultValue attribute matches inital value + TestDefaultValue(instance, propertyName); + } + + TestPropertyValue(instance, propertyName, true); + TestPropertyValue(instance, propertyName, false); + } + + public static void TestDefaultValue(object instance, string propertyName) + { + PropertyInfo propInfo = GetPropertyInfo(instance, propertyName); + + object initialValue = propInfo.GetValue(instance, null); + TestAttribute(propInfo, new DefaultValueAttribute(initialValue)); + } + + public static void TestEvent(object instance, string eventName, TEventArgs eventArgs) where TEventArgs : EventArgs + { + EventInfo eventInfo = GetEventInfo(instance, eventName); + + // Assert category "Action" + TestAttribute(eventInfo, new CategoryAttribute("Action")); + + // Call protected method with no event handlers, assert no error + MethodInfo methodInfo = GetMethodInfo(instance, "On" + eventName, attrs: MethodAttributes.Family | MethodAttributes.Virtual); + methodInfo.Invoke(instance, new object[] { eventArgs }); + + // Attach handler, call method, assert fires once + List eventHandlerArgs = new List(); + EventHandler handler = new EventHandler(delegate(object sender, TEventArgs t) + { + eventHandlerArgs.Add(sender); + eventHandlerArgs.Add(t); + }); + eventInfo.AddEventHandler(instance, handler); + methodInfo.Invoke(instance, new object[] { eventArgs }); + Assert.Equal(new[] { instance, eventArgs }, eventHandlerArgs.ToArray()); + + // Detach handler, call method, assert not fired + eventHandlerArgs = new List(); + eventInfo.RemoveEventHandler(instance, handler); + methodInfo.Invoke(instance, new object[] { eventArgs }); + Assert.Empty(eventHandlerArgs); + } + + public static void TestGetPropertyValue(object instance, string propertyName, object valueToCheck) + { + PropertyInfo propInfo = GetPropertyInfo(instance, propertyName); + object value = propInfo.GetValue(instance, null); + Assert.Equal(valueToCheck, value); + } + + public static void TestEnumProperty(object instance, string propertyName, TEnumValue initialValue, bool testDefaultValue) + { + // Assert initial value + TestGetPropertyValue(instance, propertyName, initialValue); + + if (testDefaultValue) + { + // Assert DefaultValue attribute matches inital value + TestDefaultValue(instance, propertyName); + } + + PropertyInfo propInfo = GetPropertyInfo(instance, propertyName); + + // Values are sorted numerically + TEnumValue[] values = (TEnumValue[])Enum.GetValues(propInfo.PropertyType); + + // Assert get/set works for all valid enum values + foreach (TEnumValue value in values) + { + TestPropertyValue(instance, propertyName, value); + } + + // Assert ArgumentOutOfRangeException is thrown for value one less than smallest + // enum value, and one more than largest enum value + var targetException = Assert.Throws(() => propInfo.SetValue(instance, Convert.ToInt32(values[0]) - 1, null)); + Assert.IsType(targetException.InnerException); + + targetException = Assert.Throws(() => propInfo.SetValue(instance, Convert.ToInt32(values[values.Length - 1]) + 1, null)); + Assert.IsType(targetException.InnerException); + } + + public static void TestInt32Property(object instance, string propertyName, int value1, int value2) + { + TestPropertyValue(instance, propertyName, value1); + TestPropertyValue(instance, propertyName, value2); + } + + public static void TestPropertyWithDefaultInstance(object instance, string propertyName, object valueToSet) + { + PropertyInfo propInfo = GetPropertyInfo(instance, propertyName); + + // Set to explicit property + propInfo.SetValue(instance, valueToSet, null); + object value = propInfo.GetValue(instance, null); + Assert.Equal(valueToSet, value); + + // Set to null + propInfo.SetValue(instance, null, null); + value = propInfo.GetValue(instance, null); + Assert.IsAssignableFrom(propInfo.PropertyType, value); + } + + public static void TestPropertyWithDefaultInstance(object instance, string propertyName, object valueToSet, object defaultValue) + { + PropertyInfo propInfo = GetPropertyInfo(instance, propertyName); + + // Set to explicit property + propInfo.SetValue(instance, valueToSet, null); + object value = propInfo.GetValue(instance, null); + Assert.Same(valueToSet, value); + + // Set to null + propInfo.SetValue(instance, null, null); + value = propInfo.GetValue(instance, null); + Assert.Equal(defaultValue, value); + } + + public static void TestPropertyValue(object instance, string propertyName, object value) + { + TestPropertyValue(instance, propertyName, value, value); + } + + public static void TestPropertyValue(object instance, string propertyName, object valueToSet, object valueToCheck) + { + PropertyInfo propInfo = GetPropertyInfo(instance, propertyName); + propInfo.SetValue(instance, valueToSet, null); + object value = propInfo.GetValue(instance, null); + Assert.Equal(valueToCheck, value); + } + + public static void TestStringParams(object instance, Type[] constructorParameterTypes, object[] parameters) + { + ConstructorInfo ctor = GetConstructorInfo(instance, constructorParameterTypes); + TestStringParams(ctor, instance, parameters); + } + + public static void TestStringParams(object instance, string methodName, object[] parameters) + { + TestStringParams(instance, methodName, null, parameters); + } + + public static void TestStringParams(object instance, string methodName, Type[] types, object[] parameters) + { + MethodInfo method = GetMethodInfo(instance, methodName, types); + TestStringParams(method, instance, parameters); + } + + private static void TestStringParams(MethodBase method, object instance, object[] parameters) + { + ParameterInfo[] parameterInfos = method.GetParameters(); + foreach (ParameterInfo parameterInfo in parameterInfos) + { + if (parameterInfo.ParameterType == typeof(String)) + { + object originalParameter = parameters[parameterInfo.Position]; + + parameters[parameterInfo.Position] = null; + Assert.ThrowsArgumentNullOrEmpty( + delegate() + { + try + { + method.Invoke(instance, parameters); + } + catch (TargetInvocationException e) + { + throw e.InnerException; + } + }, + parameterInfo.Name); + + parameters[parameterInfo.Position] = String.Empty; + Assert.ThrowsArgumentNullOrEmpty( + delegate() + { + try + { + method.Invoke(instance, parameters); + } + catch (TargetInvocationException e) + { + throw e.InnerException; + } + }, + parameterInfo.Name); + + parameters[parameterInfo.Position] = originalParameter; + } + } + } + + public static void TestStringProperty(object instance, string propertyName, string initialValue, + bool testDefaultValueAttribute = false, bool allowNullAndEmpty = true, + string nullAndEmptyReturnValue = "") + { + // Assert initial value + TestGetPropertyValue(instance, propertyName, initialValue); + + if (testDefaultValueAttribute) + { + // Assert DefaultValue attribute matches inital value + TestDefaultValue(instance, propertyName); + } + + if (allowNullAndEmpty) + { + // Assert get/set works for null + TestPropertyValue(instance, propertyName, null, nullAndEmptyReturnValue); + + // Assert get/set works for String.Empty + TestPropertyValue(instance, propertyName, String.Empty, nullAndEmptyReturnValue); + } + else + { + Assert.ThrowsArgumentNullOrEmpty( + delegate() + { + try + { + TestPropertyValue(instance, propertyName, null); + } + catch (TargetInvocationException e) + { + throw e.InnerException; + } + }, + "value"); + Assert.ThrowsArgumentNullOrEmpty( + delegate() + { + try + { + TestPropertyValue(instance, propertyName, String.Empty); + } + catch (TargetInvocationException e) + { + throw e.InnerException; + } + }, + "value"); + } + + // Assert get/set works for arbitrary value + TestPropertyValue(instance, propertyName, "TestValue"); + } + } +} diff --git a/test/Microsoft.TestCommon/Microsoft.TestCommon.csproj b/test/Microsoft.TestCommon/Microsoft.TestCommon.csproj new file mode 100644 index 0000000000..e484abe659 --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft.TestCommon.csproj @@ -0,0 +1,104 @@ + + + + {FCCC4CB7-BAF7-4A57-9F89-E5766FE536C0} + Library + Properties + Microsoft.TestCommon + Microsoft.TestCommon + ..\..\bin\$(Configuration)\Test\ + v4.5.1 + + + + false + + + + + + + + + + + + + False + ..\..\packages\xunit.1.9.1\lib\net20\xunit.dll + + + False + ..\..\packages\xunit.extensions.1.9.1\lib\net20\xunit.extensions.dll + + + + + + + + + + + + + + + + + + + + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/CommonUnitTestDataSets.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/CommonUnitTestDataSets.cs new file mode 100644 index 0000000000..d56a619620 --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/CommonUnitTestDataSets.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Collections.ObjectModel; +using Microsoft.TestCommon.Types; + +namespace Microsoft.TestCommon +{ + public class CommonUnitTestDataSets + { + public static ValueTypeTestData Chars { get { return TestData.CharTestData; } } + public static ValueTypeTestData Ints { get { return TestData.IntTestData; } } + public static ValueTypeTestData Uints { get { return TestData.UintTestData; } } + public static ValueTypeTestData Shorts { get { return TestData.ShortTestData; } } + public static ValueTypeTestData Ushorts { get { return TestData.UshortTestData; } } + public static ValueTypeTestData Longs { get { return TestData.LongTestData; } } + public static ValueTypeTestData Ulongs { get { return TestData.UlongTestData; } } + public static ValueTypeTestData Bytes { get { return TestData.ByteTestData; } } + public static ValueTypeTestData SBytes { get { return TestData.SByteTestData; } } + public static ValueTypeTestData Bools { get { return TestData.BoolTestData; } } + public static ValueTypeTestData Doubles { get { return TestData.DoubleTestData; } } + public static ValueTypeTestData Floats { get { return TestData.FloatTestData; } } + public static ValueTypeTestData DateTimes { get { return TestData.DateTimeTestData; } } + public static ValueTypeTestData Decimals { get { return TestData.DecimalTestData; } } + public static ValueTypeTestData TimeSpans { get { return TestData.TimeSpanTestData; } } + public static ValueTypeTestData Guids { get { return TestData.GuidTestData; } } + public static ValueTypeTestData DateTimeOffsets { get { return TestData.DateTimeOffsetTestData; } } + public static ValueTypeTestData SimpleEnums { get { return TestData.SimpleEnumTestData; } } + public static ValueTypeTestData LongEnums { get { return TestData.LongEnumTestData; } } + public static ValueTypeTestData FlagsEnums { get { return TestData.FlagsEnumTestData; } } + public static TestData EmptyStrings { get { return TestData.EmptyStrings; } } + public static RefTypeTestData Strings { get { return TestData.StringTestData; } } + public static TestData NonNullEmptyStrings { get { return TestData.NonNullEmptyStrings; } } + public static RefTypeTestData ISerializableTypes { get { return TestData.ISerializableTypeTestData; } } + public static ReadOnlyCollection ValueTypeTestDataCollection { get { return TestData.ValueTypeTestDataCollection; } } + public static ReadOnlyCollection RefTypeTestDataCollection { get { return TestData.RefTypeTestDataCollection; } } + public static ReadOnlyCollection ValueAndRefTypeTestDataCollection { get { return TestData.ValueAndRefTypeTestDataCollection; } } + public static ReadOnlyCollection RepresentativeValueAndRefTypeTestDataCollection { get { return TestData.RepresentativeValueAndRefTypeTestDataCollection; } } + } +} \ No newline at end of file diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/RefTypeTestData.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/RefTypeTestData.cs new file mode 100644 index 0000000000..49fdd7a269 --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/RefTypeTestData.cs @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.TestCommon +{ + public class RefTypeTestData : TestData where T : class + { + private Func> testDataProvider; + private Func> derivedTypeTestDataProvider; + private Func> knownTypeTestDataProvider; + + public RefTypeTestData(Func> testDataProvider) + { + if (testDataProvider == null) + { + throw new ArgumentNullException("testDataProvider"); + } + + this.testDataProvider = testDataProvider; + this.RegisterTestDataVariation(TestDataVariations.WithNull, this.Type, GetNullTestData); + } + + public RefTypeTestData( + Func> testDataProvider, + Func> derivedTypeTestDataProvider, + Func> knownTypeTestDataProvider) + : this(testDataProvider) + { + this.derivedTypeTestDataProvider = derivedTypeTestDataProvider; + if (this.derivedTypeTestDataProvider != null) + { + this.RegisterTestDataVariation(TestDataVariations.AsDerivedType, this.Type, this.GetTestDataAsDerivedType); + } + + this.knownTypeTestDataProvider = knownTypeTestDataProvider; + if (this.knownTypeTestDataProvider != null) + { + this.RegisterTestDataVariation(TestDataVariations.AsKnownType, this.Type, this.GetTestDataAsDerivedKnownType); + } + } + + public T GetNullTestData() + { + return null; + } + + public IEnumerable GetTestDataAsDerivedType() + { + if (this.derivedTypeTestDataProvider != null) + { + return this.derivedTypeTestDataProvider(); + } + + return Enumerable.Empty(); + } + + public IEnumerable GetTestDataAsDerivedKnownType() + { + if (this.knownTypeTestDataProvider != null) + { + return this.knownTypeTestDataProvider(); + } + + return Enumerable.Empty(); + } + + protected override IEnumerable GetTypedTestData() + { + return this.testDataProvider(); + } + } +} diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/TestData.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/TestData.cs new file mode 100644 index 0000000000..141a755080 --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/TestData.cs @@ -0,0 +1,463 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using Microsoft.TestCommon.Types; + +namespace Microsoft.TestCommon +{ + /// + /// A base class for test data. A instance is associated with a given type, and the instance can + /// provide instances of the given type to use as data in tests. The same instance can also provide instances + /// of types related to the given type, such as a of the type. See the enum for all the + /// variations of test data that a instance can provide. + /// + public abstract class TestData + { + /// + /// Common for a . + /// + public static readonly ValueTypeTestData CharTestData = new ValueTypeTestData('a', Char.MinValue, Char.MaxValue); + + /// + /// Common for a . + /// + public static readonly ValueTypeTestData IntTestData = new ValueTypeTestData(-1, 0, 1, Int32.MinValue, Int32.MaxValue); + + /// + /// Common for a . + /// + public static readonly ValueTypeTestData UintTestData = new ValueTypeTestData(0, 1, UInt32.MinValue, UInt32.MaxValue); + + /// + /// Common for a . + /// + public static readonly ValueTypeTestData ShortTestData = new ValueTypeTestData(-1, 0, 1, Int16.MinValue, Int16.MaxValue); + + /// + /// Common for a . + /// + public static readonly ValueTypeTestData UshortTestData = new ValueTypeTestData(0, 1, UInt16.MinValue, UInt16.MaxValue); + + /// + /// Common for a . + /// + public static readonly ValueTypeTestData LongTestData = new ValueTypeTestData(-1, 0, 1, Int64.MinValue, Int64.MaxValue); + + /// + /// Common for a . + /// + public static readonly ValueTypeTestData UlongTestData = new ValueTypeTestData(0, 1, UInt64.MinValue, UInt64.MaxValue); + + /// + /// Common for a . + /// + public static readonly ValueTypeTestData ByteTestData = new ValueTypeTestData(0, 1, Byte.MinValue, Byte.MaxValue); + + /// + /// Common for a . + /// + public static readonly ValueTypeTestData SByteTestData = new ValueTypeTestData(-1, 0, 1, SByte.MinValue, SByte.MaxValue); + + /// + /// Common for a . + /// + public static readonly ValueTypeTestData BoolTestData = new ValueTypeTestData(true, false); + + /// + /// Common for a . + /// + public static readonly ValueTypeTestData DoubleTestData = new ValueTypeTestData( + -1.0, + 0.0, + 1.0, + double.MinValue, + double.MaxValue, + double.PositiveInfinity, + double.NegativeInfinity); + + /// + /// Common for a . + /// + public static readonly ValueTypeTestData FloatTestData = new ValueTypeTestData( + -1.0f, + 0.0f, + 1.0f, + float.MinValue, + float.MaxValue, + float.PositiveInfinity, + float.NegativeInfinity); + + /// + /// Common for a . + /// + public static readonly ValueTypeTestData DecimalTestData = new ValueTypeTestData( + -1M, + 0M, + 1M, + decimal.MinValue, + decimal.MaxValue); + + /// + /// Common for a . + /// + public static readonly ValueTypeTestData DateTimeTestData = new ValueTypeTestData( + DateTime.Now, + DateTime.UtcNow, + DateTime.MaxValue, + DateTime.MinValue); + + /// + /// Common for a . + /// + public static readonly ValueTypeTestData TimeSpanTestData = new ValueTypeTestData( + TimeSpan.MinValue, + TimeSpan.MaxValue); + + /// + /// Common for a . + /// + public static readonly ValueTypeTestData GuidTestData = new ValueTypeTestData( + Guid.NewGuid(), + Guid.Empty); + + /// + /// Common for a . + /// + public static readonly ValueTypeTestData DateTimeOffsetTestData = new ValueTypeTestData( + DateTimeOffset.MaxValue, + DateTimeOffset.MinValue, + new DateTimeOffset(DateTime.Now)); + + /// + /// Common for an enum. + /// + public static readonly ValueTypeTestData SimpleEnumTestData = new ValueTypeTestData( + SimpleEnum.First, + SimpleEnum.Second, + SimpleEnum.Third); + + /// + /// Common for an enum implemented with a . + /// + public static readonly ValueTypeTestData LongEnumTestData = new ValueTypeTestData( + LongEnum.FirstLong, + LongEnum.SecondLong, + LongEnum.ThirdLong); + + /// + /// Common for an enum decorated with a . + /// + public static readonly ValueTypeTestData FlagsEnumTestData = new ValueTypeTestData( + FlagsEnum.One, + FlagsEnum.Two, + FlagsEnum.Four); + + /// + /// Expected permutations of non supported file paths. + /// + public static readonly TestData NotSupportedFilePaths = new RefTypeTestData(() => new List() { + "cc:\\a\\b", + }); + + /// + /// Expected permutations of invalid file paths. + /// + public static readonly TestData InvalidNonNullFilePaths = new RefTypeTestData(() => new List() { + String.Empty, + "", + " ", + " ", + "\t\t \n ", + "c:\\ab", + "c:\\a\"b", + "c:\\a\tb", + "c:\\a|b", + "c:\\a\bb", + "c:\\a\0b", + }); + + /// + /// All expected permutations of an empty string. + /// + public static readonly TestData NonNullEmptyStrings = new RefTypeTestData(() => new List() { String.Empty, "", " ", "\t\r\n" }); + + /// + /// All expected permutations of an empty string. + /// + public static readonly TestData EmptyStrings = new RefTypeTestData(() => new List() { null, String.Empty, "", " ", "\t\r\n" }); + + /// + /// Common for a . + /// + public static readonly RefTypeTestData StringTestData = new RefTypeTestData(() => new List() { + "", + " ", // one space + " ", // multiple spaces + " data ", // leading and trailing whitespace + "\t\t \n ", + "Some String!"}); + + /// + /// Common for a class that implements . + /// + public static readonly RefTypeTestData ISerializableTypeTestData = new RefTypeTestData( + ISerializableType.GetTestData); + + /// + /// A read-only collection of value type test data. + /// + public static readonly ReadOnlyCollection ValueTypeTestDataCollection = new ReadOnlyCollection(new TestData[] { + CharTestData, + IntTestData, + UintTestData, + ShortTestData, + UshortTestData, + LongTestData, + UlongTestData, + ByteTestData, + SByteTestData, + BoolTestData, + DoubleTestData, + FloatTestData, + DecimalTestData, + TimeSpanTestData, + GuidTestData, + DateTimeOffsetTestData, + SimpleEnumTestData, + LongEnumTestData, + FlagsEnumTestData}); + + /// + /// A read-only collection of reference type test data. + /// + public static readonly ReadOnlyCollection RefTypeTestDataCollection = new ReadOnlyCollection(new TestData[] { + StringTestData, + ISerializableTypeTestData}); + + /// + /// A read-only collection of value and reference type test data. + /// + public static readonly ReadOnlyCollection ValueAndRefTypeTestDataCollection = new ReadOnlyCollection( + ValueTypeTestDataCollection.Concat(RefTypeTestDataCollection).ToList()); + + /// + /// A read-only collection of representative values and reference type test data. + /// Uses where exhaustive coverage is not required. + /// + public static readonly ReadOnlyCollection RepresentativeValueAndRefTypeTestDataCollection = new ReadOnlyCollection(new TestData[] { + IntTestData, + BoolTestData, + SimpleEnumTestData, + StringTestData, + }); + + private Dictionary registeredTestDataVariations; + + + /// + /// Initializes a new instance of the class. + /// + /// The type associated with the instance. + protected TestData(Type type) + { + if (type.ContainsGenericParameters) + { + throw new InvalidOperationException("Only closed generic types are supported."); + } + + this.Type = type; + this.registeredTestDataVariations = new Dictionary(); + } + + /// + /// Gets the type associated with the instance. + /// + public Type Type { get; private set; } + + + /// + /// Gets the supported test data variations. + /// + /// + public IEnumerable GetSupportedTestDataVariations() + { + return this.registeredTestDataVariations.Keys; + } + + /// + /// Gets the related type for the given test data variation or returns null if the instance + /// doesn't support the given variation. + /// + /// The test data variation with which to create the related . + /// The related for the as given by the test data variation. + /// + /// For example, if the given was created for test data and the varation parameter + /// was then the returned type would be . + /// + public Type GetAsTypeOrNull(TestDataVariations variation) + { + TestDataVariationProvider testDataVariation = null; + if (this.registeredTestDataVariations.TryGetValue(variation, out testDataVariation)) + { + return testDataVariation.Type; + } + + return null; + } + + /// + /// Gets test data for the given test data variation or returns null if the instance + /// doesn't support the given variation. + /// + /// The test data variation with which to create the related test data. + /// Test data of the type specified by the method. + public object GetAsTestDataOrNull(TestDataVariations variation) + { + TestDataVariationProvider testDataVariation = null; + if (this.registeredTestDataVariations.TryGetValue(variation, out testDataVariation)) + { + return testDataVariation.TestDataProvider(); + } + + return null; + } + + + /// + /// Allows derived classes to register a that will + /// provide test data for a given variation. + /// + /// The variation with which to register the r. + /// The type of the test data created by the + /// A that will provide test data. + protected void RegisterTestDataVariation(TestDataVariations variation, Type type, Func testDataProvider) + { + this.registeredTestDataVariations.Add(variation, new TestDataVariationProvider(type, testDataProvider)); + } + + private class TestDataVariationProvider + { + public TestDataVariationProvider(Type type, Func testDataProvider) + { + this.Type = type; + this.TestDataProvider = testDataProvider; + } + + + public Func TestDataProvider { get; private set; } + + public Type Type { get; private set; } + } + } + + + /// + /// A generic base class for test data. + /// + /// The type associated with the test data. + public abstract class TestData : TestData, IEnumerable + { + private static readonly Type OpenIEnumerableType = typeof(IEnumerable<>); + private static readonly Type OpenListType = typeof(List<>); + private static readonly Type OpenIQueryableType = typeof(IQueryable<>); + private static readonly Type OpenDictionaryType = typeof(Dictionary<,>); + private static readonly Type OpenTestDataHolderType = typeof(TestDataHolder<>); + private int dictionaryKey; + + /// + /// Initializes a new instance of the class. + /// + protected TestData() + : base(typeof(T)) + { + Type[] typeParams = new Type[] { this.Type }; + Type[] dictionaryTypeParams = new Type[] { typeof(string), this.Type }; + + Type arrayType = this.Type.MakeArrayType(); + Type listType = OpenListType.MakeGenericType(typeParams); + Type iEnumerableType = OpenIEnumerableType.MakeGenericType(typeParams); + Type iQueryableType = OpenIQueryableType.MakeGenericType(typeParams); + Type dictionaryType = OpenDictionaryType.MakeGenericType(dictionaryTypeParams); + Type testDataHolderType = OpenTestDataHolderType.MakeGenericType(typeParams); + + this.RegisterTestDataVariation(TestDataVariations.AsInstance, this.Type, () => GetTypedTestData()); + this.RegisterTestDataVariation(TestDataVariations.AsArray, arrayType, GetTestDataAsArray); + this.RegisterTestDataVariation(TestDataVariations.AsIEnumerable, iEnumerableType, GetTestDataAsIEnumerable); + this.RegisterTestDataVariation(TestDataVariations.AsIQueryable, iQueryableType, GetTestDataAsIQueryable); + this.RegisterTestDataVariation(TestDataVariations.AsList, listType, GetTestDataAsList); + this.RegisterTestDataVariation(TestDataVariations.AsDictionary, dictionaryType, GetTestDataAsDictionary); + this.RegisterTestDataVariation(TestDataVariations.AsClassMember, testDataHolderType, GetTestDataInHolder); + } + + public IEnumerator GetEnumerator() + { + return (IEnumerator)this.GetTypedTestData().ToList().GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return (IEnumerator)this.GetTypedTestData().ToList().GetEnumerator(); + } + + /// + /// Gets the test data as an array. + /// + /// An array of test data of the given type. + public T[] GetTestDataAsArray() + { + return this.GetTypedTestData().ToArray(); + } + + /// + /// Gets the test data as a . + /// + /// A of test data of the given type. + public List GetTestDataAsList() + { + return this.GetTypedTestData().ToList(); + } + + /// + /// Gets the test data as an . + /// + /// An of test data of the given type. + public IEnumerable GetTestDataAsIEnumerable() + { + return this.GetTypedTestData().AsEnumerable(); + } + + /// + /// Gets the test data as an . + /// + /// An of test data of the given type. + public IQueryable GetTestDataAsIQueryable() + { + return this.GetTypedTestData().AsQueryable(); + } + + public Dictionary GetTestDataAsDictionary() + { + // Some TestData collections contain duplicates e.g. UintTestData contains both 0 and UInt32.MinValue. + // Therefore use dictionaryKey, not _unused.ToString(). Reset key to keep dictionaries consistent if used + // multiple times. + dictionaryKey = 0; + return this.GetTypedTestData().ToDictionary(_unused => (dictionaryKey++).ToString()); + } + + public IEnumerable> GetTestDataInHolder() + { + return this.GetTypedTestData().Select(value => new TestDataHolder { V1 = value, }); + } + + /// + /// Must be implemented by derived types to return test data of the given type. + /// + /// Test data of the given type. + protected abstract IEnumerable GetTypedTestData(); + } +} diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/TestDataHolder.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/TestDataHolder.cs new file mode 100644 index 0000000000..90e98f60ed --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/TestDataHolder.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Globalization; + +namespace Microsoft.TestCommon +{ + /// + /// Equatable class wrapping a single instance of type . Equatable to ease test assertions. + /// + /// The to wrap. + public class TestDataHolder : IEquatable> + { + public T V1 { get; set; } + + bool IEquatable>.Equals(TestDataHolder other) + { + if (other == null) + { + return false; + } + + return Object.Equals(V1, other.V1); + } + + public override bool Equals(object obj) + { + TestDataHolder that = obj as TestDataHolder; + return ((IEquatable>)this).Equals(that); + } + + public override int GetHashCode() + { + if (typeof(ValueType).IsAssignableFrom(typeof(T)) || V1 != null) + { + return V1.GetHashCode(); + } + else + { + return 0; + } + } + + public override string ToString() + { + return String.Format(CultureInfo.InvariantCulture, "{{ V1: '{0}' }}", V1); + } + } +} diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/TestDataVariations.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/TestDataVariations.cs new file mode 100644 index 0000000000..40a538a1a5 --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/TestDataVariations.cs @@ -0,0 +1,115 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.TestCommon +{ + /// + /// An flags enum that can be used to indicate different variations of a given + /// instance. + /// + [Flags] + public enum TestDataVariations + { + /// + /// An individual instance of a given type. + /// + AsInstance = 0x1, + + /// + /// An individual instance of a type that derives from a given type. + /// + AsDerivedType = 0x2, + + /// + /// An individual instance of a given type that has a property value + /// that is a known type of the declared property type. + /// + AsKnownType = 0x4, + + /// + /// A instance of a given type. Only applies to + /// instances of . + /// + AsNullable = 0x8, + + /// + /// An instance of a of a given type. + /// + AsList = 0x10, + + /// + /// An instance of a array of the type. + /// + AsArray = 0x20, + + /// + /// An instance of an of a given type. + /// + AsIEnumerable = 0x40, + + /// + /// An instance of an of a given type. + /// + AsIQueryable = 0x80, + + /// + /// An instance of a DataContract type in which a given type is a member. + /// + AsDataMember = 0x100, + + /// + /// An instance of a type in which a given type is decorated with a + /// . + /// + AsXmlElementProperty = 0x200, + + /// + /// An instance of a of a given + /// type. + /// + AsDictionary = 0x400, + + /// + /// Add a null instance of the given type to the data set. This variation is + /// not included in or other variation masks. + /// + WithNull = 0x800, + + /// + /// Individual instances of containing the given . This + /// variation is not included in or other variation masks. + /// + AsClassMember = 0x1000, + + /// + /// All of the flags for single instance variations of a given type. + /// + AllSingleInstances = AsInstance | AsDerivedType | AsKnownType | AsNullable, + + /// + /// All of the flags for collection variations of a given type. + /// + AllCollections = AsList | AsArray | AsIEnumerable | AsIQueryable | AsDictionary, + + /// + /// All of the flags for variations in which a given type is a property on another type. + /// + AllProperties = AsDataMember | AsXmlElementProperty, + + /// + /// All of the flags for interface collection variations of a given type. + /// + AllInterfaces = AsIEnumerable | AsIQueryable, + + /// + /// All of the flags except for the interface collection variations of a given type. + /// + AllNonInterfaces = All & ~AllInterfaces, + + /// + /// All of the flags for all of the variations of a given type. + /// + All = AllSingleInstances | AllCollections | AllProperties + } +} diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/ValueTypeTestData.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/ValueTypeTestData.cs new file mode 100644 index 0000000000..f4cd3e5ad9 --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/DataSets/ValueTypeTestData.cs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.TestCommon +{ + public class ValueTypeTestData : TestData where T : struct + { + private static readonly Type OpenNullableType = typeof(Nullable<>); + private T[] testData; + + public ValueTypeTestData(params T[] testData) + : base() + { + this.testData = testData; + + Type[] typeParams = new Type[] { this.Type }; + this.RegisterTestDataVariation(TestDataVariations.WithNull, OpenNullableType.MakeGenericType(typeParams), GetNullTestData); + this.RegisterTestDataVariation(TestDataVariations.AsNullable, OpenNullableType.MakeGenericType(typeParams), GetTestDataAsNullable); + } + + public object GetNullTestData() + { + return null; + } + + public IEnumerable> GetTestDataAsNullable() + { + return this.GetTypedTestData().Select(d => new Nullable(d)); + } + + protected override IEnumerable GetTypedTestData() + { + return this.testData; + } + } +} diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/GenericTypeAssert.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/GenericTypeAssert.cs new file mode 100644 index 0000000000..10c7232787 --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/GenericTypeAssert.cs @@ -0,0 +1,490 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Linq; +using System.Reflection; + +namespace Microsoft.TestCommon +{ + /// + /// MSTest assertion class to provide convenience and assert methods for generic types + /// whose type parameters are not known at compile time. + /// + public class GenericTypeAssert + { + private static readonly GenericTypeAssert singleton = new GenericTypeAssert(); + + public static GenericTypeAssert Singleton { get { return singleton; } } + + /// + /// Asserts the given is a generic type and creates a new + /// bound generic type using . It then asserts there + /// is a constructor that will accept and returns it. + /// + /// The unbound generic base type. + /// The type of the single generic parameter to apply to create a bound generic type. + /// The list of parameter types for a constructor that must exist. + /// The of that constructor which may be invoked to create that new generic type. + public ConstructorInfo GetConstructor(Type genericBaseType, Type genericParameterType, params Type[] parameterTypes) + { + Assert.NotNull(genericBaseType); + Assert.True(genericBaseType.IsGenericTypeDefinition); + Assert.NotNull(genericParameterType); + Assert.NotNull(parameterTypes); + + Type genericType = genericBaseType.MakeGenericType(new Type[] { genericParameterType }); + ConstructorInfo ctor = genericType.GetConstructor(parameterTypes); + Assert.True(ctor != null, String.Format("Test error: failed to locate generic ctor for type '{0}<{1}>',", genericBaseType.Name, genericParameterType.Name)); + return ctor; + } + + /// + /// Asserts the given is a generic type and creates a new + /// bound generic type using . It then asserts there + /// is a constructor that will accept and returns it. + /// + /// The unbound generic base type. + /// The types of the generic parameters to apply to create a bound generic type. + /// The list of parameter types for a constructor that must exist. + /// The of that constructor which may be invoked to create that new generic type. + public ConstructorInfo GetConstructor(Type genericBaseType, Type[] genericParameterTypes, params Type[] parameterTypes) + { + Assert.NotNull(genericBaseType); + Assert.True(genericBaseType.IsGenericTypeDefinition); + Assert.NotNull(genericParameterTypes); + Assert.NotNull(parameterTypes); + + Type genericType = genericBaseType.MakeGenericType(genericParameterTypes); + ConstructorInfo ctor = genericType.GetConstructor(parameterTypes); + Assert.True(ctor != null, String.Format("Test error: failed to locate generic ctor for type '{0}<>',", genericBaseType.Name)); + return ctor; + } + + /// + /// Creates a new bound generic type and invokes the constructor matched from . + /// + /// The unbound generic base type. + /// The type of the single generic parameter to apply to create a bound generic type. + /// The list of parameter types for a constructor that must exist. + /// The list of values to supply to the constructor + /// The instance created by calling that constructor. + public object InvokeConstructor(Type genericBaseType, Type genericParameterType, Type[] parameterTypes, object[] parameterValues) + { + ConstructorInfo ctor = GetConstructor(genericBaseType, genericParameterType, parameterTypes); + Assert.NotNull(parameterValues); + Assert.Equal(parameterTypes.Length, parameterValues.Length); + return ctor.Invoke(parameterValues); + } + + /// + /// Creates a new bound generic type and invokes the constructor matched from . + /// + /// The unbound generic base type. + /// The types of the generic parameters to apply to create a bound generic type. + /// The list of parameter types for a constructor that must exist. + /// The list of values to supply to the constructor + /// The instance created by calling that constructor. + public object InvokeConstructor(Type genericBaseType, Type[] genericParameterTypes, Type[] parameterTypes, object[] parameterValues) + { + ConstructorInfo ctor = GetConstructor(genericBaseType, genericParameterTypes, parameterTypes); + Assert.NotNull(parameterValues); + Assert.Equal(parameterTypes.Length, parameterValues.Length); + return ctor.Invoke(parameterValues); + } + + /// + /// Creates a new bound generic type and invokes the constructor matched from the types of . + /// + /// The unbound generic base type. + /// The type of the single generic parameter to apply to create a bound generic type. + /// The list of values to supply to the constructor. It must be possible to determine the + /// The instance created by calling that constructor. + public object InvokeConstructor(Type genericBaseType, Type genericParameterType, params object[] parameterValues) + { + Assert.NotNull(genericBaseType); + Assert.True(genericBaseType.IsGenericTypeDefinition); + Assert.NotNull(genericParameterType); + + Type genericType = genericBaseType.MakeGenericType(new Type[] { genericParameterType }); + + ConstructorInfo ctor = FindConstructor(genericType, parameterValues); + Assert.True(ctor != null, String.Format("Test error: failed to locate generic ctor for type '{0}<{1}>',", genericBaseType.Name, genericParameterType.Name)); + return ctor.Invoke(parameterValues); + } + + /// + /// Creates a new bound generic type and invokes the constructor matched from the types of . + /// + /// The unbound generic base type. + /// The types of the generic parameters to apply to create a bound generic type. + /// The list of values to supply to the constructor. It must be possible to determine the + /// The instance created by calling that constructor. + public object InvokeConstructor(Type genericBaseType, Type[] genericParameterTypes, params object[] parameterValues) + { + Assert.NotNull(genericBaseType); + Assert.True(genericBaseType.IsGenericTypeDefinition); + Assert.NotNull(genericParameterTypes); + + Type genericType = genericBaseType.MakeGenericType(genericParameterTypes); + + ConstructorInfo ctor = FindConstructor(genericType, parameterValues); + Assert.True(ctor != null, String.Format("Test error: failed to locate generic ctor for type '{0}<>',", genericBaseType.Name)); + return ctor.Invoke(parameterValues); + } + + /// + /// Creates a new bound generic type and invokes the constructor matched from . + /// + /// The type of object the constuctor is expected to yield. + /// The unbound generic base type. + /// The type of the single generic parameter to apply to create a bound generic type. + /// The list of parameter types for a constructor that must exist. + /// The list of values to supply to the constructor + /// An instance of type . + public T InvokeConstructor(Type genericBaseType, Type genericParameterType, Type[] parameterTypes, object[] parameterValues) + { + ConstructorInfo ctor = GetConstructor(genericBaseType, genericParameterType, parameterTypes); + Assert.NotNull(parameterValues); + Assert.Equal(parameterTypes.Length, parameterValues.Length); + return (T)ctor.Invoke(parameterValues); + } + + /// + /// Creates a new bound generic type and invokes the constructor matched from . + /// + /// The type of object the constuctor is expected to yield. + /// The unbound generic base type. + /// The types of the generic parameters to apply to create a bound generic type. + /// The list of parameter types for a constructor that must exist. + /// The list of values to supply to the constructor + /// An instance of type . + public T InvokeConstructor(Type genericBaseType, Type[] genericParameterTypes, Type[] parameterTypes, object[] parameterValues) + { + ConstructorInfo ctor = GetConstructor(genericBaseType, genericParameterTypes, parameterTypes); + Assert.NotNull(parameterValues); + Assert.Equal(parameterTypes.Length, parameterValues.Length); + return (T)ctor.Invoke(parameterValues); + } + + /// + /// Creates a new bound generic type and invokes the constructor matched from . + /// + /// The type of object the constuctor is expected to yield. + /// The unbound generic base type. + /// The type of the single generic parameter to apply to create a bound generic type. + /// The list of values to supply to the constructor. It must be possible to determine the + /// The instance created by calling that constructor. + /// An instance of type . + public T InvokeConstructor(Type genericBaseType, Type genericParameterType, params object[] parameterValues) + { + Assert.NotNull(genericBaseType == null); + Assert.True(genericBaseType.IsGenericTypeDefinition); + Assert.NotNull(genericParameterType); + + Type genericType = genericBaseType.MakeGenericType(new Type[] { genericParameterType }); + + ConstructorInfo ctor = FindConstructor(genericType, parameterValues); + Assert.True(ctor != null, String.Format("Test error: failed to locate generic ctor for type '{0}<{1}>',", genericBaseType.Name, genericParameterType.Name)); + return (T)ctor.Invoke(parameterValues); + } + + /// + /// Creates a new bound generic type and invokes the constructor matched from . + /// + /// The type of object the constuctor is expected to yield. + /// The unbound generic base type. + /// The types of the generic parameters to apply to create a bound generic type. + /// The list of values to supply to the constructor. It must be possible to determine the + /// The instance created by calling that constructor. + /// An instance of type . + public T InvokeConstructor(Type genericBaseType, Type[] genericParameterTypes, params object[] parameterValues) + { + Assert.NotNull(genericBaseType); + Assert.True(genericBaseType.IsGenericTypeDefinition); + Assert.NotNull(genericParameterTypes); + + Type genericType = genericBaseType.MakeGenericType(genericParameterTypes); + + ConstructorInfo ctor = FindConstructor(genericType, parameterValues); + Assert.True(ctor != null, String.Format("Test error: failed to locate generic ctor for type '{0}<>',", genericBaseType.Name)); + return (T)ctor.Invoke(parameterValues); + } + + /// + /// Asserts the given instance is one from a generic type of the specified parameter type. + /// + /// The type of instance. + /// The instance to test. + /// The type of the generic parameter to which the instance's generic type should have been bound. + public void IsCorrectGenericType(T instance, Type genericTypeParameter) + { + Assert.NotNull(instance); + Assert.NotNull(genericTypeParameter); + Assert.True(instance.GetType().IsGenericType); + Type[] genericArguments = instance.GetType().GetGenericArguments(); + Assert.Equal(1, genericArguments.Length); + Assert.Equal(genericTypeParameter, genericArguments[0]); + } + + /// + /// Invokes via Reflection the method on the given instance. + /// + /// The instance to use. + /// The name of the method to call. + /// The types of the parameters to the method. + /// The values to supply to the method. + /// The results of the method. + public object InvokeMethod(object instance, string methodName, Type[] parameterTypes, object[] parameterValues) + { + Assert.NotNull(instance); + Assert.NotNull(parameterTypes); + Assert.NotNull(parameterValues); + Assert.Equal(parameterTypes.Length, parameterValues.Length); + MethodInfo methodInfo = instance.GetType().GetMethod(methodName, parameterTypes); + Assert.NotNull(methodInfo); + return methodInfo.Invoke(instance, parameterValues); + } + + /// + /// Invokes via Reflection the static method on the given type. + /// + /// The type containing the method. + /// The name of the method to call. + /// The types of the parameters to the method. + /// The values to supply to the method. + /// The results of the method. + public object InvokeMethod(Type type, string methodName, Type[] parameterTypes, object[] parameterValues) + { + Assert.NotNull(type); + Assert.NotNull(parameterTypes); + Assert.NotNull(parameterValues); + Assert.Equal(parameterTypes.Length, parameterValues.Length); + MethodInfo methodInfo = type.GetMethod(methodName, parameterTypes); + Assert.NotNull(methodInfo); + return methodInfo.Invoke(null, parameterValues); + } + + /// + /// Invokes via Reflection the static method on the given type. + /// + /// The type containing the method. + /// The name of the method to call. + /// The generic parameter type of the method. + /// The types of the parameters to the method. + /// The values to supply to the method. + /// The results of the method. + public MethodInfo CreateGenericMethod(Type type, string methodName, Type genericParameterType, Type[] parameterTypes) + { + Assert.NotNull(type); + Assert.NotNull(parameterTypes); + Assert.NotNull(genericParameterType); + //MethodInfo methodInfo = type.GetMethod(methodName, parameterTypes); + MethodInfo methodInfo = type.GetMethods().Where((m) => m.Name.Equals(methodName, StringComparison.OrdinalIgnoreCase) && m.IsGenericMethod && AreAssignableFrom(m.GetParameters(), parameterTypes)).FirstOrDefault(); + Assert.NotNull(methodInfo); + Assert.True(methodInfo.IsGenericMethod); + MethodInfo genericMethod = methodInfo.MakeGenericMethod(genericParameterType); + Assert.NotNull(genericMethod); + return genericMethod; + } + + /// + /// Invokes via Reflection the static generic method on the given type. + /// + /// The type containing the method. + /// The name of the method to call. + /// The generic parameter type of the method. + /// The types of the parameters to the method. + /// The values to supply to the method. + /// The results of the method. + public object InvokeGenericMethod(Type type, string methodName, Type genericParameterType, Type[] parameterTypes, object[] parameterValues) + { + MethodInfo methodInfo = CreateGenericMethod(type, methodName, genericParameterType, parameterTypes); + Assert.Equal(parameterTypes.Length, parameterValues.Length); + return methodInfo.Invoke(null, parameterValues); + } + + /// + /// Invokes via Reflection the generic method on the given instance. + /// + /// The instance on which to invoke the method. + /// The name of the method to call. + /// The generic parameter type of the method. + /// The types of the parameters to the method. + /// The values to supply to the method. + /// The results of the method. + public object InvokeGenericMethod(object instance, string methodName, Type genericParameterType, Type[] parameterTypes, object[] parameterValues) + { + Assert.NotNull(instance); + MethodInfo methodInfo = CreateGenericMethod(instance.GetType(), methodName, genericParameterType, parameterTypes); + Assert.Equal(parameterTypes.Length, parameterValues.Length); + return methodInfo.Invoke(instance, parameterValues); + } + + /// + /// Invokes via Reflection the generic method on the given instance. + /// + /// The type of the return value from the method. + /// The instance on which to invoke the method. + /// The name of the method to call. + /// The generic parameter type of the method. + /// The types of the parameters to the method. + /// The values to supply to the method. + /// The results of the method. + public T InvokeGenericMethod(object instance, string methodName, Type genericParameterType, Type[] parameterTypes, object[] parameterValues) + { + return (T)InvokeGenericMethod(instance, methodName, genericParameterType, parameterTypes, parameterValues); + } + + /// + /// Invokes via Reflection the method on the given instance. + /// + /// The instance to use. + /// The name of the method to call. + /// The values to supply to the method. + /// The results of the method. + public object InvokeMethod(object instance, string methodName, params object[] parameterValues) + { + Assert.NotNull(instance); + MethodInfo methodInfo = FindMethod(instance.GetType(), methodName, parameterValues); + Assert.NotNull(methodInfo); + return methodInfo.Invoke(instance, parameterValues); + } + + /// + /// Invokes via Reflection the static method on the given type. + /// + /// The instance to use. + /// The name of the method to call. + /// The values to supply to the method. + /// The results of the method. + public object InvokeMethod(Type type, string methodName, params object[] parameterValues) + { + Assert.NotNull(type); + MethodInfo methodInfo = FindMethod(type, methodName, parameterValues); + Assert.NotNull(methodInfo); + return methodInfo.Invoke(null, parameterValues); + } + + /// + /// Invokes via Reflection the method on the given instance. + /// + /// The instance to use. + /// The name of the method to call. + /// The type of the generic parameter. + /// The values to supply to the method. + /// The results of the method. + public object InvokeGenericMethod(object instance, string methodName, Type genericParameterType, params object[] parameterValues) + { + Assert.NotNull(instance); + Assert.NotNull(genericParameterType); + MethodInfo methodInfo = FindMethod(instance.GetType(), methodName, parameterValues); + Assert.NotNull(methodInfo); + Assert.True(methodInfo.IsGenericMethod); + MethodInfo genericMethod = methodInfo.MakeGenericMethod(genericParameterType); + return genericMethod.Invoke(instance, parameterValues); + } + + /// + /// Invokes via Reflection the method on the given instance. + /// + /// The instance to use. + /// The name of the method to call. + /// The type of the generic parameter. + /// The values to supply to the method. + /// The results of the method. + public object InvokeGenericMethod(Type type, string methodName, Type genericParameterType, params object[] parameterValues) + { + Assert.NotNull(type); + Assert.NotNull(genericParameterType); + MethodInfo methodInfo = FindMethod(type, methodName, parameterValues); + Assert.NotNull(methodInfo); + Assert.True(methodInfo.IsGenericMethod); + MethodInfo genericMethod = methodInfo.MakeGenericMethod(genericParameterType); + return genericMethod.Invoke(null, parameterValues); + } + + /// + /// Retrieves the value from the specified property. + /// + /// The instance containing the property value. + /// The name of the property. + /// The error message to prefix any test assertions. + /// The value returned from the property. + public object GetProperty(object instance, string propertyName, string failureMessage) + { + PropertyInfo propertyInfo = instance.GetType().GetProperty(propertyName); + Assert.NotNull(propertyInfo); + return propertyInfo.GetValue(instance, null); + } + + private static bool AreAssignableFrom(Type[] parameterTypes, params object[] parameterValues) + { + Assert.NotNull(parameterTypes); + Assert.NotNull(parameterValues); + if (parameterTypes.Length != parameterValues.Length) + { + return false; + } + + for (int i = 0; i < parameterTypes.Length; ++i) + { + if (!parameterTypes[i].IsInstanceOfType(parameterValues[i])) + { + return false; + } + } + + return true; + } + + private static bool AreAssignableFrom(ParameterInfo[] parameterInfos, params Type[] parameterTypes) + { + Assert.NotNull(parameterInfos); + Assert.NotNull(parameterTypes); + Type[] parameterInfoTypes = parameterInfos.Select((info) => info.ParameterType).ToArray(); + if (parameterInfoTypes.Length != parameterTypes.Length) + { + return false; + } + + for (int i = 0; i < parameterInfoTypes.Length; ++i) + { + // Generic parameters are assumed to be assignable + if (parameterInfoTypes[i].IsGenericParameter) + { + continue; + } + + if (!parameterInfoTypes[i].IsAssignableFrom(parameterTypes[i])) + { + return false; + } + } + + return true; + } + + private static bool AreAssignableFrom(ParameterInfo[] parameterInfos, params object[] parameterValues) + { + Assert.NotNull(parameterInfos); + Assert.NotNull(parameterValues); + Type[] parameterTypes = parameterInfos.Select((info) => info.ParameterType).ToArray(); + return AreAssignableFrom(parameterTypes, parameterValues); + } + + private static ConstructorInfo FindConstructor(Type type, params object[] parameterValues) + { + Assert.NotNull(type); + Assert.NotNull(parameterValues); + return type.GetConstructors().FirstOrDefault((c) => AreAssignableFrom(c.GetParameters(), parameterValues)); + } + + private static MethodInfo FindMethod(Type type, string methodName, params object[] parameterValues) + { + Assert.NotNull(type); + Assert.False(String.IsNullOrWhiteSpace(methodName)); + Assert.NotNull(parameterValues); + return type.GetMethods().FirstOrDefault((m) => String.Equals(m.Name, methodName, StringComparison.Ordinal) && AreAssignableFrom(m.GetParameters(), parameterValues)); + } + } +} diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/HttpAssert.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/HttpAssert.cs new file mode 100644 index 0000000000..1054ea538f --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/HttpAssert.cs @@ -0,0 +1,256 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text.RegularExpressions; + +namespace Microsoft.TestCommon +{ + /// + /// Unit test utility for testing instances. + /// + public class HttpAssert + { + private const string CommaSeperator = ", "; + private static readonly HttpAssert singleton = new HttpAssert(); + + public static HttpAssert Singleton { get { return singleton; } } + + /// + /// Asserts that the expected is equal to the actual . + /// + /// The expected . Should not be null. + /// The actual . Should not be null. + public void Equal(HttpRequestMessage expected, HttpRequestMessage actual) + { + Assert.NotNull(expected); + Assert.NotNull(actual); + + Assert.Equal(expected.Version, actual.Version); + Equal(expected.Headers, actual.Headers); + + if (expected.Content == null) + { + Assert.Null(actual.Content); + } + else + { + string expectedContent = CleanContentString(expected.Content.ReadAsStringAsync().Result); + string actualContent = CleanContentString(actual.Content.ReadAsStringAsync().Result); + Assert.Equal(expectedContent, actualContent); + Equal(expected.Content.Headers, actual.Content.Headers); + } + } + + /// + /// Asserts that the expected is equal to the actual . + /// + /// The expected . Should not be null. + /// The actual . Should not be null. + public void Equal(HttpResponseMessage expected, HttpResponseMessage actual) + { + Equal(expected, actual, null); + } + + /// + /// Asserts that the expected is equal to the actual . + /// + /// The expected . Should not be null. + /// The actual . Should not be null. + /// The callback to verify the Content string. If it is null, Assert.Equal will be used. + public void Equal(HttpResponseMessage expected, HttpResponseMessage actual, Action verifyContentStringCallback) + { + Assert.NotNull(expected); + Assert.NotNull(actual); + + Assert.Equal(expected.StatusCode, actual.StatusCode); + Assert.Equal(expected.ReasonPhrase, actual.ReasonPhrase); + Assert.Equal(expected.Version, actual.Version); + Equal(expected.Headers, actual.Headers); + + if (expected.Content == null) + { + Assert.Null(actual.Content); + } + else + { + string expectedContent = CleanContentString(expected.Content.ReadAsStringAsync().Result); + string actualContent = CleanContentString(actual.Content.ReadAsStringAsync().Result); + if (verifyContentStringCallback != null) + { + verifyContentStringCallback(expectedContent, actualContent); + } + else + { + Assert.Equal(expectedContent, actualContent); + } + Equal(expected.Content.Headers, actual.Content.Headers); + } + } + + /// + /// Asserts that the expected instance is equal to the actual instance. + /// + /// The expected instance. Should not be null. + /// The actual instance. Should not be null. + public void Equal(HttpHeaders expectedHeaders, HttpHeaders actualHeaders) + { + Assert.NotNull(expectedHeaders); + Assert.NotNull(actualHeaders); + + Assert.Equal(expectedHeaders.Count(), actualHeaders.Count()); + + foreach (KeyValuePair> expectedHeader in expectedHeaders) + { + KeyValuePair> actualHeader = actualHeaders.FirstOrDefault(h => h.Key == expectedHeader.Key); + Assert.NotNull(actualHeader); + + if (expectedHeader.Key == "Date") + { + HandleDateHeader(expectedHeader.Value.ToArray(), actualHeader.Value.ToArray()); + } + else + { + string expectedHeaderStr = string.Join(CommaSeperator, expectedHeader.Value); + string actualHeaderStr = string.Join(CommaSeperator, actualHeader.Value); + Assert.Equal(expectedHeaderStr, actualHeaderStr); + } + } + } + + /// + /// Asserts the given contain the given + /// for the given . + /// + /// The to examine. It cannot be null. + /// The name of the header. It cannot be empty. + /// The values that must all be present. It cannot be null. + public void Contains(HttpHeaders headers, string name, params string[] values) + { + Assert.NotNull(headers); + Assert.False(String.IsNullOrWhiteSpace(name), "Test error: name cannot be empty."); + Assert.NotNull(values); + + IEnumerable headerValues = null; + bool foundIt = headers.TryGetValues(name, out headerValues); + Assert.True(foundIt); + + foreach (string value in values) + { + Assert.Contains(value, headerValues); + } + } + + public bool IsKnownUnserializableType(Type type, Func isTypeUnserializableCallback) + { + if (isTypeUnserializableCallback != null && isTypeUnserializableCallback(type)) + { + return true; + } + + if (type.IsGenericType) + { + if (typeof(IEnumerable).IsAssignableFrom(type)) + { + if (type.GetMethod("Add") == null) + { + return true; + } + } + + // Generic type -- recursively analyze generic arguments + return IsKnownUnserializableType(type.GetGenericArguments()[0], isTypeUnserializableCallback); + } + + if (type.HasElementType && IsKnownUnserializableType(type.GetElementType(), isTypeUnserializableCallback)) + { + return true; + } + + return false; + } + + public bool IsKnownUnserializable(Type type, object obj, Func isTypeUnserializableCallback) + { + if (IsKnownUnserializableType(type, isTypeUnserializableCallback)) + { + return true; + } + + return obj != null && IsKnownUnserializableType(obj.GetType(), isTypeUnserializableCallback); + } + + public bool IsKnownUnserializable(Type type, object obj) + { + return IsKnownUnserializable(type, obj, null); + } + + public bool CanRoundTrip(Type type) + { + if (typeof(DateTime).IsAssignableFrom(type)) + { + return false; + } + + if (typeof(DateTimeOffset).IsAssignableFrom(type)) + { + return false; + } + + if (type.IsGenericType) + { + foreach (Type genericParameterType in type.GetGenericArguments()) + { + if (!CanRoundTrip(genericParameterType)) + { + return false; + } + } + } + + if (type.HasElementType) + { + return CanRoundTrip(type.GetElementType()); + } + + return true; + } + + private static void HandleDateHeader(string[] expectedDateHeaderValues, string[] actualDateHeaderValues) + { + Assert.Equal(expectedDateHeaderValues.Length, actualDateHeaderValues.Length); + + for (int i = 0; i < expectedDateHeaderValues.Length; i++) + { + DateTime expectedDateTime = DateTime.Parse(expectedDateHeaderValues[i]); + DateTime actualDateTime = DateTime.Parse(actualDateHeaderValues[i]); + + Assert.Equal(expectedDateTime.Year, actualDateTime.Year); + Assert.Equal(expectedDateTime.Month, actualDateTime.Month); + Assert.Equal(expectedDateTime.Day, actualDateTime.Day); + + int hourDifference = Math.Abs(actualDateTime.Hour - expectedDateTime.Hour); + Assert.True(hourDifference <= 1); + + int minuteDifference = Math.Abs(actualDateTime.Minute - expectedDateTime.Minute); + Assert.True(minuteDifference <= 1); + } + } + + private static string CleanContentString(string content) + { + Assert.Null(content); + + string cleanedContent = null; + + // remove any port numbers from Uri's + cleanedContent = Regex.Replace(content, ":\\d+", ""); + + return cleanedContent; + } + } +} diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/MediaTypeAssert.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/MediaTypeAssert.cs new file mode 100644 index 0000000000..0bca8e37f5 --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/MediaTypeAssert.cs @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Net.Http.Headers; + +namespace Microsoft.TestCommon +{ + public class MediaTypeAssert + { + private static readonly MediaTypeAssert singleton = new MediaTypeAssert(); + + public static MediaTypeAssert Singleton { get { return singleton; } } + + public void AreEqual(MediaTypeHeaderValue expected, MediaTypeHeaderValue actual, string errorMessage) + { + if (expected != null || actual != null) + { + Assert.NotNull(expected); + Assert.Equal(0, new MediaTypeHeaderValueComparer().Compare(expected, actual)); + } + } + + public void AreEqual(MediaTypeHeaderValue expected, string actual, string errorMessage) + { + if (expected != null || !String.IsNullOrEmpty(actual)) + { + MediaTypeHeaderValue actualMediaType = new MediaTypeHeaderValue(actual); + Assert.NotNull(expected); + Assert.Equal(0, new MediaTypeHeaderValueComparer().Compare(expected, actualMediaType)); + } + } + + public void AreEqual(string expected, string actual, string errorMessage) + { + if (!String.IsNullOrEmpty(expected) || !String.IsNullOrEmpty(actual)) + { + Assert.NotNull(expected); + MediaTypeHeaderValue expectedMediaType = new MediaTypeHeaderValue(expected); + MediaTypeHeaderValue actualMediaType = new MediaTypeHeaderValue(actual); + Assert.Equal(0, new MediaTypeHeaderValueComparer().Compare(expectedMediaType, actualMediaType)); + } + } + + public void AreEqual(string expected, MediaTypeHeaderValue actual, string errorMessage) + { + if (!String.IsNullOrEmpty(expected) || actual != null) + { + Assert.NotNull(expected); + MediaTypeHeaderValue expectedMediaType = new MediaTypeHeaderValue(expected); + Assert.Equal(0, new MediaTypeHeaderValueComparer().Compare(expectedMediaType, actual)); + } + } + } +} diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/MediaTypeHeaderValueComparer.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/MediaTypeHeaderValueComparer.cs new file mode 100644 index 0000000000..d454146a5c --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/MediaTypeHeaderValueComparer.cs @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Net.Http.Headers; + +namespace Microsoft.TestCommon +{ + public class MediaTypeHeaderValueComparer : IComparer + { + private static readonly MediaTypeHeaderValueComparer mediaTypeComparer = new MediaTypeHeaderValueComparer(); + + public MediaTypeHeaderValueComparer() + { + } + + public static MediaTypeHeaderValueComparer Comparer + { + get + { + return mediaTypeComparer; + } + } + + public int Compare(MediaTypeHeaderValue mediaType1, MediaTypeHeaderValue mediaType2) + { + ParsedMediaTypeHeaderValue parsedMediaType1 = new ParsedMediaTypeHeaderValue(mediaType1); + ParsedMediaTypeHeaderValue parsedMediaType2 = new ParsedMediaTypeHeaderValue(mediaType2); + + int returnValue = CompareBasedOnQualityFactor(parsedMediaType1, parsedMediaType2); + + if (returnValue == 0) + { + if (!String.Equals(parsedMediaType1.Type, parsedMediaType2.Type, StringComparison.OrdinalIgnoreCase)) + { + if (parsedMediaType1.IsAllMediaRange) + { + return 1; + } + else if (parsedMediaType2.IsAllMediaRange) + { + return -1; + } + } + else if (!String.Equals(parsedMediaType1.SubType, parsedMediaType2.SubType, StringComparison.OrdinalIgnoreCase)) + { + if (parsedMediaType1.IsSubTypeMediaRange) + { + return 1; + } + else if (parsedMediaType2.IsSubTypeMediaRange) + { + return -1; + } + } + else + { + if (!parsedMediaType1.HasNonQualityFactorParameter) + { + if (parsedMediaType2.HasNonQualityFactorParameter) + { + return 1; + } + } + else if (!parsedMediaType2.HasNonQualityFactorParameter) + { + return -1; + } + } + } + + return returnValue; + } + + private static int CompareBasedOnQualityFactor(ParsedMediaTypeHeaderValue parsedMediaType1, ParsedMediaTypeHeaderValue parsedMediaType2) + { + double qualityDifference = parsedMediaType1.QualityFactor - parsedMediaType2.QualityFactor; + if (qualityDifference < 0) + { + return 1; + } + else if (qualityDifference > 0) + { + return -1; + } + + return 0; + } + } +} \ No newline at end of file diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/ParsedMediaTypeHeaderValue.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/ParsedMediaTypeHeaderValue.cs new file mode 100644 index 0000000000..a71a21825c --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/ParsedMediaTypeHeaderValue.cs @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Net.Http.Headers; + +namespace Microsoft.TestCommon +{ + internal class ParsedMediaTypeHeaderValue + { + private const string MediaRangeAsterisk = "*"; + private const char MediaTypeSubTypeDelimiter = '/'; + private const string QualityFactorParameterName = "q"; + private const double DefaultQualityFactor = 1.0; + + private MediaTypeHeaderValue mediaType; + private string type; + private string subType; + private bool? hasNonQualityFactorParameter; + private double? qualityFactor; + + public ParsedMediaTypeHeaderValue(MediaTypeHeaderValue mediaType) + { + this.mediaType = mediaType; + string[] splitMediaType = mediaType.MediaType.Split(MediaTypeSubTypeDelimiter); + this.type = splitMediaType[0]; + this.subType = splitMediaType[1]; + } + + public string Type + { + get + { + return this.type; + } + } + + public string SubType + { + get + { + return this.subType; + } + } + + public bool IsAllMediaRange + { + get + { + return this.IsSubTypeMediaRange && String.Equals(MediaRangeAsterisk, this.Type, StringComparison.Ordinal); + } + } + + public bool IsSubTypeMediaRange + { + get + { + return String.Equals(MediaRangeAsterisk, this.SubType, StringComparison.Ordinal); + } + } + + public bool HasNonQualityFactorParameter + { + get + { + if (!this.hasNonQualityFactorParameter.HasValue) + { + this.hasNonQualityFactorParameter = false; + foreach (NameValueHeaderValue param in this.mediaType.Parameters) + { + if (!String.Equals(QualityFactorParameterName, param.Name, StringComparison.Ordinal)) + { + this.hasNonQualityFactorParameter = true; + } + } + } + + return this.hasNonQualityFactorParameter.Value; + } + } + + public string CharSet + { + get + { + return this.mediaType.CharSet; + } + } + + public double QualityFactor + { + get + { + if (!this.qualityFactor.HasValue) + { + MediaTypeWithQualityHeaderValue mediaTypeWithQuality = this.mediaType as MediaTypeWithQualityHeaderValue; + if (mediaTypeWithQuality != null) + { + this.qualityFactor = mediaTypeWithQuality.Quality; + } + + if (!this.qualityFactor.HasValue) + { + this.qualityFactor = DefaultQualityFactor; + } + } + + return this.qualityFactor.Value; + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/RegexReplacement.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/RegexReplacement.cs new file mode 100644 index 0000000000..36cf366c02 --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/RegexReplacement.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.Text.RegularExpressions; + +namespace Microsoft.TestCommon +{ + public class RegexReplacement + { + Regex regex; + string replacement; + + public RegexReplacement(Regex regex, string replacement) + { + this.regex = regex; + this.replacement = replacement; + } + + public RegexReplacement(string regex, string replacement) + { + this.regex = new Regex(regex); + this.replacement = replacement; + } + + public Regex Regex + { + get + { + return this.regex; + } + } + + public string Replacement + { + get + { + return this.replacement; + } + } + } +} diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/RuntimeEnvironment.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/RuntimeEnvironment.cs new file mode 100644 index 0000000000..b0cd0eccf9 --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/RuntimeEnvironment.cs @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using Microsoft.Win32; + +namespace Microsoft.TestCommon +{ + public static class RuntimeEnvironment + { + private const string NetFx40FullSubKey = @"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full"; + private const string Version = "Version"; + + static RuntimeEnvironment() + { + object runtimeVersion = Registry.LocalMachine.OpenSubKey(RuntimeEnvironment.NetFx40FullSubKey).GetValue(RuntimeEnvironment.Version); + string versionFor40String = runtimeVersion as string; + if (versionFor40String != null) + { + VersionFor40 = new Version(versionFor40String); + } + } + + private static Version VersionFor40; + + public static bool IsVersion45Installed + { + get + { + return VersionFor40.Major > 4 || (VersionFor40.Major == 4 && VersionFor40.Minor >= 5); + } + } + } +} diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/SerializerAssert.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/SerializerAssert.cs new file mode 100644 index 0000000000..feaca48ac1 --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/SerializerAssert.cs @@ -0,0 +1,152 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Json; +using System.Xml.Serialization; + +namespace Microsoft.TestCommon +{ + /// + /// MSTest utility for testing code operating against a stream. + /// + public class SerializerAssert + { + private static SerializerAssert singleton = new SerializerAssert(); + + public static SerializerAssert Singleton { get { return singleton; } } + + /// + /// Creates a , serializes to it using + /// , rewinds the stream and calls . + /// + /// The type to serialize. It cannot be null. + /// The value to serialize. + /// Code to check the contents of the stream. + public void UsingXmlSerializer(Type type, object objectInstance, Action codeThatChecks) + { + if (type == null) + { + throw new ArgumentNullException("type"); + } + + if (codeThatChecks == null) + { + throw new ArgumentNullException("codeThatChecks"); + } + + XmlSerializer serializer = new XmlSerializer(type); + + using (MemoryStream stream = new MemoryStream()) + { + serializer.Serialize(stream, objectInstance); + + stream.Flush(); + stream.Seek(0L, SeekOrigin.Begin); + + codeThatChecks(stream); + } + } + + /// + /// Creates a , serializes to it using + /// , rewinds the stream and calls . + /// + /// The type to serialize. + /// The value to serialize. + /// Code to check the contents of the stream. + public void UsingXmlSerializer(T objectInstance, Action codeThatChecks) + { + UsingXmlSerializer(typeof(T), objectInstance, codeThatChecks); + } + + /// + /// Creates a , serializes to it using + /// , rewinds the stream and calls . + /// + /// The type to serialize. It cannot be null. + /// The value to serialize. + /// Code to check the contents of the stream. + public void UsingDataContractSerializer(Type type, object objectInstance, Action codeThatChecks) + { + if (type == null) + { + throw new ArgumentNullException("type"); + } + + if (codeThatChecks == null) + { + throw new ArgumentNullException("codeThatChecks"); + } + + DataContractSerializer serializer = new DataContractSerializer(type); + + using (MemoryStream stream = new MemoryStream()) + { + serializer.WriteObject(stream, objectInstance); + + stream.Flush(); + stream.Seek(0L, SeekOrigin.Begin); + + codeThatChecks(stream); + } + } + + /// + /// Creates a , serializes to it using + /// , rewinds the stream and calls . + /// + /// The type to serialize. + /// The value to serialize. + /// Code to check the contents of the stream. + public void UsingDataContractSerializer(T objectInstance, Action codeThatChecks) + { + UsingDataContractSerializer(typeof(T), objectInstance, codeThatChecks); + } + + /// + /// Creates a , serializes to it using + /// , rewinds the stream and calls . + /// + /// The type to serialize. It cannot be null. + /// The value to serialize. + /// Code to check the contents of the stream. + public static void UsingDataContractJsonSerializer(Type type, object objectInstance, Action codeThatChecks) + { + if (type == null) + { + throw new ArgumentNullException("type"); + } + + if (codeThatChecks == null) + { + throw new ArgumentNullException("codeThatChecks"); + } + + DataContractJsonSerializer serializer = new DataContractJsonSerializer(type); + + using (MemoryStream stream = new MemoryStream()) + { + serializer.WriteObject(stream, objectInstance); + + stream.Flush(); + stream.Seek(0L, SeekOrigin.Begin); + + codeThatChecks(stream); + } + } + + /// + /// Creates a , serializes to it using + /// , rewinds the stream and calls . + /// + /// The type to serialize. + /// The value to serialize. + /// Code to check the contents of the stream. + public void UsingDataContractJsonSerializer(T objectInstance, Action codeThatChecks) + { + UsingDataContractJsonSerializer(typeof(T), objectInstance, codeThatChecks); + } + } +} diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/StreamAssert.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/StreamAssert.cs new file mode 100644 index 0000000000..c0ab0a5aac --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/StreamAssert.cs @@ -0,0 +1,96 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.IO; + +namespace Microsoft.TestCommon +{ + //// TODO RONCAIN using System.Runtime.Serialization.Json; + + /// + /// MSTest utility for testing code operating against a stream. + /// + public class StreamAssert + { + private static StreamAssert singleton = new StreamAssert(); + + public static StreamAssert Singleton { get { return singleton; } } + + /// + /// Creates a , invokes to write to it, + /// rewinds the stream to the beginning and invokes . + /// + /// Code to write to the stream. It cannot be null. + /// Code that reads from the stream. It cannot be null. + public void WriteAndRead(Action codeThatWrites, Action codeThatReads) + { + if (codeThatWrites == null) + { + throw new ArgumentNullException("codeThatWrites"); + } + + if (codeThatReads == null) + { + throw new ArgumentNullException("codeThatReads"); + } + + using (MemoryStream stream = new MemoryStream()) + { + codeThatWrites(stream); + + stream.Flush(); + stream.Seek(0L, SeekOrigin.Begin); + + codeThatReads(stream); + } + } + + /// + /// Creates a , invokes to write to it, + /// rewinds the stream to the beginning and invokes to obtain + /// the result to return from this method. + /// + /// Code to write to the stream. It cannot be null. + /// Code that reads from the stream and returns the result. It cannot be null. + /// The value returned from . + public static object WriteAndReadResult(Action codeThatWrites, Func codeThatReads) + { + if (codeThatWrites == null) + { + throw new ArgumentNullException("codeThatWrites"); + } + + if (codeThatReads == null) + { + throw new ArgumentNullException("codeThatReads"); + } + + object result = null; + using (MemoryStream stream = new MemoryStream()) + { + codeThatWrites(stream); + + stream.Flush(); + stream.Seek(0L, SeekOrigin.Begin); + + result = codeThatReads(stream); + } + + return result; + } + + /// + /// Creates a , invokes to write to it, + /// rewinds the stream to the beginning and invokes to obtain + /// the result to return from this method. + /// + /// The type of the result expected. + /// Code to write to the stream. It cannot be null. + /// Code that reads from the stream and returns the result. It cannot be null. + /// The value returned from . + public T WriteAndReadResult(Action codeThatWrites, Func codeThatReads) + { + return (T)WriteAndReadResult(codeThatWrites, codeThatReads); + } + } +} diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/TaskAssert.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/TaskAssert.cs new file mode 100644 index 0000000000..b49ae0ddb0 --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/TaskAssert.cs @@ -0,0 +1,100 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Reflection; +using System.Threading.Tasks; + +namespace Microsoft.TestCommon +{ + /// + /// MSTest assert class to make assertions about tests using . + /// + public class TaskAssert + { + private static int timeOutMs = System.Diagnostics.Debugger.IsAttached ? TimeoutConstant.DefaultTimeout : TimeoutConstant.DefaultTimeout * 10; + private static TaskAssert singleton = new TaskAssert(); + + public static TaskAssert Singleton { get { return singleton; } } + + /// + /// Asserts the given task has been started. TAP guidelines are that all + /// objects returned from public API's have been started. + /// + /// The to test. + public void IsStarted(Task task) + { + Assert.NotNull(task); + Assert.True(task.Status != TaskStatus.Created); + } + + /// + /// Asserts the given task completes successfully. This method will block the + /// current thread waiting for the task, but will timeout if it does not complete. + /// + /// The to test. + public void Succeeds(Task task) + { + IsStarted(task); + task.Wait(timeOutMs); + AggregateException aggregateException = task.Exception; + Exception innerException = aggregateException == null ? null : aggregateException.InnerException; + Assert.Null(innerException); + } + + /// + /// Asserts the given task completes successfully and returns a result. + /// Use this overload for a generic whose generic parameter is not known at compile time. + /// This method will block the current thread waiting for the task, but will timeout if it does not complete. + /// + /// The to test. + /// The result from that task. + public object SucceedsWithResult(Task task) + { + Succeeds(task); + Assert.True(task.GetType().IsGenericType); + Type[] genericArguments = task.GetType().GetGenericArguments(); + Assert.Equal(1, genericArguments.Length); + PropertyInfo resultProperty = task.GetType().GetProperty("Result"); + Assert.NotNull(resultProperty); + return resultProperty.GetValue(task, null); + } + + /// + /// Asserts the given task completes successfully and returns a result. + /// This method will block the current thread waiting for the task, but will timeout if it does not complete. + /// + /// The result of the . + /// The to test. + /// The result from that task. + public T SucceedsWithResult(Task task) + { + Succeeds(task); + return task.Result; + } + + /// + /// Asserts the given completes successfully and yields + /// the expected result. + /// + /// The to test. + /// The expected result. + public void ResultEquals(Task task, object expectedObj) + { + object actualObj = SucceedsWithResult(task); + Assert.Equal(expectedObj, actualObj); + } + + /// + /// Asserts the given completes successfully and yields + /// the expected result. + /// + /// The type the task will return. + /// The task to test. + /// The expected result. + public void ResultEquals(Task task, T expectedObj) + { + T actualObj = SucceedsWithResult(task); + Assert.Equal(expectedObj, actualObj); + } + } +} diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/TestDataSetAttribute.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/TestDataSetAttribute.cs new file mode 100644 index 0000000000..1fa8b8c749 --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/TestDataSetAttribute.cs @@ -0,0 +1,203 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Microsoft.TestCommon +{ + public class TestDataSetAttribute : DataAttribute + { + public Type DeclaringType { get; set; } + + public string PropertyName { get; set; } + + public TestDataVariations TestDataVariations { get; set; } + + private IEnumerable> ExtraDataSets { get; set; } + + public TestDataSetAttribute(Type declaringType, string propertyName, TestDataVariations testDataVariations = TestCommon.TestDataVariations.All) + { + DeclaringType = declaringType; + PropertyName = propertyName; + TestDataVariations = testDataVariations; + ExtraDataSets = new List>(); + } + + public TestDataSetAttribute(Type declaringType, string propertyName, + Type declaringType1, string propertyName1, + TestDataVariations testDataVariations = TestCommon.TestDataVariations.All) + : this(declaringType, propertyName, testDataVariations) + { + ExtraDataSets = new List> { Tuple.Create(declaringType1, propertyName1) }; + } + + public TestDataSetAttribute(Type declaringType, string propertyName, + Type declaringType1, string propertyName1, + Type declaringType2, string propertyName2, + TestDataVariations testDataVariations = TestCommon.TestDataVariations.All) + : this(declaringType, propertyName, testDataVariations) + { + ExtraDataSets = new List> { Tuple.Create(declaringType1, propertyName1), Tuple.Create(declaringType2, propertyName2) }; + } + + public TestDataSetAttribute(Type declaringType, string propertyName, + Type declaringType1, string propertyName1, + Type declaringType2, string propertyName2, + Type declaringType3, string propertyName3, + TestDataVariations testDataVariations = TestCommon.TestDataVariations.All) + : this(declaringType, propertyName, testDataVariations) + { + ExtraDataSets = new List> { Tuple.Create(declaringType1, propertyName1), Tuple.Create(declaringType2, propertyName2), Tuple.Create(declaringType3, propertyName3) }; + } + + public TestDataSetAttribute(Type declaringType, string propertyName, + Type declaringType1, string propertyName1, + Type declaringType2, string propertyName2, + Type declaringType3, string propertyName3, + Type declaringType4, string propertyName4, + TestDataVariations testDataVariations = TestCommon.TestDataVariations.All) + : this(declaringType, propertyName, testDataVariations) + { + ExtraDataSets = new List> + { + Tuple.Create(declaringType1, propertyName1), Tuple.Create(declaringType2, propertyName2), + Tuple.Create(declaringType3, propertyName3), Tuple.Create(declaringType4, propertyName4) + }; + } + + public override IEnumerable GetData(MethodInfo methodUnderTest, Type[] parameterTypes) + { + IEnumerable baseDataSet = GetBaseDataSet(DeclaringType, PropertyName, TestDataVariations); + IEnumerable> extraDataSets = GetExtraDataSets(); + + IEnumerable> finalDataSets = (new[] { baseDataSet }).Concat(extraDataSets); + + var datasets = CrossProduct(finalDataSets); + + return datasets; + } + + private static IEnumerable CrossProduct(IEnumerable> datasets) + { + if (datasets.Count() == 1) + { + foreach (var dataset in datasets.First()) + { + yield return dataset; + } + } + else + { + IEnumerable datasetLeft = datasets.First(); + IEnumerable datasetRight = CrossProduct(datasets.Skip(1)); + + foreach (var dataLeft in datasetLeft) + { + foreach (var dataRight in datasetRight) + { + yield return dataLeft.Concat(dataRight).ToArray(); + } + } + } + } + + // The base data set(first one) can either be a TestDataSet or a TestDataSetCollection + private static IEnumerable GetBaseDataSet(Type declaringType, string propertyName, TestDataVariations variations) + { + return TryGetDataSetFromTestDataCollection(declaringType, propertyName, variations) ?? GetDataSet(declaringType, propertyName); + } + + private IEnumerable> GetExtraDataSets() + { + foreach (var tuple in ExtraDataSets) + { + yield return GetDataSet(tuple.Item1, tuple.Item2); + } + } + + private static object GetTestDataPropertyValue(Type declaringType, string propertyName) + { + PropertyInfo property = declaringType.GetProperty(propertyName, BindingFlags.Static | BindingFlags.Public); + + if (property == null) + { + throw new ArgumentException(String.Format("Could not find public static property {0} on {1}", propertyName, declaringType.FullName)); + } + else + { + return property.GetValue(null, null); + } + } + + private static IEnumerable GetDataSet(Type declaringType, string propertyName) + { + object propertyValue = GetTestDataPropertyValue(declaringType, propertyName); + + // box the dataset items if the property is not a RefTypeTestData + IEnumerable value = (propertyValue as IEnumerable) ?? (propertyValue as IEnumerable).Cast(); + if (value == null) + { + throw new InvalidOperationException(String.Format("{0}.{1} is either null or does not implement IEnumerable", declaringType.FullName, propertyName)); + } + + IEnumerable dataset = value as IEnumerable; + if (dataset != null) + { + return dataset; + } + else + { + return value.Select((data) => new object[] { data }); + } + } + + private static IEnumerable TryGetDataSetFromTestDataCollection(Type declaringType, string propertyName, TestDataVariations variations) + { + object propertyValue = GetTestDataPropertyValue(declaringType, propertyName); + + IEnumerable testDataCollection = propertyValue as IEnumerable; + + return testDataCollection == null ? null : GetDataSetFromTestDataCollection(testDataCollection, variations); + } + + private static IEnumerable GetDataSetFromTestDataCollection(IEnumerable testDataCollection, TestDataVariations variations) + { + foreach (TestData testdataInstance in testDataCollection) + { + foreach (TestDataVariations variation in testdataInstance.GetSupportedTestDataVariations()) + { + if ((variation & variations) == variation) + { + Type variationType = testdataInstance.GetAsTypeOrNull(variation); + object testData = testdataInstance.GetAsTestDataOrNull(variation); + if (AsSingleInstances(variation)) + { + foreach (object obj in (IEnumerable)testData) + { + yield return new object[] { variationType, obj }; + } + } + else + { + yield return new object[] { variationType, testData }; + } + } + } + } + } + + private static bool AsSingleInstances(TestDataVariations variation) + { + return variation == TestDataVariations.AsInstance || + variation == TestDataVariations.AsNullable || + variation == TestDataVariations.AsDerivedType || + variation == TestDataVariations.AsKnownType || + variation == TestDataVariations.AsDataMember || + variation == TestDataVariations.AsClassMember || + variation == TestDataVariations.AsXmlElementProperty; + } + } +} diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/TimeoutConstant.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/TimeoutConstant.cs new file mode 100644 index 0000000000..3064827cff --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/TimeoutConstant.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +namespace Microsoft.TestCommon +{ + /// + /// MSTest timeout constants for use with the . + /// + public class TimeoutConstant + { + private const int seconds = 1000; + + /// + /// The default timeout for test methods. + /// + public const int DefaultTimeout = 30 * seconds; + + /// + /// An extended timeout for longer running test methods. + /// + public const int ExtendedTimeout = 240 * seconds; + } +} diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/TypeAssert.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/TypeAssert.cs new file mode 100644 index 0000000000..9eae1d3d9e --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/TypeAssert.cs @@ -0,0 +1,163 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.TestCommon +{ + /// + /// MSTest utility for testing that a given type has the expected properties such as being public, sealed, etc. + /// + public class TypeAssert + { + /// + /// Specifies a set of type properties to test for using the method. + /// This enumeration has a attribute that allows a bitwise combination of its member values. + /// + [Flags] + public enum TypeProperties + { + /// + /// Indicates that the type must be abstract. + /// + IsAbstract = 0x1, + + /// + /// Indicates that the type must be a class. + /// + IsClass = 0x2, + + /// + /// Indicates that the type must be a COM object. + /// + IsComObject = 0x4, + + /// + /// Indicates that the type must be disposable. + /// + IsDisposable = 0x8, + + /// + /// Indicates that the type must be an enum. + /// + IsEnum = 0x10, + + /// + /// Indicates that the type must be a generic type. + /// + IsGenericType = 0x20, + + /// + /// Indicates that the type must be a generic type definition. + /// + IsGenericTypeDefinition = 0x40, + + /// + /// Indicates that the type must be an interface. + /// + IsInterface = 0x80, + + /// + /// Indicates that the type must be nested and declared private. + /// + IsNestedPrivate = 0x100, + + /// + /// Indicates that the type must be nested and declared public. + /// + IsNestedPublic = 0x200, + + /// + /// Indicates that the type must be public. + /// + IsPublic = 0x400, + + /// + /// Indicates that the type must be sealed. + /// + IsSealed = 0x800, + + /// + /// Indicates that the type must be visible outside the assembly. + /// + IsVisible = 0x1000, + + /// + /// Indicates that the type must be static. + /// + IsStatic = TypeAssert.TypeProperties.IsAbstract | TypeAssert.TypeProperties.IsSealed, + + /// + /// Indicates that the type must be a public, visible class. + /// + IsPublicVisibleClass = TypeAssert.TypeProperties.IsClass | TypeAssert.TypeProperties.IsPublic | TypeAssert.TypeProperties.IsVisible + } + + private static void CheckProperty(Type type, bool expected, bool actual, string property) + { + Assert.NotNull(type); + Assert.True(expected == actual, String.Format("Type '{0}' should{1} be {2}.", type.FullName, expected ? "" : " NOT", property)); + } + + /// + /// Determines whether the specified type has a given set of properties such as being public, sealed, etc. + /// The method asserts if one or more of the properties are not satisfied. + /// + /// The type to test for properties. + /// The set of type properties to test for. + public void HasProperties(TypeProperties typeProperties) + { + HasProperties(typeof(T), typeProperties); + } + + /// + /// Determines whether the specified type has a given set of properties such as being public, sealed, etc. + /// The method asserts if one or more of the properties are not satisfied. + /// + /// The type to test for properties. + /// Verify that the type to test is assignable from this type. + /// The set of type properties to test for. + public void HasProperties(TypeProperties typeProperties) + { + HasProperties(typeof(T), typeProperties, typeof(TIsAssignableFrom)); + } + + /// + /// Determines whether the specified type has a given set of properties such as being public, sealed, etc. + /// The method asserts if one or more of the properties are not satisfied. + /// + /// The type to test for properties. + /// The set of type properties to test for. + public void HasProperties(Type type, TypeProperties typeProperties) + { + HasProperties(type, typeProperties, null); + } + + /// + /// Determines whether the specified type has a given set of properties such as being public, sealed, etc. + /// The method asserts if one or more of the properties are not satisfied. + /// + /// The type to test for properties. + /// The set of type properties to test for. + /// Verify that the type to test is assignable from this type. + public void HasProperties(Type type, TypeProperties typeProperties, Type isAssignableFrom) + { + TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsAbstract) > 0, type.IsAbstract, "abstract"); + TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsClass) > 0, type.IsClass, "a class"); + TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsComObject) > 0, type.IsCOMObject, "a COM object"); + TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsDisposable) > 0, typeof(IDisposable).IsAssignableFrom(type), "disposable"); + TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsEnum) > 0, type.IsEnum, "an enum"); + TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsGenericType) > 0, type.IsGenericType, "a generic type"); + TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsGenericTypeDefinition) > 0, type.IsGenericTypeDefinition, "a generic type definition"); + TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsInterface) > 0, type.IsInterface, "an interface"); + TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsNestedPrivate) > 0, type.IsNestedPrivate, "nested private"); + TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsNestedPublic) > 0, type.IsNestedPublic, "nested public"); + TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsPublic) > 0, type.IsPublic, "public"); + TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsSealed) > 0, type.IsSealed, "sealed"); + TypeAssert.CheckProperty(type, (typeProperties & TypeProperties.IsVisible) > 0, type.IsVisible, "visible"); + if (isAssignableFrom != null) + { + TypeAssert.CheckProperty(type, true, isAssignableFrom.IsAssignableFrom(type), String.Format("assignable from {0}", isAssignableFrom.FullName)); + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/ByteEnum.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/ByteEnum.cs new file mode 100644 index 0000000000..c1d4c7b89a --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/ByteEnum.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +namespace Microsoft.TestCommon.Types +{ + public enum ByteEnum : byte + { + FirstByte, + SecondByte, + ThirdByte + } +} diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/FlagsEnum.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/FlagsEnum.cs new file mode 100644 index 0000000000..d1b48a2fda --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/FlagsEnum.cs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.TestCommon.Types +{ + [Flags] + public enum FlagsEnum + { + One = 0x1, + Two = 0x2, + Four = 0x4 + } +} diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/INameAndIdContainer.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/INameAndIdContainer.cs new file mode 100644 index 0000000000..53d58b677e --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/INameAndIdContainer.cs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +namespace Microsoft.TestCommon.Types +{ + /// + /// Tagging interface to assist comparing instances of these types. + /// + public interface INameAndIdContainer + { + string Name { get; set; } + + int Id { get; set; } + } +} diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/ISerializableType.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/ISerializableType.cs new file mode 100644 index 0000000000..7fd7956b1d --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/ISerializableType.cs @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace Microsoft.TestCommon.Types +{ + [Serializable] + public class ISerializableType : ISerializable, INameAndIdContainer + { + private int id; + private string name; + + public ISerializableType() + { + } + + public ISerializableType(int id, string name) + { + this.id = id; + this.name = name; + } + + public ISerializableType(SerializationInfo information, StreamingContext context) + { + this.id = information.GetInt32("Id"); + this.name = information.GetString("Name"); + } + + public int Id + { + get + { + return this.id; + } + + set + { + this.IdSet = true; + this.id = value; + } + } + + public string Name + { + get + { + return this.name; + } + + set + { + this.NameSet = true; + this.name = value; + } + + } + + public bool IdSet { get; private set; } + + public bool NameSet { get; private set; } + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("Id", this.Id); + info.AddValue("Name", this.Name); + } + + public static IEnumerable GetTestData() + { + return new ISerializableType[] { new ISerializableType(), new ISerializableType(1, "SomeName") }; + } + } +} diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/LongEnum.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/LongEnum.cs new file mode 100644 index 0000000000..d27b40ac9a --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/LongEnum.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +namespace Microsoft.TestCommon.Types +{ + public enum LongEnum : long + { + FirstLong, + SecondLong, + ThirdLong, + FourthLong + } +} diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/SByteEnum.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/SByteEnum.cs new file mode 100644 index 0000000000..1afb32c1cb --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/SByteEnum.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +namespace Microsoft.TestCommon.Types +{ + public enum SByteEnum : sbyte + { + FirstSByte, + SecondSByte, + ThirdSByte + } +} diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/ShortEnum.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/ShortEnum.cs new file mode 100644 index 0000000000..d74576a73c --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/ShortEnum.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +namespace Microsoft.TestCommon.Types +{ + public enum ShortEnum : short + { + FirstShort, + SecondShort, + ThirdShort + } +} diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/SimpleEnum.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/SimpleEnum.cs new file mode 100644 index 0000000000..b740c8f207 --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/SimpleEnum.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +namespace Microsoft.TestCommon.Types +{ + public enum SimpleEnum + { + First, + Second, + Third, + Fourth + } +} diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/UIntEnum.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/UIntEnum.cs new file mode 100644 index 0000000000..8804f23096 --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/UIntEnum.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +namespace Microsoft.TestCommon.Types +{ + public enum UIntEnum : uint + { + FirstUInt, + SecondUInt, + ThirdUInt + } +} diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/UShortEnum.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/UShortEnum.cs new file mode 100644 index 0000000000..53be0b9e27 --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/Types/UShortEnum.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +namespace Microsoft.TestCommon.Types +{ + public enum UShortEnum : ushort + { + FirstUShort, + SecondUShort, + ThirdUShort + } +} diff --git a/test/Microsoft.TestCommon/Microsoft/TestCommon/XmlAssert.cs b/test/Microsoft.TestCommon/Microsoft/TestCommon/XmlAssert.cs new file mode 100644 index 0000000000..4608150b72 --- /dev/null +++ b/test/Microsoft.TestCommon/Microsoft/TestCommon/XmlAssert.cs @@ -0,0 +1,78 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Xml.Linq; + +namespace Microsoft.TestCommon +{ + /// + /// Assert class that compares two XML strings for equality. Namespaces are ignored during comparison + /// + public class XmlAssert + { + public void Equal(string expected, string actual, params RegexReplacement[] regexReplacements) + { + if (regexReplacements != null) + { + for (int i = 0; i < regexReplacements.Length; i++) + { + actual = regexReplacements[i].Regex.Replace(actual, regexReplacements[i].Replacement); + } + } + + Equal(XElement.Parse(expected), XElement.Parse(actual)); + } + + public void Equal(XElement expected, XElement actual) + { + Assert.Equal(Normalize(expected).ToString(), Normalize(actual).ToString()); + } + + private static XElement Normalize(XElement element) + { + if (element.HasElements) + { + return new XElement( + Encode(element.Name), + Normalize(element.Attributes()), + Normalize(element.Elements())); + } + + if (element.IsEmpty) + { + return new XElement( + Encode(element.Name), + Normalize(element.Attributes())); + } + else + { + return new XElement( + Encode(element.Name), + Normalize(element.Attributes()), + element.Value); + } + } + + private static IEnumerable Normalize(IEnumerable attributes) + { + return attributes + .Where((attrib) => !attrib.IsNamespaceDeclaration) + .Select((attrib) => new XAttribute(Encode(attrib.Name), attrib.Value)) + .OrderBy(a => a.Name.ToString()); + } + + private static IEnumerable Normalize(IEnumerable elements) + { + return elements + .Select(e => Normalize(e)) + .OrderBy(a => a.ToString()); + } + + private static string Encode(XName name) + { + return string.Format("{0}_{1}", HttpUtility.UrlEncode(name.NamespaceName).Replace('%', '_'), name.LocalName); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.TestCommon/Platform.cs b/test/Microsoft.TestCommon/Platform.cs new file mode 100644 index 0000000000..8ec9d6694b --- /dev/null +++ b/test/Microsoft.TestCommon/Platform.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.TestCommon +{ + /// + /// An enumeration of known platforms that the unit test might be running under. + /// + [Flags] + public enum Platform + { + /// + /// A special value used to indicate that the test is valid on all known platforms. + /// + All = 0xFFFFFF, + + /// + /// Indicates that the test wants to run on .NET 4 (when used with + /// and/or ), + /// or that the current platform that the test is running on is .NET 4 (when used with the + /// , , and/or + /// ). + /// + Net40 = 0x01, + + /// + /// Indicates that the test wants to run on .NET 4.5 (when used with + /// and/or ), + /// or that the current platform that the test is running on is .NET 4.5 (when used with the + /// , , and/or + /// ). + /// + Net45 = 0x02, + } +} diff --git a/test/Microsoft.TestCommon/PlatformInfo.cs b/test/Microsoft.TestCommon/PlatformInfo.cs new file mode 100644 index 0000000000..f65809d165 --- /dev/null +++ b/test/Microsoft.TestCommon/PlatformInfo.cs @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.TestCommon +{ + /// + /// Used to retrieve the currently running platform. + /// + public static class PlatformInfo + { + private const string _net45TypeName = "System.IWellKnownStringEqualityComparer, mscorlib, Version=4.0.0.0, PublicKeyToken=b77a5c561934e089"; + private static Lazy _platform = new Lazy(GetPlatform, isThreadSafe: true); + + /// + /// Gets the platform that the unit test is currently running on. + /// + public static Platform Platform + { + get { return _platform.Value; } + } + + private static Platform GetPlatform() + { + if (Type.GetType(_net45TypeName, throwOnError: false) != null) + { + return Platform.Net45; + } + + return Platform.Net40; + } + } +} diff --git a/test/Microsoft.TestCommon/PortReserver.cs b/test/Microsoft.TestCommon/PortReserver.cs new file mode 100644 index 0000000000..3cc9968af4 --- /dev/null +++ b/test/Microsoft.TestCommon/PortReserver.cs @@ -0,0 +1,135 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Net.NetworkInformation; +using System.Threading; + +namespace Microsoft.TestCommon +{ + /// + /// This class allocates ports while ensuring that: + /// 1. Ports that are permanentaly taken (or taken for the duration of the test) are not being attempted to be used. + /// 2. Ports are not shared across different tests (but you can allocate two different ports in the same test). + /// + /// Gotcha: If another application grabs a port during the test, we have a race condition. + /// + [DebuggerDisplay("Port: {PortNumber}, Port count for this app domain: {_appDomainOwnedPorts.Count}")] + public class PortReserver : IDisposable + { + private Mutex _portMutex; + + // We use this list to hold on to all the ports used because the Mutex will be blown through on the same thread. + // Theoretically we can do a thread local hashset, but that makes dispose thread dependant, or requires more complicated concurrency checks. + // Since practically there is no perf issue or concern here, this keeps the code the simplest possible. + private static HashSet _appDomainOwnedPorts = new HashSet(); + + public int PortNumber + { + get; + private set; + } + + public PortReserver(int basePort = 50231) + { + if (basePort <= 0) + { + throw new InvalidOperationException(); + } + + // Grab a cross appdomain/cross process/cross thread lock, to ensure only one port is reserved at a time. + using (Mutex mutex = GetGlobalMutex()) + { + try + { + int port = basePort - 1; + + while (true) + { + port++; + + if (port > 65535) + { + throw new InvalidOperationException("Exceeded port range"); + } + + // AppDomainOwnedPorts check enables reserving two ports from the same thread in sequence. + // ListUsedTCPPort prevents port contention with other apps. + if (_appDomainOwnedPorts.Contains(port) || + ListUsedTCPPort().Any(endPoint => endPoint.Port == port)) + { + continue; + } + + string mutexName = "WebStack-Port-" + port.ToString(CultureInfo.InvariantCulture); // Create a well known mutex + _portMutex = new Mutex(initiallyOwned: false, name: mutexName); + + // If no one else is using this port grab it. + if (_portMutex.WaitOne(millisecondsTimeout: 0)) + { + break; + } + + // dispose this mutex since the port it represents is not available. + _portMutex.Dispose(); + _portMutex = null; + } + + PortNumber = port; + _appDomainOwnedPorts.Add(port); + } + finally + { + mutex.ReleaseMutex(); + } + } + } + + public string BaseUri + { + get + { + return String.Format(CultureInfo.InvariantCulture, "http://localhost:{0}/", PortNumber); + } + } + + public void Dispose() + { + if (PortNumber == -1) + { + // Object already disposed + return; + } + + using (Mutex mutex = GetGlobalMutex()) + { + _portMutex.Dispose(); + _appDomainOwnedPorts.Remove(PortNumber); + PortNumber = -1; + } + } + + private static Mutex GetGlobalMutex() + { + Mutex mutex = new Mutex(initiallyOwned: false, name: "WebStack-RandomPortAcquisition"); + if (!mutex.WaitOne(30000)) + { + throw new InvalidOperationException(); + } + + return mutex; + } + + private static IPEndPoint[] ListUsedTCPPort() + { + var usedPort = new HashSet(); + IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties(); + + return ipGlobalProperties.GetActiveTcpListeners(); + } + } +} diff --git a/test/Microsoft.TestCommon/PreAppStartTestHelper.cs b/test/Microsoft.TestCommon/PreAppStartTestHelper.cs new file mode 100644 index 0000000000..47638d36f1 --- /dev/null +++ b/test/Microsoft.TestCommon/PreAppStartTestHelper.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.ComponentModel; +using System.Reflection; +using Microsoft.TestCommon; + +namespace System.Web.WebPages.TestUtils +{ + public static class PreAppStartTestHelper + { + public static void TestPreAppStartClass(Type preAppStartType) + { + string typeMessage = String.Format("The type '{0}' must be static, public, and named 'PreApplicationStartCode'.", preAppStartType.FullName); + Assert.True(preAppStartType.IsSealed && preAppStartType.IsAbstract && preAppStartType.IsPublic && preAppStartType.Name == "PreApplicationStartCode", typeMessage); + + string editorBrowsableMessage = String.Format("The only attribute on type '{0}' must be [EditorBrowsable(EditorBrowsableState.Never)].", preAppStartType.FullName); + object[] attrs = preAppStartType.GetCustomAttributes(typeof(EditorBrowsableAttribute), true); + Assert.True(attrs.Length == 1 && ((EditorBrowsableAttribute)attrs[0]).State == EditorBrowsableState.Never, editorBrowsableMessage); + + string startMethodMessage = String.Format("The only public member on type '{0}' must be a method called Start().", preAppStartType.FullName); + MemberInfo[] publicMembers = preAppStartType.GetMembers(BindingFlags.Public | BindingFlags.Static); + Assert.True(publicMembers.Length == 1, startMethodMessage); + Assert.True(publicMembers[0].MemberType == MemberTypes.Method, startMethodMessage); + Assert.True(publicMembers[0].Name == "Start", startMethodMessage); + } + } +} diff --git a/test/Microsoft.TestCommon/PreserveSyncContextAttribute.cs b/test/Microsoft.TestCommon/PreserveSyncContextAttribute.cs new file mode 100644 index 0000000000..1c1dd1d85f --- /dev/null +++ b/test/Microsoft.TestCommon/PreserveSyncContextAttribute.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.Threading; + +namespace Microsoft.TestCommon +{ + /// + /// Preserves the current . Use this attribute on + /// tests which modify the current . + /// + public class PreserveSyncContextAttribute : Xunit.BeforeAfterTestAttribute + { + private SynchronizationContext _syncContext; + + public override void Before(System.Reflection.MethodInfo methodUnderTest) + { + _syncContext = SynchronizationContext.Current; + } + + public override void After(System.Reflection.MethodInfo methodUnderTest) + { + SynchronizationContext.SetSynchronizationContext(_syncContext); + } + } +} diff --git a/test/Microsoft.TestCommon/PropertyDataAttribute.cs b/test/Microsoft.TestCommon/PropertyDataAttribute.cs new file mode 100644 index 0000000000..af1b5aa6de --- /dev/null +++ b/test/Microsoft.TestCommon/PropertyDataAttribute.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.TestCommon +{ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)] + public class PropertyDataAttribute : Xunit.Extensions.PropertyDataAttribute + { + public PropertyDataAttribute(string propertyName) + : base(propertyName) + { + } + } +} diff --git a/test/Microsoft.TestCommon/ReflectionAssert.cs b/test/Microsoft.TestCommon/ReflectionAssert.cs new file mode 100644 index 0000000000..74d0766a44 --- /dev/null +++ b/test/Microsoft.TestCommon/ReflectionAssert.cs @@ -0,0 +1,221 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Linq.Expressions; +using System.Reflection; + +namespace Microsoft.TestCommon +{ + public class ReflectionAssert + { + private static PropertyInfo GetPropertyInfo(Expression> property) + { + if (property.Body is MemberExpression) + { + return (PropertyInfo)((MemberExpression)property.Body).Member; + } + else if (property.Body is UnaryExpression && property.Body.NodeType == ExpressionType.Convert) + { + return (PropertyInfo)((MemberExpression)((UnaryExpression)property.Body).Operand).Member; + } + else + { + throw new InvalidOperationException("Could not determine property from lambda expression."); + } + } + + private static void TestPropertyValue(TInstance instance, Func getFunc, Action setFunc, TValue valueToSet, TValue valueToCheck) + { + setFunc(instance, valueToSet); + TValue newValue = getFunc(instance); + Assert.Equal(valueToCheck, newValue); + } + + private static void TestPropertyValue(TInstance instance, Func getFunc, Action setFunc, TValue value) + { + TestPropertyValue(instance, getFunc, setFunc, value, value); + } + + public void Property(T instance, Expression> propertyGetter, TResult expectedDefaultValue, bool allowNull = false, TResult roundTripTestValue = null) where TResult : class + { + PropertyInfo property = GetPropertyInfo(propertyGetter); + Func getFunc = (obj) => (TResult)property.GetValue(obj, index: null); + Action setFunc = (obj, value) => property.SetValue(obj, value, index: null); + + Assert.Equal(expectedDefaultValue, getFunc(instance)); + + if (allowNull) + { + TestPropertyValue(instance, getFunc, setFunc, null); + } + else + { + Assert.ThrowsArgumentNull(() => + { + setFunc(instance, null); + }, "value"); + } + + if (roundTripTestValue != null) + { + TestPropertyValue(instance, getFunc, setFunc, roundTripTestValue); + } + } + + public void IntegerProperty(T instance, Expression> propertyGetter, TResult expectedDefaultValue, + TResult? minLegalValue, TResult? illegalLowerValue, + TResult? maxLegalValue, TResult? illegalUpperValue, + TResult roundTripTestValue) where TResult : struct + { + PropertyInfo property = GetPropertyInfo(propertyGetter); + Func getFunc = (obj) => (TResult)property.GetValue(obj, index: null); + Action setFunc = (obj, value) => property.SetValue(obj, value, index: null); + + Assert.Equal(expectedDefaultValue, getFunc(instance)); + + if (minLegalValue.HasValue) + { + TestPropertyValue(instance, getFunc, setFunc, minLegalValue.Value); + } + + if (maxLegalValue.HasValue) + { + TestPropertyValue(instance, getFunc, setFunc, maxLegalValue.Value); + } + + if (illegalLowerValue.HasValue) + { + Assert.ThrowsArgumentGreaterThanOrEqualTo(() => { setFunc(instance, illegalLowerValue.Value); }, "value", minLegalValue.Value.ToString(), illegalLowerValue.Value); + } + + if (illegalUpperValue.HasValue) + { + Assert.ThrowsArgumentLessThanOrEqualTo(() => { setFunc(instance, illegalLowerValue.Value); }, "value", maxLegalValue.Value.ToString(), illegalUpperValue.Value); + } + + TestPropertyValue(instance, getFunc, setFunc, roundTripTestValue); + } + + public void NullableIntegerProperty(T instance, Expression> propertyGetter, TResult? expectedDefaultValue, + TResult? minLegalValue, TResult? illegalLowerValue, + TResult? maxLegalValue, TResult? illegalUpperValue, + TResult roundTripTestValue) where TResult : struct + { + PropertyInfo property = GetPropertyInfo(propertyGetter); + Func getFunc = (obj) => (TResult?)property.GetValue(obj, index: null); + Action setFunc = (obj, value) => property.SetValue(obj, value, index: null); + + Assert.Equal(expectedDefaultValue, getFunc(instance)); + + TestPropertyValue(instance, getFunc, setFunc, null); + + if (minLegalValue.HasValue) + { + TestPropertyValue(instance, getFunc, setFunc, minLegalValue.Value); + } + + if (maxLegalValue.HasValue) + { + TestPropertyValue(instance, getFunc, setFunc, maxLegalValue.Value); + } + + if (illegalLowerValue.HasValue) + { + Assert.ThrowsArgumentGreaterThanOrEqualTo(() => { setFunc(instance, illegalLowerValue.Value); }, "value", minLegalValue.Value.ToString(), illegalLowerValue.Value); + } + + if (illegalUpperValue.HasValue) + { + Assert.ThrowsArgumentLessThanOrEqualTo(() => { setFunc(instance, illegalLowerValue.Value); }, "value", maxLegalValue.Value.ToString(), illegalUpperValue.Value); + } + + TestPropertyValue(instance, getFunc, setFunc, roundTripTestValue); + } + + public void BooleanProperty(T instance, Expression> propertyGetter, bool expectedDefaultValue) + { + PropertyInfo property = GetPropertyInfo(propertyGetter); + Func getFunc = (obj) => (bool)property.GetValue(obj, index: null); + Action setFunc = (obj, value) => property.SetValue(obj, value, index: null); + + Assert.Equal(expectedDefaultValue, getFunc(instance)); + + TestPropertyValue(instance, getFunc, setFunc, !expectedDefaultValue); + } + + public void EnumProperty(T instance, Expression> propertyGetter, TResult expectedDefaultValue, TResult illegalValue, TResult roundTripTestValue) where TResult : struct + { + PropertyInfo property = GetPropertyInfo(propertyGetter); + Func getFunc = (obj) => (TResult)property.GetValue(obj, index: null); + Action setFunc = (obj, value) => property.SetValue(obj, value, index: null); + + Assert.Equal(expectedDefaultValue, getFunc(instance)); + + Assert.ThrowsInvalidEnumArgument(() => { setFunc(instance, illegalValue); }, "value", Convert.ToInt32(illegalValue), typeof(TResult)); + + TestPropertyValue(instance, getFunc, setFunc, roundTripTestValue); + } + + public void EnumPropertyWithoutIllegalValueCheck(T instance, Expression> propertyGetter, TResult expectedDefaultValue, TResult roundTripTestValue) where TResult : struct + { + PropertyInfo property = GetPropertyInfo(propertyGetter); + Func getFunc = (obj) => (TResult)property.GetValue(obj, index: null); + Action setFunc = (obj, value) => property.SetValue(obj, value, index: null); + + Assert.Equal(expectedDefaultValue, getFunc(instance)); + + TestPropertyValue(instance, getFunc, setFunc, roundTripTestValue); + } + + public void StringProperty(T instance, Expression> propertyGetter, string expectedDefaultValue, + bool allowNullAndEmpty = true, bool treatNullAsEmpty = true) + { + PropertyInfo property = GetPropertyInfo(propertyGetter); + Func getFunc = (obj) => (string)property.GetValue(obj, index: null); + Action setFunc = (obj, value) => property.SetValue(obj, value, index: null); + + Assert.Equal(expectedDefaultValue, getFunc(instance)); + + if (allowNullAndEmpty) + { + // Assert get/set works for null + TestPropertyValue(instance, getFunc, setFunc, null, treatNullAsEmpty ? String.Empty : null); + + // Assert get/set works for String.Empty + TestPropertyValue(instance, getFunc, setFunc, String.Empty, String.Empty); + } + else + { + Assert.ThrowsArgumentNullOrEmpty( + delegate() + { + try + { + TestPropertyValue(instance, getFunc, setFunc, null); + } + catch (TargetInvocationException e) + { + throw e.InnerException; + } + }, + "value"); + Assert.ThrowsArgumentNullOrEmpty( + delegate() + { + try + { + TestPropertyValue(instance, getFunc, setFunc, String.Empty); + } + catch (TargetInvocationException e) + { + throw e.InnerException; + } + }, + "value"); + } + + // Assert get/set works for arbitrary value + TestPropertyValue(instance, getFunc, setFunc, "TestValue"); + } + } +} diff --git a/test/Microsoft.TestCommon/ReplaceCultureAttribute.cs b/test/Microsoft.TestCommon/ReplaceCultureAttribute.cs new file mode 100644 index 0000000000..c149763601 --- /dev/null +++ b/test/Microsoft.TestCommon/ReplaceCultureAttribute.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Globalization; +using System.Reflection; +using System.Threading; + +namespace Microsoft.TestCommon +{ + /// + /// Replaces the current culture and UI culture for the test. + /// + [AttributeUsage(AttributeTargets.Method)] + public class ReplaceCultureAttribute : Xunit.BeforeAfterTestAttribute + { + private CultureInfo _originalCulture; + private CultureInfo _originalUICulture; + + public ReplaceCultureAttribute() + { + Culture = CultureReplacer.DefaultCultureName; + UICulture = CultureReplacer.DefaultUICultureName; + } + + /// + /// Sets for the test. Defaults to en-GB. + /// + /// + /// en-GB is used here as the default because en-US is equivalent to the InvariantCulture. We + /// want to be able to find bugs where we're accidentally relying on the Invariant instead of the + /// user's culture. + /// + public string Culture { get; set; } + + /// + /// Sets for the test. Defaults to en-US. + /// + public string UICulture { get; set; } + + public override void Before(MethodInfo methodUnderTest) + { + _originalCulture = Thread.CurrentThread.CurrentCulture; + _originalUICulture = Thread.CurrentThread.CurrentUICulture; + + Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(Culture); + Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(UICulture); + } + + public override void After(MethodInfo methodUnderTest) + { + Thread.CurrentThread.CurrentCulture = _originalCulture; + Thread.CurrentThread.CurrentUICulture = _originalUICulture; + } + } +} diff --git a/test/Microsoft.TestCommon/RestoreThreadPrincipalAttribute.cs b/test/Microsoft.TestCommon/RestoreThreadPrincipalAttribute.cs new file mode 100644 index 0000000000..1f08dc6fb3 --- /dev/null +++ b/test/Microsoft.TestCommon/RestoreThreadPrincipalAttribute.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Reflection; +using System.Security.Principal; +using System.Threading; + +namespace Microsoft.TestCommon +{ + public class RestoreThreadPrincipalAttribute : Xunit.BeforeAfterTestAttribute + { + private IPrincipal _originalPrincipal; + + public override void Before(MethodInfo methodUnderTest) + { + _originalPrincipal = Thread.CurrentPrincipal; + + AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.NoPrincipal); + + Thread.CurrentPrincipal = null; + } + + public override void After(MethodInfo methodUnderTest) + { + Thread.CurrentPrincipal = _originalPrincipal; + + AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.UnauthenticatedPrincipal); + } + } +} diff --git a/test/Microsoft.TestCommon/StringAssertException.cs b/test/Microsoft.TestCommon/StringAssertException.cs new file mode 100644 index 0000000000..441fc6fc65 --- /dev/null +++ b/test/Microsoft.TestCommon/StringAssertException.cs @@ -0,0 +1,117 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using Xunit.Sdk; + +namespace Microsoft.TestCommon +{ + // An early copy of the new string assert exception from xUnit.net 2.0 (temporarily, until it RTMs) + + [Serializable] + internal class StringEqualException : AssertException + { + private static Dictionary _encodings = new Dictionary { { '\r', "\\r" }, { '\n', "\\n" }, { '\t', "\\t" }, { '\0', "\\0" } }; + private string _message; + + public StringEqualException(string expected, string actual, int expectedIndex, int actualIndex) + : base("Assert.Equal() failure") + { + Actual = actual; + ActualIndex = actualIndex; + Expected = expected; + ExpectedIndex = expectedIndex; + } + + public string Actual { get; private set; } + + public int ActualIndex { get; private set; } + + public string Expected { get; private set; } + + public int ExpectedIndex { get; private set; } + + public override string Message + { + get + { + if (_message == null) + _message = CreateMessage(); + + return _message; + } + } + + private string CreateMessage() + { + Tuple printedExpected = ShortenAndEncode(Expected, ExpectedIndex, '↓'); + Tuple printedActual = ShortenAndEncode(Actual, ActualIndex, '↑'); + + return String.Format( + CultureInfo.CurrentCulture, + "{1}{0} {2}{0}Expected: {3}{0}Actual: {4}{0} {5}", + Environment.NewLine, + base.Message, + printedExpected.Item2, + printedExpected.Item1, + printedActual.Item1, + printedActual.Item2 + ); + } + + private Tuple ShortenAndEncode(string value, int position, char pointer) + { + int start = Math.Max(position - 20, 0); + int end = Math.Min(position + 41, value.Length); + StringBuilder printedValue = new StringBuilder(100); + StringBuilder printedPointer = new StringBuilder(100); + + if (start > 0) + { + printedValue.Append("···"); + printedPointer.Append(" "); + } + + for (int idx = start; idx < end; ++idx) + { + char c = value[idx]; + string encoding; + int paddingLength = 1; + + if (_encodings.TryGetValue(c, out encoding)) + { + printedValue.Append(encoding); + paddingLength = encoding.Length; + } + else + { + printedValue.Append(c); + } + + if (idx < position) + { + printedPointer.Append(' ', paddingLength); + } + else if (idx == position) + { + printedPointer.AppendFormat("{0} (pos {1})", pointer, position); + } + } + + if (end < value.Length) + { + printedValue.Append("···"); + } + + return new Tuple(printedValue.ToString(), printedPointer.ToString()); + } + + protected override bool ExcludeStackFrame(string stackFrame) + { + return base.ExcludeStackFrame(stackFrame) + || stackFrame.StartsWith("at Microsoft.TestCommon.Assert.", StringComparison.OrdinalIgnoreCase); + } + } +} diff --git a/test/Microsoft.TestCommon/StringAssertions.cs b/test/Microsoft.TestCommon/StringAssertions.cs new file mode 100644 index 0000000000..f84f7f647d --- /dev/null +++ b/test/Microsoft.TestCommon/StringAssertions.cs @@ -0,0 +1,135 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.TestCommon +{ + // An early copy of the new string assert from xUnit.net 2.0 (temporarily, until it RTMs) + + public partial class Assert + { + private const string NullDisplayValue = "(null)"; + + /// + /// Verifies that two strings are equivalent. + /// + /// The expected string value. + /// The actual string value. + /// If set to true, ignores cases differences. The invariant culture is used. + /// If set to true, treats \r\n, \r, and \n as equivalent. + /// If set to true, treats spaces and tabs (in any non-zero quantity) as equivalent. + /// Thrown when the strings are not equivalent. + public static void Equal(string expected, string actual, bool ignoreCase = false, bool ignoreLineEndingDifferences = false, bool ignoreWhiteSpaceDifferences = false) + { + // Start out assuming the one of the values is null + int expectedIndex = -1; + int actualIndex = -1; + int expectedLength = 0; + int actualLength = 0; + + if (expected == null) + { + if (actual == null) + { + return; + } + + expected = NullDisplayValue; + } + else if (actual == null) + { + actual = NullDisplayValue; + } + else + { + // Walk the string, keeping separate indices since we can skip variable amounts of + // data based on ignoreLineEndingDifferences and ignoreWhiteSpaceDifferences. + expectedIndex = 0; + actualIndex = 0; + expectedLength = expected.Length; + actualLength = actual.Length; + + while (expectedIndex < expectedLength && actualIndex < actualLength) + { + char expectedChar = expected[expectedIndex]; + char actualChar = actual[actualIndex]; + + if (ignoreLineEndingDifferences && IsLineEnding(expectedChar) && IsLineEnding(actualChar)) + { + expectedIndex = SkipLineEnding(expected, expectedIndex); + actualIndex = SkipLineEnding(actual, actualIndex); + } + else if (ignoreWhiteSpaceDifferences && IsWhiteSpace(expectedChar) && IsWhiteSpace(actualChar)) + { + expectedIndex = SkipWhitespace(expected, expectedIndex); + actualIndex = SkipWhitespace(actual, actualIndex); + } + else + { + if (ignoreCase) + { + expectedChar = Char.ToUpperInvariant(expectedChar); + actualChar = Char.ToUpperInvariant(actualChar); + } + + if (expectedChar != actualChar) + { + break; + } + + expectedIndex++; + actualIndex++; + } + } + } + + if (expectedIndex < expectedLength || actualIndex < actualLength) + { + throw new StringEqualException(expected, actual, expectedIndex, actualIndex); + } + } + + private static bool IsLineEnding(char c) + { + return c == '\r' || c == '\n'; + } + + private static bool IsWhiteSpace(char c) + { + return c == ' ' || c == '\t'; + } + + private static int SkipLineEnding(string value, int index) + { + if (value[index] == '\r') + { + ++index; + } + if (index < value.Length && value[index] == '\n') + { + ++index; + } + + return index; + } + + private static int SkipWhitespace(string value, int index) + { + while (index < value.Length) + { + switch (value[index]) + { + case ' ': + case '\t': + index++; + break; + + default: + return index; + } + } + + return index; + } + } +} diff --git a/test/Microsoft.TestCommon/TaskExtensions.cs b/test/Microsoft.TestCommon/TaskExtensions.cs new file mode 100644 index 0000000000..eeb441970d --- /dev/null +++ b/test/Microsoft.TestCommon/TaskExtensions.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Threading.Tasks; +using Microsoft.TestCommon; + +// No namespace so that these extensions are available for all test classes + +public static class TaskExtensions +{ + /// + /// Waits until the given task finishes executing and completes in any of the 3 states. + /// + /// A task + public static void WaitUntilCompleted(this Task task) + { + if (task == null) throw new ArgumentNullException("task"); + task.ContinueWith(prev => + { + if (prev.IsFaulted) + { + // Observe the exception in the faulted case to avoid an unobserved exception leaking and + // killing the thread finalizer. + var e = prev.Exception; + } + }, TaskContinuationOptions.ExecuteSynchronously).Wait(); + } + + public static void RethrowFaultedTaskException(this Task task) + { + task.WaitUntilCompleted(); + Assert.Equal(TaskStatus.Faulted, task.Status); + throw task.Exception.GetBaseException(); + } +} diff --git a/test/Microsoft.TestCommon/TestFile.cs b/test/Microsoft.TestCommon/TestFile.cs new file mode 100644 index 0000000000..a4b0a5fe90 --- /dev/null +++ b/test/Microsoft.TestCommon/TestFile.cs @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.IO; +using System.Reflection; +using Microsoft.TestCommon; + +namespace System.Web.WebPages.TestUtils +{ + public class TestFile + { + public const string ResourceNameFormat = "{0}.TestFiles.{1}"; + + public string ResourceName { get; set; } + public Assembly Assembly { get; set; } + + public TestFile(string resName, Assembly asm) + { + ResourceName = resName; + Assembly = asm; + } + + public static TestFile Create(string localResourceName) + { + return new TestFile(String.Format(ResourceNameFormat, Assembly.GetCallingAssembly().GetName().Name, localResourceName), Assembly.GetCallingAssembly()); + } + + public Stream OpenRead() + { + Stream strm = Assembly.GetManifestResourceStream(ResourceName); + if (strm == null) + { + Assert.True(false, String.Format("Manifest resource: {0} not found", ResourceName)); + } + return strm; + } + + public byte[] ReadAllBytes() + { + using (Stream stream = OpenRead()) + { + byte[] buffer = new byte[stream.Length]; + stream.Read(buffer, 0, buffer.Length); + return buffer; + } + } + + public string ReadAllText() + { + using (StreamReader reader = new StreamReader(OpenRead())) + { + // The .Replace() calls normalize line endings, in case you get \n instead of \r\n + // since all the unit tests rely on the assumption that the files will have \r\n endings. + return reader.ReadToEnd().Replace("\r", "").Replace("\n", "\r\n"); + } + } + + /// + /// Saves the file to the specified path. + /// + public void Save(string filePath) + { + var directory = Path.GetDirectoryName(filePath); + if (!Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } + + using (Stream outStream = File.Create(filePath)) + { + using (Stream inStream = OpenRead()) + { + inStream.CopyTo(outStream); + } + } + } + } +} diff --git a/test/Microsoft.TestCommon/TestHelper.cs b/test/Microsoft.TestCommon/TestHelper.cs new file mode 100644 index 0000000000..afe7c535e9 --- /dev/null +++ b/test/Microsoft.TestCommon/TestHelper.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.Globalization; +using System.Linq; +using Microsoft.TestCommon; + +namespace System.Web.TestUtil +{ + public static class UnitTestHelper + { + public static bool EnglishBuildAndOS + { + get + { + bool englishBuild = String.Equals(CultureInfo.CurrentCulture.TwoLetterISOLanguageName, "en", + StringComparison.OrdinalIgnoreCase); + bool englishOS = String.Equals(CultureInfo.CurrentCulture.TwoLetterISOLanguageName, "en", + StringComparison.OrdinalIgnoreCase); + return englishBuild && englishOS; + } + } + + public static void AssertEqualsIgnoreWhitespace(string expected, string actual) + { + expected = new String(expected.Where(c => !Char.IsWhiteSpace(c)).ToArray()); + actual = new String(actual.Where(c => !Char.IsWhiteSpace(c)).ToArray()); + + Assert.Equal(expected, actual, StringComparer.OrdinalIgnoreCase); + } + } +} diff --git a/test/Microsoft.TestCommon/TheoryAttribute.cs b/test/Microsoft.TestCommon/TheoryAttribute.cs new file mode 100644 index 0000000000..34558a3dfe --- /dev/null +++ b/test/Microsoft.TestCommon/TheoryAttribute.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Xunit.Sdk; + +namespace Microsoft.TestCommon +{ + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + public class TheoryAttribute : Xunit.Extensions.TheoryAttribute + { + public TheoryAttribute() + { + Timeout = Debugger.IsAttached ? Int32.MaxValue : TimeoutConstant.DefaultTimeout; + Platforms = Platform.All; + PlatformJustification = "Unsupported platform (test runs on {0}, current platform is {1})"; + } + + /// + /// Gets the platform that the unit test is currently running on. + /// + protected Platform Platform + { + get { return PlatformInfo.Platform; } + } + + /// + /// Gets or set the platforms that the unit test is compatible with. Defaults to + /// . + /// + public Platform Platforms { get; set; } + + /// + /// Gets or sets the platform skipping justification. This message can receive + /// the supported platforms as {0}, and the current platform as {1}. + /// + public string PlatformJustification { get; set; } + + /// + protected override IEnumerable EnumerateTestCommands(IMethodInfo method) + { + if ((Platforms & Platform) == 0) + { + return new[] { + new SkipCommand( + method, + DisplayName, + String.Format(PlatformJustification, Platforms.ToString().Replace(", ", " | "), Platform) + ) + }; + } + + return base.EnumerateTestCommands(method); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.TestCommon/TheoryDataSet.cs b/test/Microsoft.TestCommon/TheoryDataSet.cs new file mode 100644 index 0000000000..66c3ae2a3d --- /dev/null +++ b/test/Microsoft.TestCommon/TheoryDataSet.cs @@ -0,0 +1,105 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.Collections; +using System.Collections.Generic; + +namespace Microsoft.TestCommon +{ + /// + /// Helper class for generating test data for XUnit's -based tests. + /// Should be used in combination with . + /// + /// First parameter type + public class TheoryDataSet : TheoryDataSet + { + public void Add(TParam p) + { + AddItem(p); + } + } + + /// + /// Helper class for generating test data for XUnit's -based tests. + /// Should be used in combination with . + /// + /// First parameter type + /// Second parameter type + public class TheoryDataSet : TheoryDataSet + { + public void Add(TParam1 p1, TParam2 p2) + { + AddItem(p1, p2); + } + } + + /// + /// Helper class for generating test data for XUnit's -based tests. + /// Should be used in combination with . + /// + /// First parameter type + /// Second parameter type + /// Third parameter type + public class TheoryDataSet : TheoryDataSet + { + public void Add(TParam1 p1, TParam2 p2, TParam3 p3) + { + AddItem(p1, p2, p3); + } + } + + /// + /// Helper class for generating test data for XUnit's -based tests. + /// Should be used in combination with . + /// + /// First parameter type + /// Second parameter type + /// Third parameter type + /// Fourth parameter type + public class TheoryDataSet : TheoryDataSet + { + public void Add(TParam1 p1, TParam2 p2, TParam3 p3, TParam4 p4) + { + AddItem(p1, p2, p3, p4); + } + } + + /// + /// Helper class for generating test data for XUnit's -based tests. + /// Should be used in combination with . + /// + /// First parameter type + /// Second parameter type + /// Third parameter type + /// Fourth parameter type + /// Fifth parameter type + public class TheoryDataSet : TheoryDataSet + { + public void Add(TParam1 p1, TParam2 p2, TParam3 p3, TParam4 p4, TParam5 p5) + { + AddItem(p1, p2, p3, p4, p5); + } + } + + /// + /// Base class for TheoryDataSet classes. + /// + public abstract class TheoryDataSet : IEnumerable + { + private readonly List data = new List(); + + protected void AddItem(params object[] values) + { + data.Add(values); + } + + public IEnumerator GetEnumerator() + { + return data.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/test/Microsoft.TestCommon/ThreadPoolSyncContext.cs b/test/Microsoft.TestCommon/ThreadPoolSyncContext.cs new file mode 100644 index 0000000000..900526e4e4 --- /dev/null +++ b/test/Microsoft.TestCommon/ThreadPoolSyncContext.cs @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.Threading; + +namespace Microsoft.TestCommon +{ + /// + /// This is an implementation of SynchronizationContext that not only queues things on the thread pool for + /// later work, but also ensures that it sets itself back as the synchronization context (something that the + /// default implementatation of SynchronizationContext does not do). + /// + public class ThreadPoolSyncContext : SynchronizationContext + { + public override void Post(SendOrPostCallback d, object state) + { + ThreadPool.QueueUserWorkItem(_ => + { + SynchronizationContext oldContext = SynchronizationContext.Current; + SynchronizationContext.SetSynchronizationContext(this); + d.Invoke(state); + SynchronizationContext.SetSynchronizationContext(oldContext); + }, null); + } + + public override void Send(SendOrPostCallback d, object state) + { + SynchronizationContext oldContext = SynchronizationContext.Current; + SynchronizationContext.SetSynchronizationContext(this); + d.Invoke(state); + SynchronizationContext.SetSynchronizationContext(oldContext); + } + } +} diff --git a/test/Microsoft.TestCommon/TraitAttribute.cs b/test/Microsoft.TestCommon/TraitAttribute.cs new file mode 100644 index 0000000000..19965e62f9 --- /dev/null +++ b/test/Microsoft.TestCommon/TraitAttribute.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.TestCommon +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] + public class TraitAttribute : Xunit.TraitAttribute + { + public TraitAttribute(string name, string value) + : base(name, value) + { + } + } +} diff --git a/test/Microsoft.TestCommon/VersionTestHelper.cs b/test/Microsoft.TestCommon/VersionTestHelper.cs new file mode 100644 index 0000000000..2b4257b5bc --- /dev/null +++ b/test/Microsoft.TestCommon/VersionTestHelper.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Reflection; + +namespace Microsoft.TestCommon +{ + public static class VersionTestHelper + { + // returns a version for an assembly by using a type from the assembly + // also verifies that type wasn't moved to another assembly. + public static Version GetVersionFromAssembly(string assemblyName, Type typeFromAssembly) + { + Assembly assembly = typeFromAssembly.Assembly; + + Assert.Equal(assemblyName, assembly.GetName().Name); + return assembly.GetName().Version; + } + } +} diff --git a/test/Microsoft.TestCommon/WebUtils.cs b/test/Microsoft.TestCommon/WebUtils.cs new file mode 100644 index 0000000000..640dcaa952 --- /dev/null +++ b/test/Microsoft.TestCommon/WebUtils.cs @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. + +using System.IO; +using System.Reflection; +using System.Web.UI; + +namespace System.Web.WebPages.TestUtils +{ + public static class WebUtils + { + /// + /// Creates an instance of HttpRuntime and assigns it (using magic) to the singleton instance of HttpRuntime. + /// Ensure that the returned value is disposed at the end of the test. + /// + /// Returns an IDisposable that restores the original HttpRuntime. + public static IDisposable CreateHttpRuntime(string appVPath, string appPath = null) + { + var runtime = new HttpRuntime(); + var appDomainAppVPathField = typeof(HttpRuntime).GetField("_appDomainAppVPath", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); + appDomainAppVPathField.SetValue(runtime, CreateVirtualPath(appVPath)); + + if (appPath != null) + { + var appDomainAppPathField = typeof(HttpRuntime).GetField("_appDomainAppPath", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance); + appDomainAppPathField.SetValue(runtime, Path.GetFullPath(appPath)); + } + + GetTheRuntime().SetValue(null, runtime); + var appDomainIdField = typeof(HttpRuntime).GetField("_appDomainId", BindingFlags.NonPublic | BindingFlags.Instance); + appDomainIdField.SetValue(runtime, "test"); + + return new DisposableAction(RestoreHttpRuntime); + } + + internal static FieldInfo GetTheRuntime() + { + return typeof(HttpRuntime).GetField("_theRuntime", BindingFlags.NonPublic | BindingFlags.Static); + } + + internal static void RestoreHttpRuntime() + { + GetTheRuntime().SetValue(null, null); + } + + internal static object CreateVirtualPath(string path) + { + var vPath = typeof(Page).Assembly.GetType("System.Web.VirtualPath"); + var method = vPath.GetMethod("CreateNonRelativeTrailingSlash", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); + return method.Invoke(null, new object[] { path }); + } + + private class DisposableAction : IDisposable + { + private Action _action; + private bool _hasDisposed; + + public DisposableAction(Action action) + { + if (action == null) + { + throw new ArgumentNullException("action"); + } + _action = action; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + // If we were disposed by the finalizer it's because the user didn't use a "using" block, so don't do anything! + if (disposing) + { + lock (this) + { + if (!_hasDisposed) + { + _hasDisposed = true; + _action(); + } + } + } + } + } + } +} diff --git a/test/Microsoft.TestCommon/packages.config b/test/Microsoft.TestCommon/packages.config new file mode 100644 index 0000000000..9d80a7fba3 --- /dev/null +++ b/test/Microsoft.TestCommon/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file