/// <summary>
        /// Copy constructor
        /// </summary>
        /// <param name="previousDefine">Defines instance to be copied</param>
        public Defines(Defines previousDefine)
        {
            if (previousDefine == null) throw new ArgumentNullException("previousDefine");

            SubstitutionTokens = new Dictionary<string, object>(previousDefine.SubstitutionTokens);
            NonSubstitutionTokens = new HashSet<string>(previousDefine.NonSubstitutionTokens);
        }
        public void ConditionalInclusionsComplexExpressionEvaluationFail()
        {
            #region setup

            var definesHandler = new Defines(); //no defines supplied

            var filter = new ConditionalInclusionsFilter(new ExpressionEvaluation());

            const string nameSpace = "BoostTestAdapterNunit.Resources.SourceFiltering.";
            const string unfilteredSourceCodeResourceName = "ConditionalInclusionsComplexEvaluationFail.cpp";

            string sourceCodeOriginal = TestHelper.ReadEmbeddedResource(nameSpace + unfilteredSourceCodeResourceName);
            string sourceCodeExpected = sourceCodeOriginal;

            var cppSourceFile = new CppSourceFile()
            {
                FileName = nameSpace + unfilteredSourceCodeResourceName,
                SourceCode = sourceCodeOriginal
            };

            #endregion

            #region excercise

            filter.Filter(cppSourceFile, definesHandler);

            #endregion

            #region verify

            Assert.AreEqual(sourceCodeExpected, cppSourceFile.SourceCode); //no filtering should be done due to inability to evaluate an expression

            #endregion
        }
        /// <summary>
        /// Given 2 strings, filters the left-hand string and checks whether the filtered output matches that of the right-hand string.
        /// </summary>
        /// <param name="filter">The source filter to apply</param>
        /// <param name="defines">The preprocessor definitions which are to be used by the source filter</param>
        /// <param name="lhs">The left-hand string whose value will be filtered</param>
        /// <param name="rhs">The right-hand string whose value is used to compare the filtered result</param>
        protected void FilterAndCompare(ISourceFilter filter, Defines defines, string lhs, string rhs)
        {
            var cppSourceFile = new CppSourceFile(){SourceCode = lhs};
            filter.Filter(cppSourceFile, defines);

            Assert.AreEqual(cppSourceFile.SourceCode, rhs);
        }
 private void ApplySourceFilter(CppSourceFile cppSourceFile, Defines definesHandler)
 {
     foreach( var filter in _sourceFilters )
     {
         filter.Filter(cppSourceFile, definesHandler);
     }
 }
        /// <summary>
        /// Applies the quoted strings filter action on the supplied sourceCode string
        /// </summary>
        /// <param name="cppSourceFile">CppSourceFile object containing the source file information</param>
        /// <param name="definesHandler">not used for this filter</param>
        public void Filter(CppSourceFile cppSourceFile, Defines definesHandler)
        {
            /*
             * It is important not to the change order of the filters.
             */
            cppSourceFile.SourceCode = stringLiteralsRegex.Replace(cppSourceFile.SourceCode, new MatchEvaluator(ComputeReplacement));

            cppSourceFile.SourceCode = quotedStringsRegex.Replace(cppSourceFile.SourceCode, "");
        }
 /// <summary>
 /// Given 2 embedded resources locations, filters the left-hand resource and checks whether the filtered output matches that of the right-hand resource.
 /// </summary>
 /// <param name="filter">The source filter to apply</param>
 /// <param name="defines">The preprocessor definitions which are to be used by the source filter</param>
 /// <param name="lhsEmbeddedResource">The left-hand embedded resource fully qualified location whose content will be filtered</param>
 /// <param name="rhsEmbeddedResource">The right-hand embedded resource fully qualified location whose content is used to compare the filtered result</param>
 protected void FilterAndCompareResources(ISourceFilter filter, Defines defines, string lhsEmbeddedResource, string rhsEmbeddedResource)
 {
     FilterAndCompare(
         filter,
         defines,
         TestHelper.ReadEmbeddedResource(lhsEmbeddedResource),
         TestHelper.ReadEmbeddedResource(rhsEmbeddedResource)
     );
 }
        /// <summary>
        /// Given a parameter list of strings, generates a Defines structure
        /// where pairs of strings are treated as a definition and its value.
        /// </summary>
        /// <param name="definitions">The string pair definitions array from which to generate the Defines structue</param>
        /// <returns>A Defines structure built out of string pairs available in the definitions array</returns>
        private Defines GenerateDefines(string[] definitions)
        {
            Assert.That(definitions.Length % 2, Is.EqualTo(0));

            Defines definesHandler = new Defines();

            for (int i = 1; i < definitions.Length; i += 2)
            {
                definesHandler.Define(definitions[i - 1], definitions[i]);
            }

            return definesHandler;
        }
        public void ConditionalInclusionsIfTests()
        {
            #region setup
            
            var definesHandler = new Defines();

            definesHandler.Define("DEBUG", "");
            definesHandler.Define("NDEBUG", "");
            definesHandler.Define("DEBUGGER", "");

            var expectedPreprocessorDefines = new HashSet<string>()
            {
                "DEBUG",
                "NDEBUG",
                "DEBUGGER",
                "DLEVEL"
            };

            var filter = new ConditionalInclusionsFilter( new ExpressionEvaluation() );

            const string nameSpace = "BoostTestAdapterNunit.Resources.SourceFiltering.";
            const string unfilteredSourceCodeResourceName = "ConditionalInclusionsIfTests_UnFilteredSourceCode.cpp";
            const string filteredSourceCodeResourceName = "ConditionalInclusionsIfTests_FilteredSourceCode.cpp";

            string sourceCodeOriginal = TestHelper.ReadEmbeddedResource(nameSpace + unfilteredSourceCodeResourceName);
            string sourceCodeExpected = TestHelper.ReadEmbeddedResource(nameSpace + filteredSourceCodeResourceName);

            var cppSourceFile = new CppSourceFile()
            {
                FileName = nameSpace + unfilteredSourceCodeResourceName,
                SourceCode = sourceCodeOriginal
            };

            #endregion

            #region excercise

            filter.Filter(cppSourceFile, definesHandler);

            #endregion

            #region verify

            Assert.AreEqual(sourceCodeExpected, cppSourceFile.SourceCode);
            Assert.AreEqual(expectedPreprocessorDefines, definesHandler.NonSubstitutionTokens);
            Assert.AreEqual(0, definesHandler.SubstitutionTokens.Count);

            #endregion

        }
        /// <summary>
        /// Evaluates an expression
        /// </summary>
        /// <param name="expression">expression to be evaluated</param>
        /// <param name="definesHandler">reference to the defines handler </param>
        /// <returns></returns>
        public EvaluationResult EvaluateExpression(string expression, Defines definesHandler)
        {
            this._definesHandler = definesHandler;

            Expression e = new Expression(expression, EvaluateOptions.NoCache);
            e.EvaluateParameter += EvaluateParam;
            e.EvaluateFunction += EvaluateFunction;

            EvaluationResult evaluationResult = EvaluationResult.UnDetermined;

            try
            {
                object result = e.Evaluate();

                evaluationResult = Convert.ToBoolean(result, CultureInfo.InvariantCulture) ? EvaluationResult.IsTrue : EvaluationResult.IsFalse;
            }
            catch
            {
                evaluationResult = EvaluationResult.UnDetermined;
            }

            return evaluationResult;
        }
        // (/\*(?:.+?)\*/)
        //
        // Options: Case insensitive; Exact spacing; Dot matches line breaks; ^$ don't match at line breaks; Numbered capture
        //
        // Match the regex below and capture its match into backreference number 1 «(/\*(?:.+?)\*/)»
        //    Match the character “/” literally «/»
        //    Match the character “*” literally «\*»
        //    Match the regular expression below «(?:.+?)»
        //       Match any single character «.+?»
        //          Between one and unlimited times, as few times as possible, expanding as needed (lazy) «+?»
        //    Match the character “*” literally «\*»
        //    Match the character “/” literally «/»

        #region ISourceFilter

        /// <summary>
        /// Filters any multiline comments from the source code.
        /// </summary>
        /// <param name="cppSourceFile">CppSourceFile object containing the source file information</param>
        /// <param name="definesHandler">not used for this filter</param>
        public void Filter(CppSourceFile cppSourceFile, Defines definesHandler)
        {
            Utility.Code.Require(cppSourceFile, "cppSourceFile");
            cppSourceFile.SourceCode = multiLineCommentRegex.Replace(cppSourceFile.SourceCode, ComputeMultiLineCommentReplacement);
        }
        private void GetBoostTests(IDictionary<string, ProjectInfo> solutionInfo, ITestCaseDiscoverySink discoverySink)
        {
            if (solutionInfo != null)
            {
                foreach (KeyValuePair<string, ProjectInfo> info in solutionInfo)
                {
                    string source = info.Key;
                    ProjectInfo projectInfo = info.Value;

                    foreach (var sourceFile in projectInfo.CppSourceFiles)
                    {
                        try
                        {
                            using (var sr = new StreamReader(sourceFile))
                            {
                                try
                                {
                                    var cppSourceFile = new CppSourceFile()
                                    {
                                        FileName = sourceFile,
                                        SourceCode = sr.ReadToEnd()
                                    };

                                    // Filter out any false positives and quickly reject files which do not contain any Boost Unit Test eye-catchers
                                    if ( ShouldConsiderSourceFile(cppSourceFile.SourceCode) )
                                    {
                                        /*
                                         * it is important that the pre-processor defines at project level are not modified
                                         * because every source file in the project has to have the same starting point.
                                         */

                                        //call to cpy ctor
                                        Defines definitions = new Defines(projectInfo.DefinesHandler);

                                        ApplySourceFilter(cppSourceFile, definitions);
                                        DiscoverBoostTests(cppSourceFile, source, discoverySink);
                                    }
                                }
                                catch (Exception ex)
                                {
                                    Logger.Error(
                                            "Exception raised while discovering tests from \"{0}\" of project \"{1}\", ({2})",
                                            sourceFile, projectInfo.ProjectExe, ex.Message);
                                    Logger.Error(ex.StackTrace);
                                }
                            }
                        }
                        catch
                        {
                            Logger.Error("Unable to open file \"{0}\" of project \"{1}\".", sourceFile, projectInfo.ProjectExe);
                        }
                    }
                }
            }
            else
            {
                Logger.Error("the solutionInfo object was found to be null whilst");
            }
        }
        /// <summary>
        /// Filters any single line comments from the source code
        /// </summary>
        /// <param name="cppSourceFile">CppSourceFile object containing the source file information</param>
        /// <param name="definesHandler">not used for this filter</param>
        public void Filter(CppSourceFile cppSourceFile, Defines definesHandler)
        {
            Utility.Code.Require(cppSourceFile, "cppSourceFile");

            cppSourceFile.SourceCode = singleLineCommentRegex.Replace(cppSourceFile.SourceCode, "");
        }
 /// <summary>
 /// Identifies the project's configured preprocessor definitions
 /// </summary>
 /// <param name="definitions">The preprocessor definitions in use by this project</param>
 /// <returns>this</returns>
 public FakeProjectBuilder Defines(Defines definitions)
 {
     this._definitions = definitions;
     return this;
 }
 /// <summary>
 /// Filters any multiline comments from the source code.
 /// </summary>
 /// <param name="cppSourceFile">CppSourceFile object containing the source file information</param>
 /// <param name="definesHandler">not used for this filter</param>
 public void Filter(CppSourceFile cppSourceFile, Defines definesHandler)
 {
     cppSourceFile.SourceCode = multiLineCommentRegex.Replace(cppSourceFile.SourceCode, ComputeMultiLineCommentReplacement);
 }
        //Options: Case insensitive; Exact spacing; Dot matches line breaks; ^$ don't match at line breaks; Numbered capture
        //
        //Match the regex below and capture its match into backreference number 1 «(\r\n?|\n)»
        //   Match this alternative (attempting the next alternative only if this one fails) «\r\n?»
        //      Match the carriage return character «\r»
        //      Match the line feed character «\n?»
        //         Between zero and one times, as many times as possible, giving back as needed (greedy) «?»
        //   Or match this alternative (the entire group fails if this one fails to match) «\n»
        //      Match the line feed character «\n»

        #region ISourceFilter

        /// <summary>
        /// Applies the quoted strings filter action on the supplied sourceCode string
        /// </summary>
        /// <param name="cppSourceFile">CppSourceFile object containing the source file information</param>
        /// <param name="definesHandler">not used for this filter</param>
        public void Filter(CppSourceFile cppSourceFile, Defines definesHandler)
        {
            Utility.Code.Require(cppSourceFile, "cppSourceFile");

            cppSourceFile.SourceCode = stringLiteralsRegex.Replace(cppSourceFile.SourceCode, ComputeReplacement);
        }
        public void Apply(CppSourceFile cppSourceFile, Defines definesHandler)
        {
            Utility.Code.Require(cppSourceFile, "cppSourceFile");

            DefinesHandler = definesHandler;

            string[] sourceLines = LinefeedRegex.Split(cppSourceFile.SourceCode);

            _parserState.Clear();
            AddState(ParserState.NormalState);  //initial state

            SourceLineType sourceLineType;
            string expression;
            string subtitutionText;
            int lineNumber = 0;

            try
            {
                for (; lineNumber < sourceLines.Length; lineNumber++)
                {
                    Inspect(sourceLines[lineNumber], out sourceLineType, out expression, out subtitutionText);

                    _conditionalInclusionsMachineState.Process(ref sourceLines[lineNumber], sourceLineType, expression,
                        subtitutionText);
                }

                /*
                 * Once the parsing is complete we just check that the parserState is back to Normal State.
                 * If not it is either because we parsed bad code (i.e. the code structure was not consistent to start with)
                 * or we've got a problem (programmatically) with our state engine
                 */

                //sanity check
                if (_parserState.Peek() != ParserState.NormalState)
                {
                    Logger.Error("The conditionals filter state machine failed to return to normal state. The source file \"{0}\" will not be filtered for conditionals.", cppSourceFile.FileName);

                    //the source code for the specific file is left unfiltered in case the state machine did not return to a normal state.
                }
                else
                {
                    //State machine returned to normal state so we can apply the filtering done by the conditional inclusions machine
                    cppSourceFile.SourceCode = string.Join(Environment.NewLine, sourceLines);
                }
            }
            catch (Exception ex)
            {
                Logger.Error(
                        "The conditionals filter encountered error: {0} whilst processing line number {1} of source file \"{2}\". The source file will not be filtered for conditionals",
                        ex.Message, lineNumber + 1, cppSourceFile.FileName);
            }
        }
        public void ConditionalInclusionsComplexExpressionEvaluationSuccess()
        {
            #region setup

            var definesHandler = new Defines();

            var expectedNonSubstitutionTokens = new HashSet<string>()
            {
                "VERSION",
                "HALF",
                "THIRD",
                "DEBUG",
                "SIN",
                "MAX",
                "CUBE",
                "fPRINT",
                "ASSERT",
            };

            var expectedSubtitutionTokens = new Dictionary<string, object>()
            {
                {"LEVEL", "19"},
                {"EVER", ";;"},
                {"BIG", "(512)"},
                {"PRINT", "cout << #x"},
            };

            var filter = new ConditionalInclusionsFilter(new ExpressionEvaluation());

            const string nameSpace = "BoostTestAdapterNunit.Resources.SourceFiltering.";
            const string unfilteredSourceCodeResourceName = "ConditionalInclusionsComplexEvaluationSuccess_UnfilteredSourceCode.cpp";
            const string filteredSourceCodeResourceName = "ConditionalInclusionsComplexEvaluationSuccess_FilteredSourceCode.cpp";

            string sourceCodeOriginal = TestHelper.ReadEmbeddedResource(nameSpace + unfilteredSourceCodeResourceName);

            string sourceCodeExpected = TestHelper.ReadEmbeddedResource(nameSpace + filteredSourceCodeResourceName);

            var cppSourceFile = new CppSourceFile()
            {
                FileName = nameSpace + unfilteredSourceCodeResourceName,
                SourceCode = sourceCodeOriginal
            };

            #endregion

            #region excercise

            filter.Filter(cppSourceFile, definesHandler);

            #endregion

            #region verify

            Assert.AreEqual(sourceCodeExpected, cppSourceFile.SourceCode);
            Assert.AreEqual(expectedNonSubstitutionTokens, definesHandler.NonSubstitutionTokens);
            Assert.AreEqual(expectedSubtitutionTokens, definesHandler.SubstitutionTokens);

            #endregion
        }
 /// <summary>
 /// Filters any single line comments from the source code
 /// </summary>
 /// <param name="cppSourceFile">CppSourceFile object containing the source file information</param>
 /// <param name="definesHandler">not used for this filter</param>
 public void Filter(CppSourceFile cppSourceFile, Defines definesHandler)
 {
     cppSourceFile.SourceCode = singleLineCommentRegex.Replace(cppSourceFile.SourceCode, "");
 }
 /// <summary>
 /// Applies the filter action onto the source code
 /// </summary>
 /// <param name="cppSourceFile">source file information</param>
 /// <param name="definesHandler">pre-processor defines</param>
 public void Filter(CppSourceFile cppSourceFile, Defines definesHandler)
 {
     _conditionalInclusionsMachine.Apply(cppSourceFile, definesHandler);
 }
        /// <summary>
        /// Acquires the pre-processor definitions used by the project
        /// </summary>
        /// <param name="projectInfo">The Visual Studio project</param>
        /// <returns>The pre-processor definitions specified within the project</returns>
        private static Defines GetDefines(ProjectInfo projectInfo)
        {
            /*
             * it is important that the pre-processor defines at project level are not modified
             * because every source file in the project has to have the same starting point.
             */

            //call to cpy ctor
            Defines defines = new Defines(projectInfo.DefinesHandler);

            // Additional Boost definitions

            // NOTE These definitions are generally assumed to be available since
            //      we are operating in a BOOST Test context
            if (!defines.IsDefined("BOOST_VERSION"))
            {
                defines.Define("BOOST_VERSION", "0");
            }

            return defines;
        }