Commits

Matt Oswald  committed 577ba7f

profiling showed that a good portion of startup was spent managing the attribute multimap, so switched to using vector

  • Participants
  • Parent commits dbf193f

Comments (0)

Files changed (16)

File Tests/UnitTests/Attributes.cpp

     {
         if (test->TestDetails().Name == "TestWithAttributes")
         {
-            auto it = test->TestDetails().Attributes.find("Cats");
+            auto it = std::find_if(test->TestDetails().Attributes.begin(), test->TestDetails().Attributes.end(), [](const std::pair<std::string, std::string> &item) { return item.first == "Cats"; });
             Assert.True(it != test->TestDetails().Attributes.end());
             Assert.True(it->second == "Meow");
             return;

File Tests/UnitTests/TestRunner.cpp

 
     operator std::shared_ptr<xUnitTest>()
     {
-        return std::make_shared<xUnitTest>(std::move(testFn), std::move(name), std::move(name), suite, attributes, timeLimit, std::move(file), line, testEventRecorders);
+        return std::make_shared<xUnitTest>(std::move(testFn), std::move(name), std::move(name), suite, std::move(attributes), timeLimit, std::move(file), line, testEventRecorders);
     }
 
 private:

File Tests/UnitTests/Theory.cpp

     {
         if (test->TestDetails().ShortName == "TheoriesCanHaveAttributes")
         {
-            auto it = test->TestDetails().Attributes.find("Cats");
+            auto it = std::find_if(test->TestDetails().Attributes.begin(), test->TestDetails().Attributes.end(), [](const std::pair<std::string, std::string> &item) { return item.first == "Cats"; });
             Assert.True(it != test->TestDetails().Attributes.end());
             Assert.True(it->second == "Meow");
             return;

File xUnit++.console/main.cpp

                 {
                     bool included = false;
 
-                    for (auto att = td.Attributes.begin(); !included && att != td.Attributes.end(); ++att)
+                    for (auto test = options.inclusiveAttributes.begin(); !included && test != options.inclusiveAttributes.end(); ++test)
                     {
-                        auto it = options.inclusiveAttributes.equal_range(att->first);
+                        auto range = td.Attributes.find(*test);
 
-                        for (auto test = it.first; test != it.second; ++test)
+                        if (range.first != range.second)
                         {
-                            if (test->second == "" || test->second == att->second)
+                            if (test->second == "")
                             {
                                 included = true;
                                 break;
                             }
+
+                            for (auto it = range.first; it != range.second; ++it)
+                            {
+                                if (range.first->second == test->second)
+                                {
+                                    included = true;
+                                    break;
+                                }
+                            }
                         }
                     }
 
                 // if a test has *all* matching keys, it is excluded
                 if (!options.exclusiveAttributes.empty())
                 {
-                    for (auto test = options.exclusiveAttributes.begin(); test != options.exclusiveAttributes.end(); ++test)
+                    bool matchFailed = false;
+                    for (auto test = options.exclusiveAttributes.begin(); !matchFailed && test != options.exclusiveAttributes.end(); ++test)
                     {
-                        auto range = td.Attributes.equal_range(test->first);
+                        auto range = td.Attributes.find(*test);
 
                         if (range.first != range.second)
                         {
-                            if (test->second == "")
+                            // key matched, and we want to exclude all tests with a specific value
+                            if (test->second != "")
                             {
-                                return;
-                            }
+                                auto it = range.first;
+                                for (; it != range.second; ++it)
+                                {
+                                    if (it->second == test->second)
+                                    {
+                                        break;
+                                    }
+                                }
 
-                            for (auto att = range.first; att != range.second; ++att)
-                            {
-                                if (att->second == test->second)
+                                if (it == range.second)
                                 {
-                                    return;
+                                    matchFailed = true;
                                 }
                             }
                         }
                     }
+
+                    if (!matchFailed)
+                    {
+                        return;
+                    }
                 }
 
                 onList(td);

File xUnit++/src/Attributes.cpp

+#include "Attributes.h"
+#include <algorithm>
+
+namespace xUnitAttributes
+{
+
+xUnitpp::AttributeCollection Attributes()
+{
+    return xUnitpp::AttributeCollection();
+}
+
+}
+
+namespace xUnitpp
+{
+
+AttributeCollection::AttributeCollection()
+    : skipped(std::make_pair(false, ""))
+{
+}
+
+AttributeCollection::AttributeCollection(const AttributeCollection &other)
+    : sortedAttributes(other.sortedAttributes)
+    , skipped(other.skipped)
+{
+}
+
+AttributeCollection::AttributeCollection(AttributeCollection &&other)
+    : sortedAttributes(std::move(other.sortedAttributes))
+    , skipped(std::move(other.skipped))
+{
+}
+
+AttributeCollection &AttributeCollection::operator =(AttributeCollection other)
+{
+    swap(*this, other);
+    return *this;
+}
+
+void swap(AttributeCollection &a, AttributeCollection &b)
+{
+    using std::swap;
+    swap(a.sortedAttributes, b.sortedAttributes);
+    swap(a.skipped, b.skipped);
+}
+
+void AttributeCollection::insert(Attribute &&a)
+{
+    sortedAttributes.push_back(a);
+
+    if (a.first == "Skip")
+    {
+        skipped.first = true;
+        skipped.second = a.second;
+    }
+}
+
+const std::pair<bool, std::string> &AttributeCollection::Skipped() const
+{
+    return skipped;
+}
+
+bool AttributeCollection::empty() const
+{
+    return sortedAttributes.empty();
+}
+
+AttributeCollection::const_iterator AttributeCollection::begin() const
+{
+    return sortedAttributes.begin();
+}
+
+AttributeCollection::const_iterator AttributeCollection::end() const
+{
+    return sortedAttributes.end();
+}
+
+void AttributeCollection::sort()
+{
+    std::sort(sortedAttributes.begin(), sortedAttributes.end());
+}
+
+AttributeCollection::iterator_range AttributeCollection::find(const Attribute &att) const
+{
+    return std::equal_range(begin(), end(), att, [](const Attribute &a0, const Attribute &a1) { return a0.first < a1.first; });
+}
+
+}

File xUnit++/src/TestDetails.cpp

 {
 }
 
-TestDetails::TestDetails(std::string &&name, std::string &&shortName, const std::string &suite, const AttributeCollection &attributes,
+TestDetails::TestDetails(std::string &&name, std::string &&shortName, const std::string &suite, AttributeCollection &&attributes,
                          Time::Duration timeLimit, std::string &&filename, int line)
     : Id(NextId())
     , Name(std::move(name))

File xUnit++/src/xUnitTest.cpp

 {
 
 xUnitTest::xUnitTest(std::function<void()> &&test, std::string &&name, std::string &&shortName, const std::string &suite,
-                     const AttributeCollection &attributes, Time::Duration timeLimit,
+                     AttributeCollection &&attributes, Time::Duration timeLimit,
                      std::string &&filename, int line, const std::vector<std::shared_ptr<TestEventRecorder>> &testEventRecorders)
     : test(std::move(test))
-    , testDetails(std::move(name), std::move(shortName), suite, attributes, timeLimit, std::move(filename), line)
+    , testDetails(std::move(name), std::move(shortName), suite, std::move(attributes), timeLimit, std::move(filename), line)
     , testEventRecorders(testEventRecorders)
     , failureEventLogged(false)
 {

File xUnit++/src/xUnitTestRunner.cpp

     for (auto &test : activeTests)
     {
         {
-            auto skip = test->TestDetails().Attributes.find("Skip");
-            if (skip != test->TestDetails().Attributes.end())
+            auto skip = test->TestDetails().Attributes.Skipped();
+            if (skip.first)
             {
                 skippedTests++;
-                sharedOutput.ReportSkip(test->TestDetails(), skip->second);
+                sharedOutput.ReportSkip(test->TestDetails(), skip.second);
                 continue;
             }
         }

File xUnit++/xUnit++.vcxproj

     <ClCompile Include="src/xUnitCheck.cpp" />
     <ClCompile Include="src/xUnitLog.cpp" />
     <ClCompile Include="src/xUnitWarn.cpp" />
+    <ClCompile Include="src\Attributes.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="xUnit++/Attributes.h" />

File xUnit++/xUnit++.vcxproj.filters

     <ClCompile Include="src/xUnitWarn.cpp" />
     <ClCompile Include="src/TestEventRecorder.cpp" />
     <ClCompile Include="src/xUnitLog.cpp" />
+    <ClCompile Include="src\Attributes.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="xUnit++/xUnitAssert.h" />

File xUnit++/xUnit++/Attributes.h

 #ifndef ATTRIBUTES_H_
 #define ATTRIBUTES_H_
 
-#include <map>
 #include <string>
-#include "TestDetails.h"
+#include <utility>
+#include <vector>
+
+namespace xUnitpp { class AttributeCollection; }
 
 namespace xUnitAttributes
 {
-    inline xUnitpp::AttributeCollection Attributes()
-    {
-        return xUnitpp::AttributeCollection();
-    }
+    xUnitpp::AttributeCollection Attributes();
+}
+
+namespace xUnitpp
+{
+
+class AttributeCollection
+{
+public:
+    typedef std::pair<std::string, std::string> Attribute;
+    typedef std::vector<Attribute>::const_iterator const_iterator;
+    typedef std::pair<const_iterator, const_iterator> iterator_range;
+
+    AttributeCollection();
+    AttributeCollection(const AttributeCollection &other);
+    AttributeCollection(AttributeCollection &&other);
+    AttributeCollection &operator =(AttributeCollection other);
+    friend void swap(AttributeCollection &a, AttributeCollection &b);
+
+    // !!!VS My kingdom for initialization lists!
+    void insert(Attribute &&a);
+
+    bool empty() const;
+
+    const_iterator begin() const;
+    const_iterator end() const;
+
+    void sort();
+
+    iterator_range find(const Attribute &key) const;
+
+    const std::pair<bool, std::string> &Skipped() const;
+
+private:
+    std::vector<Attribute> sortedAttributes;
+
+    // shortcut to searching
+    std::pair<bool, std::string> skipped;
+};
 
 }
 

File xUnit++/xUnit++/TestCollection.h

                 auto theoryName = GetLongTheoryName(name + " [" + ToString(id++) + "] (", SplitParams(std::move(params)), std::forward<decltype(t)>(t));
 
                 collection.mTests.push_back(std::make_shared<xUnitTest>(TheoryHelper(std::forward<TTheory>(theory), std::move(t)), std::move(theoryName), std::move(name), suite,
-                    attributes, Time::ToDuration(Time::ToMilliseconds(milliseconds)), std::move(filename), line, testEventRecorders));
+                    AttributeCollection(attributes), Time::ToDuration(Time::ToMilliseconds(milliseconds)), std::move(filename), line, testEventRecorders));
             }
         }
     };

File xUnit++/xUnit++/TestDetails.h

 #include <string>
 #include <tuple>
 #include <vector>
+#include "Attributes.h"
 #include "LineInfo.h"
 #include "xUnitTime.h"
 
 {
 
 class xUnitAssert;
-typedef std::multimap<std::string, std::string> AttributeCollection;
 
 struct TestDetails
 {
     TestDetails();
     TestDetails(std::string &&name, std::string &&shortName, const std::string &suite,
-        const AttributeCollection &attributes, Time::Duration timeLimit,
+        AttributeCollection &&attributes, Time::Duration timeLimit,
         std::string &&filename, int line);
     TestDetails(const TestDetails &other);
     TestDetails(TestDetails &&other);

File xUnit++/xUnit++/xUnitMacroHelpers.h

 
 #define _xPP_NARGS_IMPL(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,N,...) N
 
-#define FIRST_ARG(x, ...) x
+#define CAR(x, ...) x
+#define CDR(x, ...) __VA_ARGS__
 
 // attribute expansion helpers
 #define XU_A_1(a) \

File xUnit++/xUnit++/xUnitMacros.h

 #include "xUnitMacroHelpers.h"
 #include "xUnitWarn.h"
 
+// !!!VS initializer lists can make this faster...
 #define ATTRIBUTES(...) \
     namespace XU_UNIQUE_ATT_NS { \
         namespace xUnitAttributes { \
             { \
                 xUnitpp::AttributeCollection attributes; \
                 XU_ATTRIBUTES(__VA_ARGS__) \
+                attributes.sort(); \
                 return attributes; \
             } \
         } \
         const xUnitpp::Warn &Warn = *detail::pWarn; \
         const xUnitpp::Log &Log = *detail::pLog; \
         void XU_UNIQUE_TEST params; \
-        decltype(FIRST_ARG(__VA_ARGS__)) args[] = { __VA_ARGS__ }; \
+        decltype(CAR(__VA_ARGS__)) args[] = { __VA_ARGS__ }; \
         xUnitpp::TestCollection::Register reg(xUnitpp::TestCollection::Instance(), \
             &XU_UNIQUE_TEST, xUnitpp::TheoryData(PP_NARGS(__VA_ARGS__), args), std::string(TheoryDetails), \
             xUnitSuite::Name(), std::string(#params), xUnitAttributes::Attributes(), timeout, std::string(__FILE__), __LINE__, eventRecorders); \

File xUnit++/xUnit++/xUnitTest.h

 {
 public:
     xUnitTest(std::function<void()> &&test, std::string &&name, std::string &&shortName,
-        const std::string &suite, const AttributeCollection &attributes, Time::Duration timeLimit,
+        const std::string &suite, AttributeCollection &&attributes, Time::Duration timeLimit,
         std::string &&filename, int line, const std::vector<std::shared_ptr<TestEventRecorder>> &testEventRecorders);
 
     const xUnitpp::TestDetails &TestDetails() const;