Commit 5e80856a authored by Daniel Sommermann's avatar Daniel Sommermann Committed by Dave Watson

Move some tests to folly

Summary:
These lived in fbthrift previously, but they should move with the
main code too.

Test Plan: unit tests

Reviewed By: davejwatson@fb.com

Subscribers: doug, alandau, bmatheny, njormrod, mshneer, folly-diffs@

FB internal diff: D1683229

Signature: t1:1683229:1416010730:36fb7e4c9916ae7a9b5972cd476f82014c5f4c78
parent 6d8f063d
...@@ -167,6 +167,9 @@ nobase_follyinclude_HEADERS = \ ...@@ -167,6 +167,9 @@ nobase_follyinclude_HEADERS = \
io/async/Request.h \ io/async/Request.h \
io/async/SSLContext.h \ io/async/SSLContext.h \
io/async/TimeoutManager.h \ io/async/TimeoutManager.h \
io/async/test/TimeUtil.h \
io/async/test/UndelayedDestruction.h \
io/async/test/Util.h \
json.h \ json.h \
Lazy.h \ Lazy.h \
LifoSem.h \ LifoSem.h \
...@@ -296,6 +299,7 @@ libfolly_la_SOURCES = \ ...@@ -296,6 +299,7 @@ libfolly_la_SOURCES = \
io/async/Request.cpp \ io/async/Request.cpp \
io/async/SSLContext.cpp \ io/async/SSLContext.cpp \
io/async/HHWheelTimer.cpp \ io/async/HHWheelTimer.cpp \
io/async/test/TimeUtil.cpp \
json.cpp \ json.cpp \
detail/MemoryIdler.cpp \ detail/MemoryIdler.cpp \
MacAddress.cpp \ MacAddress.cpp \
......
This diff is collapsed.
This diff is collapsed.
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 <folly/io/async/test/SocketPair.h>
#include <folly/Conv.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdexcept>
namespace folly {
SocketPair::SocketPair(Mode mode) {
if (socketpair(PF_UNIX, SOCK_STREAM, 0, fds_) != 0) {
throw std::runtime_error(
folly::to<std::string>("test::SocketPair: failed create socket pair",
errno));
}
if (mode == NONBLOCKING) {
if (fcntl(fds_[0], F_SETFL, O_NONBLOCK) != 0) {
throw std::runtime_error(
folly::to<std::string>("test::SocketPair: failed to set non-blocking "
"read mode", errno));
}
if (fcntl(fds_[1], F_SETFL, O_NONBLOCK) != 0) {
throw std::runtime_error(
folly::to<std::string>("test::SocketPair: failed to set non-blocking "
"write mode", errno));
}
}
}
SocketPair::~SocketPair() {
closeFD0();
closeFD1();
}
void SocketPair::closeFD0() {
if (fds_[0] >= 0) {
close(fds_[0]);
fds_[0] = -1;
}
}
void SocketPair::closeFD1() {
if (fds_[1] >= 0) {
close(fds_[1]);
fds_[1] = -1;
}
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#pragma once
namespace folly {
class SocketPair {
public:
enum Mode {
BLOCKING,
NONBLOCKING
};
explicit SocketPair(Mode mode = NONBLOCKING);
~SocketPair();
int operator[](int index) const {
return fds_[index];
}
void closeFD0();
void closeFD1();
int extractFD0() {
return extractFD(0);
}
int extractFD1() {
return extractFD(1);
}
int extractFD(int index) {
int fd = fds_[index];
fds_[index] = -1;
return fd;
}
private:
int fds_[2];
};
}
/*
* Copyright 2014 Facebook, Inc.
*
* 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.
*/
#define __STDC_FORMAT_MACROS
#include <folly/io/async/test/TimeUtil.h>
#include <folly/Conv.h>
#include <chrono>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/utsname.h>
#include <errno.h>
#include <glog/logging.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdexcept>
using std::string;
using namespace std::chrono;
namespace folly {
/**
* glibc doesn't provide gettid(), so define it ourselves.
*/
static pid_t gettid() {
return syscall(SYS_gettid);
}
/**
* The /proc/<pid>/schedstat file reports time values in jiffies.
*
* Determine how many jiffies are in a second.
* Returns -1 if the number of jiffies/second cannot be determined.
*/
static int64_t determineJiffiesHZ() {
// It seems like the only real way to figure out the CONFIG_HZ value used by
// this kernel is to look it up in the config file.
//
// Look in /boot/config-<kernel_release>
struct utsname unameInfo;
if (uname(&unameInfo) != 0) {
LOG(ERROR) << "unable to determine jiffies/second: uname failed: %s"
<< strerror(errno);
return -1;
}
char configPath[256];
snprintf(configPath, sizeof(configPath), "/boot/config-%s",
unameInfo.release);
FILE* f = fopen(configPath, "r");
if (f == nullptr) {
LOG(ERROR) << "unable to determine jiffies/second: "
"cannot open kernel config file %s" << configPath;
return -1;
}
int64_t hz = -1;
char buf[1024];
while (fgets(buf, sizeof(buf), f) != nullptr) {
if (strcmp(buf, "CONFIG_NO_HZ=y\n") == 0) {
// schedstat info seems to be reported in nanoseconds on tickless
// kernels.
//
// The CONFIG_HZ value doesn't matter for our purposes,
// so return as soon as we see CONFIG_NO_HZ.
fclose(f);
return 1000000000;
} else if (strcmp(buf, "CONFIG_HZ=1000\n") == 0) {
hz = 1000;
} else if (strcmp(buf, "CONFIG_HZ=300\n") == 0) {
hz = 300;
} else if (strcmp(buf, "CONFIG_HZ=250\n") == 0) {
hz = 250;
} else if (strcmp(buf, "CONFIG_HZ=100\n") == 0) {
hz = 100;
}
}
fclose(f);
if (hz == -1) {
LOG(ERROR) << "unable to determine jiffies/second: no CONFIG_HZ setting "
"found in %s" << configPath;
return -1;
}
return hz;
}
/**
* Determine how long this process has spent waiting to get scheduled on the
* CPU.
*
* Returns the number of milliseconds spent waiting, or -1 if the amount of
* time cannot be determined.
*/
static milliseconds getTimeWaitingMS(pid_t tid) {
static int64_t jiffiesHZ = 0;
if (jiffiesHZ == 0) {
jiffiesHZ = determineJiffiesHZ();
}
if (jiffiesHZ < 0) {
// We couldn't figure out how many jiffies there are in a second.
// Don't bother reading the schedstat info if we can't interpret it.
return milliseconds(0);
}
int fd = -1;
try {
char schedstatFile[256];
snprintf(schedstatFile, sizeof(schedstatFile),
"/proc/%d/schedstat", tid);
fd = open(schedstatFile, O_RDONLY);
if (fd < 0) {
throw std::runtime_error(
folly::to<string>("failed to open process schedstat file", errno));
}
char buf[512];
ssize_t bytesReadRet = read(fd, buf, sizeof(buf) - 1);
if (bytesReadRet <= 0) {
throw std::runtime_error(
folly::to<string>("failed to read process schedstat file", errno));
}
size_t bytesRead = size_t(bytesReadRet);
if (buf[bytesRead - 1] != '\n') {
throw std::runtime_error("expected newline at end of schedstat data");
}
assert(bytesRead < sizeof(buf));
buf[bytesRead] = '\0';
uint64_t activeJiffies = 0;
uint64_t waitingJiffies = 0;
uint64_t numTasks = 0;
int rc = sscanf(buf, "%" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
&activeJiffies, &waitingJiffies, &numTasks);
if (rc != 3) {
throw std::runtime_error("failed to parse schedstat data");
}
close(fd);
return milliseconds((waitingJiffies * 1000) / jiffiesHZ);
} catch (const std::runtime_error& e) {
if (fd >= 0) {
close(fd);
}
LOG(ERROR) << "error determining process wait time: %s" << e.what();
return milliseconds(0);
}
}
void TimePoint::reset() {
// Remember the current time
timeStart_ = system_clock::now();
// Remember how long this process has spent waiting to be scheduled
tid_ = gettid();
timeWaiting_ = getTimeWaitingMS(tid_);
// In case it took a while to read the schedstat info,
// also record the time after the schedstat check
timeEnd_ = system_clock::now();
}
std::ostream& operator<<(std::ostream& os, const TimePoint& timePoint) {
os << "TimePoint(" << timePoint.getTimeStart().time_since_epoch().count()
<< ", " << timePoint.getTimeEnd().time_since_epoch().count() << ", "
<< timePoint.getTimeWaiting().count() << ")";
return os;
}
bool
checkTimeout(const TimePoint& start, const TimePoint& end,
milliseconds expectedMS, bool allowSmaller,
milliseconds tolerance) {
auto elapsedMS = end.getTimeStart() - start.getTimeEnd();
if (!allowSmaller) {
// Timeouts should never fire before the time was up.
// Allow 1ms of wiggle room for rounding errors.
if (elapsedMS < expectedMS - milliseconds(1)) {
return false;
}
}
// Check that the event fired within a reasonable time of the timout.
//
// If the system is under heavy load, our process may have had to wait for a
// while to be run. The time spent waiting for the processor shouldn't
// count against us, so exclude this time from the check.
milliseconds excludedMS;
if (end.getTid() != start.getTid()) {
// We can only correctly compute the amount of time waiting to be scheduled
// if both TimePoints were set in the same thread.
excludedMS = milliseconds(0);
} else {
excludedMS = end.getTimeWaiting() - start.getTimeWaiting();
assert(end.getTimeWaiting() >= start.getTimeWaiting());
// Add a tolerance here due to precision issues on linux, see below note.
assert( (elapsedMS + tolerance) >= excludedMS);
}
milliseconds effectiveElapsedMS = milliseconds(0);
if (elapsedMS > excludedMS) {
effectiveElapsedMS = duration_cast<milliseconds>(elapsedMS) - excludedMS;
}
// On x86 Linux, sleep calls generally have precision only to the nearest
// millisecond. The tolerance parameter lets users allow a few ms of slop.
milliseconds overrun = effectiveElapsedMS - expectedMS;
if (overrun > tolerance) {
return false;
}
return true;
}
}
/*
* Copyright 2014 Facebook, Inc.
*
* 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.
*/
#pragma once
#include <chrono>
#include <iostream>
namespace folly {
class TimePoint {
public:
explicit TimePoint(bool set = true)
: tid_(0) {
if (set) {
reset();
}
}
void reset();
bool isUnset() const {
return (timeStart_.time_since_epoch().count() == 0 &&
timeEnd_.time_since_epoch().count() == 0 &&
timeWaiting_.count() == 0);
}
std::chrono::system_clock::time_point getTime() const {
return timeStart_;
}
std::chrono::system_clock::time_point getTimeStart() const {
return timeStart_;
}
std::chrono::system_clock::time_point getTimeEnd() const {
return timeStart_;
}
std::chrono::milliseconds getTimeWaiting() const {
return timeWaiting_;
}
pid_t getTid() const {
return tid_;
}
private:
std::chrono::system_clock::time_point timeStart_;
std::chrono::system_clock::time_point timeEnd_;
std::chrono::milliseconds timeWaiting_;
pid_t tid_;
};
std::ostream& operator<<(std::ostream& os, const TimePoint& timePoint);
bool checkTimeout(const TimePoint& start,
const TimePoint& end,
std::chrono::milliseconds expectedMS,
bool allowSmaller,
std::chrono::milliseconds tolerance =
std::chrono::milliseconds(5));
}
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#pragma once
#include <cstdlib>
#include <type_traits>
#include <utility>
#include <cassert>
namespace folly {
/**
* A helper class to allow a DelayedDestruction object to be instantiated on
* the stack.
*
* This class derives from an existing DelayedDestruction type and makes the
* destructor public again. This allows objects of this type to be declared on
* the stack or directly inside another class. Normally DelayedDestruction
* objects must be dynamically allocated on the heap.
*
* However, the trade-off is that you lose some of the protections provided by
* DelayedDestruction::destroy(). DelayedDestruction::destroy() will
* automatically delay destruction of the object until it is safe to do so.
* If you use UndelayedDestruction, you become responsible for ensuring that
* you only destroy the object where it is safe to do so. Attempting to
* destroy a UndelayedDestruction object while it has a non-zero destructor
* guard count will abort the program.
*/
template<typename TDD>
class UndelayedDestruction : public TDD {
public:
// We could just use constructor inheritance, but not all compilers
// support that. So, just use a forwarding constructor.
//
// Ideally we would use std::enable_if<> and std::is_constructible<> to
// provide only constructor methods that are valid for our parent class.
// Unfortunately std::is_constructible<> doesn't work for types that aren't
// destructible. In gcc-4.6 it results in a compiler error. In the latest
// gcc code it looks like it has been fixed to return false. (The language
// in the standard seems to indicate that returning false is the correct
// behavior for non-destructible types, which is unfortunate.)
template<typename ...Args>
explicit UndelayedDestruction(Args&& ...args)
: TDD(std::forward<Args>(args)...) {}
/**
* Public destructor.
*
* The caller is responsible for ensuring that the object is only destroyed
* where it is safe to do so. (i.e., when the destructor guard count is 0).
*
* The exact conditions for meeting this may be dependant upon your class
* semantics. Typically you are only guaranteed that it is safe to destroy
* the object directly from the event loop (e.g., directly from a
* TEventBase::LoopCallback), or when the event loop is stopped.
*/
virtual ~UndelayedDestruction() {
// Crash if the caller is destroying us with outstanding destructor guards.
if (this->getDestructorGuardCount() != 0) {
abort();
}
// Invoke destroy. This is necessary since our base class may have
// implemented custom behavior in destroy().
this->destroy();
}
protected:
/**
* Override our parent's destroy() method to make it protected.
* Callers should use the normal destructor instead of destroy
*/
virtual void destroy() {
this->TDD::destroy();
}
virtual void destroyNow(bool delayed) {
// Do nothing. This will always be invoked from the call to destroy inside
// our destructor.
assert(!delayed);
// prevent unused variable warnings when asserts are compiled out.
(void)delayed;
}
private:
// Forbidden copy constructor and assignment operator
UndelayedDestruction(UndelayedDestruction const &) = delete;
UndelayedDestruction& operator=(UndelayedDestruction const &) = delete;
};
}
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#pragma once
#include <folly/io/async/test/TimeUtil.h>
#include <gtest/gtest.h>
/**
* Check how long a timeout took to fire.
*
* This method verifies:
* - that the timeout did not fire too early (never less than expectedMS)
* - that the timeout fired within a reasonable period of the expected
* duration. It must fire within the specified tolerance, excluding time
* that this process spent waiting to be scheduled.
*
* @param start A TimePoint object set just before the timeout
* was scheduled.
* @param end A TimePoint object set when the timeout fired.
* @param expectedMS The timeout duration, in milliseconds
* @param tolerance The tolerance, in milliseconds.
*/
#define T_CHECK_TIMEOUT(start, end, expectedMS, ...) \
EXPECT_TRUE(::folly::checkTimeout((start), (end), \
(expectedMS), false, \
##__VA_ARGS__))
/**
* Verify that an event took less than a specified amount of time.
*
* This is similar to T_CHECK_TIMEOUT, but does not fail if the event took less
* than the allowed time.
*/
#define T_CHECK_TIME_LT(start, end, expectedMS, ...) \
EXPECT_TRUE(::folly::checkTimeout((start), (end), \
(expectedMS), true, \
##__VA_ARGS__))
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment