1// Copyright 2014 The Android Open Source Project
2//
3// This software is licensed under the terms of the GNU General Public
4// License version 2, as published by the Free Software Foundation, and
5// may be copied, distributed, and modified under those terms.
6//
7// This program is distributed in the hope that it will be useful,
8// but WITHOUT ANY WARRANTY; without even the implied warranty of
9// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10// GNU General Public License for more details.
11
12// Forces debug mode
13#define EINTR_WRAPPER_DEBUG 1
14
15#include "android/base/EintrWrapper.h"
16
17#include <stdarg.h>
18#include <setjmp.h>
19
20#include <gtest/gtest.h>
21
22namespace android {
23namespace base {
24
25// Implementation of a custom panic function used to detect that
26// HANDLE_EINTR() called panic after too many loop iterations.
27// Uses setjmp()/longjmp() since the panic handler must be
28// __attribute__((noreturn)).
29using namespace ::android::base::testing;
30
31class EintrWrapperTest : public ::testing::Test, LogOutput {
32public:
33    EintrWrapperTest() :
34            mFatal(false),
35            mLogged(true),
36            mPrevious(LogOutput::setNewOutput(this)) {}
37
38    ~EintrWrapperTest() {
39        LogOutput::setNewOutput(mPrevious);
40    }
41
42    virtual void logMessage(const android::base::LogParams& params,
43                            const char* message,
44                            size_t messageLen) {
45        mFatal = (params.severity == LOG_FATAL);
46        mLogged = true;
47        if (mFatal)
48            longjmp(mJumper, 1);
49    }
50
51protected:
52    bool mFatal;
53    bool mLogged;
54    LogOutput* mPrevious;
55    jmp_buf mJumper;
56};
57
58
59// Loop counter used by several functions below.
60static int gLoopCount = 0;
61
62// This function returns the first time it is called, or -1/EINVAL
63// otherwise.
64static int returnEinvalAfterFirstCall(void) {
65    if (++gLoopCount == 1)
66        return 0;
67
68    errno = EINVAL;
69    return -1;
70}
71
72TEST_F(EintrWrapperTest, NoLoopOnSuccess) {
73    gLoopCount = 0;
74    EXPECT_EQ(0, HANDLE_EINTR(returnEinvalAfterFirstCall()));
75    EXPECT_EQ(1, gLoopCount);
76}
77
78TEST_F(EintrWrapperTest, NoLoopOnRegularError) {
79    gLoopCount = 0;
80    EXPECT_EQ(0, HANDLE_EINTR(returnEinvalAfterFirstCall()));
81    EXPECT_EQ(-1, HANDLE_EINTR(returnEinvalAfterFirstCall()));
82    EXPECT_EQ(EINVAL, errno);
83    EXPECT_EQ(2, gLoopCount);
84}
85
86static int alwaysReturnEintr(void) {
87    gLoopCount++;
88#ifdef _WIN32
89    // Win32 cannot generate EINTR.
90    return 0;
91#else
92    errno = EINTR;
93    return -1;
94#endif
95}
96
97TEST_F(EintrWrapperTest, IgnoreEintr) {
98    gLoopCount = 0;
99    EXPECT_EQ(0, IGNORE_EINTR(alwaysReturnEintr()));
100    EXPECT_EQ(1, gLoopCount);
101}
102
103#ifndef _WIN32
104
105// This function loops 10 times around |gLoopCount|, while returning
106// -1/errno.
107static int loopEintr10(void) {
108    if (++gLoopCount < 10) {
109        errno = EINTR;
110        return -1;
111    }
112    return 0;
113}
114
115TEST_F(EintrWrapperTest, LoopOnEintr) {
116    gLoopCount = 0;
117    EXPECT_EQ(0, HANDLE_EINTR(loopEintr10()));
118    EXPECT_EQ(10, gLoopCount);
119}
120
121static int loopEintr200(void) {
122    if (++gLoopCount < 200) {
123        errno = EINTR;
124        return -1;
125    }
126    return 0;
127}
128
129TEST_F(EintrWrapperTest, PanicOnTooManyLoops) {
130    gLoopCount = 0;
131    if (setjmp(mJumper) == 0) {
132        HANDLE_EINTR(loopEintr200());
133        ASSERT_TRUE(0) << "HANDLE_EINTR() didn't panic!";
134    } else {
135        EXPECT_TRUE(mLogged);
136        EXPECT_TRUE(mFatal);
137        EXPECT_EQ(MAX_EINTR_LOOP_COUNT, gLoopCount);
138    }
139}
140
141#endif  // !_WIN32
142
143}  // namespace base
144}  // namespace android
145