1 /*
2  *  Created by Phil Nash on 1/2/2013.
3  *  Copyright 2013 Two Blue Cubes Ltd. All rights reserved.
4  *
5  *  Distributed under the Boost Software License, Version 1.0. (See accompanying
6  *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7  */
8 
9 #include "catch_message.h"
10 #include "catch_interfaces_capture.h"
11 #include "catch_uncaught_exceptions.h"
12 #include "catch_enforce.h"
13 
14 #include <cassert>
15 #include <stack>
16 
17 namespace Catch {
18 
MessageInfo(StringRef const & _macroName,SourceLineInfo const & _lineInfo,ResultWas::OfType _type)19     MessageInfo::MessageInfo(   StringRef const& _macroName,
20                                 SourceLineInfo const& _lineInfo,
21                                 ResultWas::OfType _type )
22     :   macroName( _macroName ),
23         lineInfo( _lineInfo ),
24         type( _type ),
25         sequence( ++globalCount )
26     {}
27 
operator ==(MessageInfo const & other) const28     bool MessageInfo::operator==( MessageInfo const& other ) const {
29         return sequence == other.sequence;
30     }
31 
operator <(MessageInfo const & other) const32     bool MessageInfo::operator<( MessageInfo const& other ) const {
33         return sequence < other.sequence;
34     }
35 
36     // This may need protecting if threading support is added
37     unsigned int MessageInfo::globalCount = 0;
38 
39 
40     ////////////////////////////////////////////////////////////////////////////
41 
MessageBuilder(StringRef const & macroName,SourceLineInfo const & lineInfo,ResultWas::OfType type)42     Catch::MessageBuilder::MessageBuilder( StringRef const& macroName,
43                                            SourceLineInfo const& lineInfo,
44                                            ResultWas::OfType type )
45         :m_info(macroName, lineInfo, type) {}
46 
47     ////////////////////////////////////////////////////////////////////////////
48 
49 
ScopedMessage(MessageBuilder const & builder)50     ScopedMessage::ScopedMessage( MessageBuilder const& builder )
51     : m_info( builder.m_info ), m_moved()
52     {
53         m_info.message = builder.m_stream.str();
54         getResultCapture().pushScopedMessage( m_info );
55     }
56 
ScopedMessage(ScopedMessage && old)57     ScopedMessage::ScopedMessage( ScopedMessage&& old )
58     : m_info( old.m_info ), m_moved()
59     {
60         old.m_moved = true;
61     }
62 
~ScopedMessage()63     ScopedMessage::~ScopedMessage() {
64         if ( !uncaught_exceptions() && !m_moved ){
65             getResultCapture().popScopedMessage(m_info);
66         }
67     }
68 
69 
Capturer(StringRef macroName,SourceLineInfo const & lineInfo,ResultWas::OfType resultType,StringRef names)70     Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ) {
71         auto trimmed = [&] (size_t start, size_t end) {
72             while (names[start] == ',' || isspace(names[start])) {
73                 ++start;
74             }
75             while (names[end] == ',' || isspace(names[end])) {
76                 --end;
77             }
78             return names.substr(start, end - start + 1);
79         };
80         auto skipq = [&] (size_t start, char quote) {
81             for (auto i = start + 1; i < names.size() ; ++i) {
82                 if (names[i] == quote)
83                     return i;
84                 if (names[i] == '\\')
85                     ++i;
86             }
87             CATCH_INTERNAL_ERROR("CAPTURE parsing encountered unmatched quote");
88         };
89 
90         size_t start = 0;
91         std::stack<char> openings;
92         for (size_t pos = 0; pos < names.size(); ++pos) {
93             char c = names[pos];
94             switch (c) {
95             case '[':
96             case '{':
97             case '(':
98             // It is basically impossible to disambiguate between
99             // comparison and start of template args in this context
100 //            case '<':
101                 openings.push(c);
102                 break;
103             case ']':
104             case '}':
105             case ')':
106 //           case '>':
107                 openings.pop();
108                 break;
109             case '"':
110             case '\'':
111                 pos = skipq(pos, c);
112                 break;
113             case ',':
114                 if (start != pos && openings.size() == 0) {
115                     m_messages.emplace_back(macroName, lineInfo, resultType);
116                     m_messages.back().message = static_cast<std::string>(trimmed(start, pos));
117                     m_messages.back().message += " := ";
118                     start = pos;
119                 }
120             }
121         }
122         assert(openings.size() == 0 && "Mismatched openings");
123         m_messages.emplace_back(macroName, lineInfo, resultType);
124         m_messages.back().message = static_cast<std::string>(trimmed(start, names.size() - 1));
125         m_messages.back().message += " := ";
126     }
~Capturer()127     Capturer::~Capturer() {
128         if ( !uncaught_exceptions() ){
129             assert( m_captured == m_messages.size() );
130             for( size_t i = 0; i < m_captured; ++i  )
131                 m_resultCapture.popScopedMessage( m_messages[i] );
132         }
133     }
134 
captureValue(size_t index,std::string const & value)135     void Capturer::captureValue( size_t index, std::string const& value ) {
136         assert( index < m_messages.size() );
137         m_messages[index].message += value;
138         m_resultCapture.pushScopedMessage( m_messages[index] );
139         m_captured++;
140     }
141 
142 } // end namespace Catch
143