blob: 0e6b09fd77345cf2593d05e78b9509d798e64585 [file] [log] [blame]
/* Copyright 2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $Revision: 88625 $
*/
#include "teamcity_messages.h"
#include <cstdlib>
#include <iostream>
#include <sstream>
namespace jetbrains { namespace teamcity { namespace {
/// Use RAII to write message open/close markers
class RaiiMessage
{
public:
RaiiMessage(const char* const name, std::ostream& out)
: m_out(out)
{
// endl for http://jetbrains.net/tracker/issue/TW-4412
m_out << std::endl << "##teamcity[" << name;
}
~RaiiMessage()
{
// endl for http://jetbrains.net/tracker/issue/TW-4412
m_out << "]" << std::endl;
}
/// \todo Copying char-by-char is ineffective!
std::string escape(const std::string &s)
{
std::string result;
result.reserve(s.length());
for (size_t i = 0; i < s.length(); i++)
{
char c = s[i];
switch (c)
{
case '\n': result.append("|n"); break;
case '\r': result.append("|r"); break;
case '\'': result.append("|'"); break;
case '|': result.append("||"); break;
case ']': result.append("|]"); break;
default: result.append(&c, 1);
}
}
return result;
}
void writeProperty(const std::string& name, const std::string& value)
{
m_out << ' ' << name << "='" << escape(value) << '\'';
}
void writePropertyIfNonEmpty(const char* const name, const std::string value)
{
if (!value.empty())
writeProperty(name, value);
}
private:
std::ostream& m_out;
};
} // anonymous namespace
//BEGIN Public helper functions
std::string getFlowIdFromEnvironment()
{
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__) && !defined(__MINGW32__)
char* flowId = 0;
size_t sz = 0;
std::string result;
if (!_dupenv_s(&flowId, &sz, "TEAMCITY_PROCESS_FLOW_ID"))
{
result = flowId != 0 ? flowId : std::string();
free(flowId);
}
return result;
#else
const char *flowId = getenv("TEAMCITY_PROCESS_FLOW_ID");
return flowId == 0 ? std::string() : flowId;
#endif
}
bool underTeamcity()
{
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__) && !defined(__MINGW32__)
char* teamCityProjectName = 0;
size_t sz = 0;
bool result = false;
if (!_dupenv_s(&teamCityProjectName, &sz, "TEAMCITY_PROJECT_NAME"))
{
result = teamCityProjectName != 0;
free(teamCityProjectName);
}
return result;
#else
return getenv("TEAMCITY_PROJECT_NAME") != 0;
#endif
}
//END Public helper functions
//BEGIN TeamcityMessages members
TeamcityMessages::TeamcityMessages()
: m_out(&std::cout)
{}
void TeamcityMessages::setOutput(std::ostream& out)
{
m_out = &out;
}
void TeamcityMessages::suiteStarted(const std::string& name, const std::string& flowId)
{
RaiiMessage msg("testSuiteStarted", *m_out);
msg.writeProperty("name", name);
msg.writePropertyIfNonEmpty("flowId", flowId);
}
void TeamcityMessages::suiteFinished(const std::string& name, const std::string& flowId)
{
RaiiMessage msg("testSuiteFinished", *m_out);
msg.writeProperty("name", name);
msg.writePropertyIfNonEmpty("flowId", flowId);
}
void TeamcityMessages::testStarted(const std::string& name, const std::string& flowId, const bool captureStandardOutput)
{
RaiiMessage msg("testStarted", *m_out);
msg.writeProperty("name", name);
msg.writePropertyIfNonEmpty("flowId", flowId);
if (captureStandardOutput)
msg.writeProperty("captureStandardOutput", "true"); // false by default
}
void TeamcityMessages::testFinished(const std::string& name, const int durationMs, const std::string& flowId)
{
RaiiMessage msg("testFinished", *m_out);
msg.writeProperty("name", name);
msg.writePropertyIfNonEmpty("flowId", flowId);
if (durationMs >= 0)
{
/// \bug W/ some locales it is possible to get a number w/ a number separator(s)!
/// \todo Make a test for that!
std::stringstream out(std::ios_base::out);
out << durationMs;
msg.writeProperty("duration", out.str());
}
}
void TeamcityMessages::testFailed(const std::string& name, const std::string& message, const std::string& details, const std::string& flowId)
{
RaiiMessage msg("testFailed", *m_out);
msg.writeProperty("name", name);
msg.writeProperty("message", message);
msg.writeProperty("details", details);
msg.writePropertyIfNonEmpty("flowId", flowId);
}
void TeamcityMessages::testIgnored(const std::string& name, const std::string& message, const std::string& flowId)
{
RaiiMessage msg("testIgnored", *m_out);
msg.writeProperty("name", name);
msg.writeProperty("message", message);
msg.writePropertyIfNonEmpty("flowId", flowId);
}
void TeamcityMessages::testOutput(const std::string& name, const std::string& output, const std::string& flowId, const bool isStdError)
{
RaiiMessage msg(isStdError ? "testStdErr" : "testStdOut", *m_out);
msg.writeProperty("name", name);
msg.writeProperty("out", output);
msg.writePropertyIfNonEmpty("flowId", flowId);
}
//END TeamcityMessages members
}} // namespace teamcity, jetbrains