gtest: Add plugin for TeamCity integration
Automatically report unit tests to the TeamCity server if run within TeamCity. See tests/gtest/teamcity/README.cef for details. To test: Set the TEAMCITY_PROJECT_NAME environment variable and run ceftests.
This commit is contained in:
parent
50f627b07e
commit
7efac13ac1
25
BUILD.gn
25
BUILD.gn
|
@ -1654,6 +1654,29 @@ if (is_mac) {
|
|||
# Executable/app targets.
|
||||
#
|
||||
|
||||
# Source files for TeamCity GTest integration.
|
||||
# See tests/gtest/teamcity/README.cef for details.
|
||||
source_set("gtest_teamcity") {
|
||||
testonly = true
|
||||
|
||||
sources = [
|
||||
"tests/gtest/teamcity/include/teamcity_gtest.h",
|
||||
"tests/gtest/teamcity/src/teamcity_gtest.cpp",
|
||||
"tests/gtest/teamcity/src/teamcity_gtest.h",
|
||||
"tests/gtest/teamcity/src/teamcity_messages.cpp",
|
||||
"tests/gtest/teamcity/src/teamcity_messages.h",
|
||||
]
|
||||
|
||||
deps = [
|
||||
"//testing/gtest",
|
||||
]
|
||||
|
||||
configs += [
|
||||
"libcef/features:config",
|
||||
"//build/config:precompiled_headers",
|
||||
]
|
||||
}
|
||||
|
||||
if (is_mac) {
|
||||
# Helper for generating the CEF app bundle.
|
||||
template("cef_app") {
|
||||
|
@ -2001,6 +2024,7 @@ if (is_mac) {
|
|||
":ceftests_resources_bundle_data",
|
||||
":ceftests_resources_bundle_data_english",
|
||||
":ceftests_xibs",
|
||||
":gtest_teamcity",
|
||||
"//testing/gtest",
|
||||
]
|
||||
frameworks = [
|
||||
|
@ -2219,6 +2243,7 @@ if (is_mac) {
|
|||
deps = [
|
||||
":libcef",
|
||||
":libcef_dll_wrapper",
|
||||
":gtest_teamcity",
|
||||
"//testing/gtest",
|
||||
]
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "include/cef_file_util.h"
|
||||
#include "include/wrapper/cef_scoped_temp_dir.h"
|
||||
#include "tests/gtest/include/gtest/gtest.h"
|
||||
#include "tests/gtest/teamcity/include/teamcity_gtest.h"
|
||||
#include "tests/shared/common/client_switches.h"
|
||||
|
||||
namespace {
|
||||
|
@ -121,6 +122,12 @@ void CefTestSuite::InitMainProcess() {
|
|||
|
||||
// This will modify |argc_| and |argv_|.
|
||||
testing::InitGoogleTest(&argc_, argv_.array());
|
||||
|
||||
if (jetbrains::teamcity::underTeamcity()) {
|
||||
auto& listeners = ::testing::UnitTest::GetInstance()->listeners();
|
||||
listeners.Append(
|
||||
new jetbrains::teamcity::TeamcityGoogleTestEventListener());
|
||||
}
|
||||
}
|
||||
|
||||
// Don't add additional code to this method. Instead add it to Initialize().
|
||||
|
|
|
@ -6,6 +6,10 @@ set(CEF_TARGET "cef_gtest")
|
|||
|
||||
set(GTEST_SRCS
|
||||
src/gtest-all.cc
|
||||
teamcity/include/teamcity_gtest.h
|
||||
teamcity/src/teamcity_gtest.cpp
|
||||
teamcity/src/teamcity_messages.cpp
|
||||
teamcity/src/teamcity_messages.h
|
||||
)
|
||||
source_group(cef_gtest FILES ${GTEST_SRCS})
|
||||
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
Name: TeamCity C++ Unit Test Reporting
|
||||
Short Name: teamcity-cpp
|
||||
URL: https://github.com/JetBrains/teamcity-cpp
|
||||
Version: 1.8 (commit b2c95c5)
|
||||
License: Apache 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
||||
|
||||
Description:
|
||||
GTest integration for reporting to TeamCity Continuous Integration Server.
|
||||
|
||||
See the project URL for related documentation.
|
||||
|
||||
Local Modifications:
|
||||
1. Copy necessary C++ files only.
|
||||
1. Use absolute include paths.
|
|
@ -0,0 +1,54 @@
|
|||
/* Copyright 2015 Paul Shmakov
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef H_TEAMCITY_GTEST
|
||||
#define H_TEAMCITY_GTEST
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "tests/gtest/include/gtest/gtest.h"
|
||||
#include "tests/gtest/teamcity/src/teamcity_messages.h"
|
||||
|
||||
namespace jetbrains {
|
||||
namespace teamcity {
|
||||
|
||||
class TeamcityGoogleTestEventListener: public ::testing::EmptyTestEventListener {
|
||||
public:
|
||||
TeamcityGoogleTestEventListener(const std::string& flowid);
|
||||
TeamcityGoogleTestEventListener();
|
||||
|
||||
// Fired before the test case starts.
|
||||
virtual void OnTestCaseStart(const ::testing::TestCase& test_case);
|
||||
// Fired before the test starts.
|
||||
virtual void OnTestStart(const ::testing::TestInfo& test_info);
|
||||
// Fired after the test ends.
|
||||
virtual void OnTestEnd(const ::testing::TestInfo& test_info);
|
||||
// Fired after the test case ends.
|
||||
virtual void OnTestCaseEnd(const ::testing::TestCase& test_case);
|
||||
|
||||
private:
|
||||
TeamcityMessages messages;
|
||||
std::string flowid;
|
||||
|
||||
// Prevent copying.
|
||||
TeamcityGoogleTestEventListener(const TeamcityGoogleTestEventListener&);
|
||||
void operator =(const TeamcityGoogleTestEventListener&);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* H_TEAMCITY_GTEST */
|
|
@ -0,0 +1,86 @@
|
|||
/* Copyright 2015 Paul Shmakov
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tests/gtest/teamcity/include/teamcity_gtest.h"
|
||||
|
||||
namespace jetbrains {
|
||||
namespace teamcity {
|
||||
|
||||
using namespace testing;
|
||||
|
||||
TeamcityGoogleTestEventListener::TeamcityGoogleTestEventListener() {
|
||||
flowid = getFlowIdFromEnvironment();
|
||||
}
|
||||
|
||||
TeamcityGoogleTestEventListener::TeamcityGoogleTestEventListener(const std::string& flowid_)
|
||||
: flowid(flowid_) {
|
||||
}
|
||||
|
||||
// Fired before the test case starts.
|
||||
void TeamcityGoogleTestEventListener::OnTestCaseStart(const TestCase& test_case) {
|
||||
messages.suiteStarted(test_case.name(), flowid);
|
||||
}
|
||||
|
||||
// Fired before the test starts.
|
||||
void TeamcityGoogleTestEventListener::OnTestStart(const TestInfo& test_info) {
|
||||
messages.testStarted(test_info.name(), flowid);
|
||||
}
|
||||
|
||||
// Fired after the test ends.
|
||||
void TeamcityGoogleTestEventListener::OnTestEnd(const TestInfo& test_info) {
|
||||
const TestResult* result = test_info.result();
|
||||
if (result->Failed()) {
|
||||
std::string message;
|
||||
std::string details;
|
||||
for (int i = 0; i < result->total_part_count(); ++i) {
|
||||
const TestPartResult& partResult = result->GetTestPartResult(i);
|
||||
if (partResult.passed()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (message.empty()) {
|
||||
message = partResult.summary();
|
||||
}
|
||||
|
||||
if (!details.empty()) {
|
||||
details.append("\n");
|
||||
}
|
||||
details.append(partResult.message());
|
||||
|
||||
if (partResult.file_name() && partResult.line_number() >= 0) {
|
||||
std::stringstream ss;
|
||||
ss << "\n at " << partResult.file_name() << ":" << partResult.line_number();
|
||||
details.append(ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
messages.testFailed(
|
||||
test_info.name(),
|
||||
!message.empty() ? message : "failed",
|
||||
details,
|
||||
flowid
|
||||
);
|
||||
}
|
||||
messages.testFinished(test_info.name(), static_cast<int>(result->elapsed_time()), flowid);
|
||||
}
|
||||
|
||||
// Fired after the test case ends.
|
||||
void TeamcityGoogleTestEventListener::OnTestCaseEnd(const TestCase& test_case) {
|
||||
messages.suiteFinished(test_case.name(), flowid);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
/* 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 "tests/gtest/teamcity/src/teamcity_messages.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
|
||||
namespace jetbrains {
|
||||
namespace teamcity {
|
||||
|
||||
std::string getFlowIdFromEnvironment() {
|
||||
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__) && !defined(__MINGW32__)
|
||||
char *flowId = NULL;
|
||||
size_t sz = 0;
|
||||
std::string result;
|
||||
if(!_dupenv_s(&flowId, &sz,"TEAMCITY_PROCESS_FLOW_ID")) {
|
||||
result = flowId != NULL ? flowId : "";
|
||||
free(flowId);
|
||||
}
|
||||
|
||||
return result;
|
||||
#else
|
||||
const char *flowId = getenv("TEAMCITY_PROCESS_FLOW_ID");
|
||||
return flowId == NULL ? "" : 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 != NULL;
|
||||
free(teamCityProjectName);
|
||||
}
|
||||
|
||||
return result;
|
||||
#else
|
||||
return getenv("TEAMCITY_PROJECT_NAME") != NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
TeamcityMessages::TeamcityMessages()
|
||||
: m_out(&std::cout)
|
||||
{}
|
||||
|
||||
void TeamcityMessages::setOutput(std::ostream &out) {
|
||||
m_out = &out;
|
||||
}
|
||||
|
||||
std::string TeamcityMessages::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 TeamcityMessages::openMsg(const std::string &name) {
|
||||
// endl for http://jetbrains.net/tracker/issue/TW-4412
|
||||
*m_out << std::endl << "##teamcity[" << name;
|
||||
}
|
||||
|
||||
void TeamcityMessages::closeMsg() {
|
||||
*m_out << "]";
|
||||
// endl for http://jetbrains.net/tracker/issue/TW-4412
|
||||
*m_out << std::endl;
|
||||
}
|
||||
|
||||
void TeamcityMessages::writeProperty(const std::string &name, const std::string &value) {
|
||||
*m_out << " " << name << "='" << escape(value) << "'";
|
||||
}
|
||||
|
||||
void TeamcityMessages::suiteStarted(const std::string &name, const std::string &flowid) {
|
||||
openMsg("testSuiteStarted");
|
||||
writeProperty("name", name);
|
||||
if(flowid.length() > 0) {
|
||||
writeProperty("flowId", flowid);
|
||||
}
|
||||
|
||||
closeMsg();
|
||||
}
|
||||
|
||||
void TeamcityMessages::suiteFinished(const std::string &name, const std::string &flowid) {
|
||||
openMsg("testSuiteFinished");
|
||||
writeProperty("name", name);
|
||||
if(flowid.length() > 0) {
|
||||
writeProperty("flowId", flowid);
|
||||
}
|
||||
|
||||
closeMsg();
|
||||
}
|
||||
|
||||
void TeamcityMessages::testStarted(const std::string &name, const std::string &flowid, bool captureStandardOutput) {
|
||||
openMsg("testStarted");
|
||||
writeProperty("name", name);
|
||||
if(flowid.length() > 0) {
|
||||
writeProperty("flowId", flowid);
|
||||
}
|
||||
|
||||
if(captureStandardOutput) {
|
||||
writeProperty("captureStandardOutput", "true"); // false by default
|
||||
}
|
||||
|
||||
closeMsg();
|
||||
}
|
||||
|
||||
void TeamcityMessages::testFinished(const std::string &name, int durationMs, const std::string &flowid) {
|
||||
openMsg("testFinished");
|
||||
|
||||
writeProperty("name", name);
|
||||
|
||||
if(flowid.length() > 0) {
|
||||
writeProperty("flowId", flowid);
|
||||
}
|
||||
|
||||
if(durationMs >= 0) {
|
||||
std::stringstream out(std::ios_base::out);
|
||||
out << durationMs;
|
||||
writeProperty("duration", out.str());
|
||||
}
|
||||
|
||||
closeMsg();
|
||||
}
|
||||
|
||||
void TeamcityMessages::testFailed(const std::string &name, const std::string &message, const std::string &details, const std::string &flowid) {
|
||||
openMsg("testFailed");
|
||||
writeProperty("name", name);
|
||||
writeProperty("message", message);
|
||||
writeProperty("details", details);
|
||||
if(flowid.length() > 0) {
|
||||
writeProperty("flowId", flowid);
|
||||
}
|
||||
|
||||
closeMsg();
|
||||
}
|
||||
|
||||
void TeamcityMessages::testIgnored(const std::string &name, const std::string &message, const std::string &flowid) {
|
||||
openMsg("testIgnored");
|
||||
writeProperty("name", name);
|
||||
writeProperty("message", message);
|
||||
if(flowid.length() > 0) {
|
||||
writeProperty("flowId", flowid);
|
||||
}
|
||||
|
||||
closeMsg();
|
||||
}
|
||||
|
||||
void TeamcityMessages::testOutput(const std::string &name, const std::string &output, const std::string &flowid, bool isStdError) {
|
||||
openMsg(isStdError ? "testStdErr" : "testStdOut");
|
||||
writeProperty("name", name);
|
||||
writeProperty("out", output);
|
||||
if(flowid.length() > 0) {
|
||||
writeProperty("flowId", flowid);
|
||||
}
|
||||
|
||||
closeMsg();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/* 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 $
|
||||
*/
|
||||
|
||||
#ifndef H_TEAMCITY_MESSAGES
|
||||
#define H_TEAMCITY_MESSAGES
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace jetbrains {
|
||||
namespace teamcity {
|
||||
|
||||
std::string getFlowIdFromEnvironment();
|
||||
bool underTeamcity();
|
||||
|
||||
class TeamcityMessages {
|
||||
std::ostream *m_out;
|
||||
|
||||
protected:
|
||||
std::string escape(const std::string &s);
|
||||
|
||||
void openMsg(const std::string &name);
|
||||
void writeProperty(const std::string &name, const std::string &value);
|
||||
void closeMsg();
|
||||
|
||||
public:
|
||||
static const bool StdErr = true;
|
||||
static const bool StdOut = false;
|
||||
|
||||
TeamcityMessages();
|
||||
|
||||
void setOutput(std::ostream &);
|
||||
|
||||
void suiteStarted(const std::string &name, const std::string &flowid = std::string());
|
||||
void suiteFinished(const std::string &name, const std::string &flowid = std::string());
|
||||
|
||||
void testStarted(const std::string &name, const std::string &flowid = std::string(), bool captureStandardOutput = false);
|
||||
void testFailed(const std::string &name, const std::string &message, const std::string &details, const std::string &flowid = std::string());
|
||||
void testIgnored(const std::string &name, const std::string &message, const std::string &flowid = std::string());
|
||||
void testOutput(const std::string &name, const std::string &output, const std::string &flowid, bool isStdErr = StdOut);
|
||||
void testFinished(const std::string &name, int durationMs = -1, const std::string &flowid = std::string());
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* H_TEAMCITY_MESSAGES */
|
|
@ -213,6 +213,11 @@ def create_fuzed_gtest(tests_dir):
|
|||
os.path.join(cef_dir, 'tests', 'gtest', 'README.cef.in'),
|
||||
os.path.join(target_gtest_dir, 'README.cef'), options.quiet)
|
||||
|
||||
# Copy tests/gtest/teamcity files
|
||||
copy_dir(
|
||||
os.path.join(cef_dir, 'tests', 'gtest', 'teamcity'),
|
||||
os.path.join(target_gtest_dir, 'teamcity'), options.quiet)
|
||||
|
||||
|
||||
def transfer_gypi_files(src_dir, gypi_paths, gypi_path_prefix, dst_dir, quiet):
|
||||
""" Transfer files from one location to another. """
|
||||
|
|
Loading…
Reference in New Issue