From: Juan Hoyos Date: Thu, 14 Nov 2019 19:40:42 +0000 (-0800) Subject: Add new function options to conditions in test runner (#611) X-Git-Tag: submit/tizen/20200402.013218~14^2^2~11 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=850fa77cf99f8bc53c94a741cb4b45c18c78a836;p=platform%2Fcore%2Fdotnet%2Fdiagnostics.git Add new function options to conditions in test runner (#611) Adds the following options to the test helper condition attributes: * Contains(string, string) * StartsWith(string, string) * EndsWith(string, string) And adds argument validation that throws exception - to prevent any more tests to be skipped silently. --- diff --git a/src/Microsoft.Diagnostics.TestHelpers/TestConfiguration.cs b/src/Microsoft.Diagnostics.TestHelpers/TestConfiguration.cs index d1dfbba4f..30c287694 100644 --- a/src/Microsoft.Diagnostics.TestHelpers/TestConfiguration.cs +++ b/src/Microsoft.Diagnostics.TestHelpers/TestConfiguration.cs @@ -171,51 +171,146 @@ namespace Microsoft.Diagnostics.TestHelpers return templates; } + // Currently we only support single function clauses as follows + // Exists('') + // StartsWith('', '') + // EndsWith('', '') + // Contains('', '') + // '' == '' + // '' != '' + // strings support variable embedding with $(). e.g Exists('$(PropsFile)') bool EvaluateConditional(Dictionary config, XElement node) { + void ValidateAndResolveParameters(string funcName, int expectedParamCount, List paramList) + { + if (paramList.Count != expectedParamCount) + { + throw new InvalidDataException($"Expected {expectedParamCount} arguments for {funcName} in condition"); + } + + for (int i = 0; i < paramList.Count; i++) + { + paramList[i] = ResolveProperties(config, paramList[i]); + } + } + foreach (XAttribute attr in node.Attributes("Condition")) { string conditionText = attr.Value; + bool isNegative = conditionText.Length > 0 && conditionText[0] == '!'; // Check if Exists('') - const string existsKeyword = "Exists('"; - int existsStartIndex = conditionText.IndexOf(existsKeyword); - if (existsStartIndex != -1) + const string existsKeyword = "Exists"; + if (TryGetParametersForFunction(conditionText, existsKeyword, out List paramList)) + { + ValidateAndResolveParameters(existsKeyword, 1, paramList); + bool exists = Directory.Exists(paramList[0]) || File.Exists(paramList[0]); + return isNegative ? !exists : exists; + } + + // Check if StartsWith('string', 'prefix') + const string startsWithKeyword = "StartsWith"; + if (TryGetParametersForFunction(conditionText, startsWithKeyword, out paramList)) { - bool not = (existsStartIndex > 0) && (conditionText[existsStartIndex - 1] == '!'); + ValidateAndResolveParameters(startsWithKeyword, 2, paramList); + bool isPrefix = paramList[0].StartsWith(paramList[1]); + return isNegative ? !isPrefix : isPrefix; + } - existsStartIndex += existsKeyword.Length; - int existsEndIndex = conditionText.IndexOf("')", existsStartIndex); - Assert.NotEqual(-1, existsEndIndex); + // Check if EndsWith('string', 'postfix') + const string endsWithKeyword = "EndsWith"; + if (TryGetParametersForFunction(conditionText, endsWithKeyword, out paramList)) + { + ValidateAndResolveParameters(endsWithKeyword, 2, paramList); + bool isPostfix = paramList[0].EndsWith(paramList[1]); + return isNegative ? !isPostfix : isPostfix; + } - string path = conditionText.Substring(existsStartIndex, existsEndIndex - existsStartIndex); - path = Path.GetFullPath(ResolveProperties(config, path)); - bool exists = Directory.Exists(path) || File.Exists(path); - return not ? !exists : exists; + // Check if Contains('string', 'substring') + const string containsKeyword = "Contains"; + if (TryGetParametersForFunction(conditionText, containsKeyword, out paramList)) + { + ValidateAndResolveParameters(containsKeyword, 2, paramList); + bool isInString = paramList[0].Contains(paramList[1]); + return isNegative ? !isInString : isInString; } - else + + // Check if equals and not equals + bool isEquals = conditionText.Contains("=="); + bool isDifferent = conditionText.Contains("!="); + + if (isEquals == isDifferent) { - // Check if equals and not equals - string[] parts = conditionText.Split("=="); - bool equal; + throw new InvalidDataException($"Unknown condition type in {conditionText}. See TestRunConfiguration.EvaluateConditional for supported values."); + } + + string[] parts = isEquals ? conditionText.Split("==") : conditionText.Split("!="); + + // Resolve any config values in the condition + string leftValue = ResolveProperties(config, parts[0]).Trim(); + string rightValue = ResolveProperties(config, parts[1]).Trim(); + + // Now do the simple string comparison of the left/right sides of the condition + return isEquals ? leftValue == rightValue : leftValue != rightValue; + } + return true; + } + + private bool TryGetParametersForFunction(string expression, string targetFunctionName, out List exprParams) + { + int functionKeyworkIndex = expression.IndexOf($"{targetFunctionName}("); + if (functionKeyworkIndex == -1) { + exprParams = null; + return false; + } - if (parts.Length == 2) + if (functionKeyworkIndex != 0 || functionKeyworkIndex >= 1 && expression[0] != '!' || !expression.EndsWith(')')) + { + throw new InvalidDataException($"Condition {expression} malformed. Currently only single-function conditions are supported."); + } + + exprParams = new List(); + bool isWithinString = false; + bool expectDelimiter = false; + int curParsingIndex = functionKeyworkIndex + targetFunctionName.Length + 1; + StringBuilder resolvedValue = new StringBuilder(); + + // Account for the trailing parenthesis. + while(curParsingIndex + 1 < expression.Length) + { + char currentChar = expression[curParsingIndex]; + // toggle string nesting on ', except if scaped + if (currentChar == '\'' && !(curParsingIndex > 0 && expression[curParsingIndex - 1] == '\\')) + { + if (isWithinString) { - equal = true; + exprParams.Add(resolvedValue.ToString()); + expectDelimiter = true; } - else + + isWithinString = !isWithinString; + } + else if (isWithinString) + { + resolvedValue.Append(currentChar); + } + else if (currentChar == ',') + { + if (!expectDelimiter) { - parts = conditionText.Split("!="); - Assert.Equal(2, parts.Length); - equal = false; + throw new InvalidDataException($"Unexpected comma found within {expression}"); } - // Resolve any config values in the condition - string leftValue = ResolveProperties(config, parts[0]).Trim(); - string rightValue = ResolveProperties(config, parts[1]).Trim(); - - // Now do the simple string comparison of the left/right sides of the condition - return equal ? leftValue == rightValue : leftValue != rightValue; + expectDelimiter = false; + } + else if (!Char.IsWhiteSpace(currentChar)) + { + throw new InvalidDataException($"Non whitespace, non comma value found outside of string within: {expression}"); } + curParsingIndex++; + } + + if (isWithinString) { + throw new InvalidDataException($"Non-terminated string detected within {expression}"); } return true; }