// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System.Web.Razor.Parser; using System.Web.Razor.Parser.SyntaxTree; using System.Web.Razor.Resources; using System.Web.Razor.Test.Framework; using System.Web.Razor.Text; using Xunit; namespace System.Web.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 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(@"{ List photos = gallery.Photo.ToList(); }", 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) { // Foo

A real tag!

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

A real tag!

\r\n") .Accepts(AcceptedCharacters.None)), Factory.Code("}").AsStatement() )); } } }