int PipeControlChannel::createNamedPipe(const bsl::string& pipeName) { BALL_LOG_SET_CATEGORY(LOG_CATEGORY); BALL_LOG_TRACE << "Creating named pipe '" << pipeName << "'" << BALL_LOG_END; if (bdls::PipeUtil::isOpenForReading(pipeName)) { BALL_LOG_ERROR << "Named pipe '" << pipeName << "' already exists" << BALL_LOG_END; return -2; } d_impl.d_windows.d_handle = CreateNamedPipe(pipeName.c_str(), PIPE_ACCESS_INBOUND, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, MAX_PIPE_BUFFER_LEN, MAX_PIPE_BUFFER_LEN, 2000, // default timeout in ms for pipe operations NULL); if (INVALID_HANDLE_VALUE == d_impl.d_windows.d_handle) { BALL_LOG_TRACE << "Failed to create named pipe '" << pipeName << "': " << describeWin32Error(GetLastError()) << BALL_LOG_END; return -1; } return 0; }
void TestReader::close() { if (d_prefixes) { d_prefixes->reset(); } d_isOpen = false; d_encoding.clear(); d_nodeDepth = 0; d_currentNode = 0; }
void WordCountJob::operator()() { bool inWord = false; *d_result_p = 0; for (int i = 0; i < d_message.length(); ++i) { if (isspace(d_message[i])) { inWord = false; } else if (!inWord) { inWord = true; ++(*d_result_p); } } }
// ACCESSORS const char *TestReader::documentEncoding() const { return d_encoding.c_str(); }
int main(int argc, char *argv[]) { int test = (argc > 1) ? bsl::atoi(argv[1]) : 0; verbose = (argc > 2); veryVerbose = (argc > 3); veryVeryVerbose = (argc > 4); veryVeryVeryVerbose = (argc > 5); cout << "TEST " << __FILE__ << " CASE " << test << endl; switch (test) { case 0: case 7: { // -------------------------------------------------------------------- // TESTING USAGE EXAMPLE 1 // // Concerns: // The usage example provided in the component header file must // compile, link, and execute as shown. // // Plan: // Incorporate the usage example from the header file into the test // driver. Make use of existing test apparatus by instantiating // objects with a 'bslma::TestAllocator' object where applicable. // Additionally, replace all calls to 'assert' in the usage example // with calls to 'ASSERT'. This now becomes the source, which is // then "copied" back to the header file by reversing the above // process. // // Testing: // USAGE EXAMPLE 1 // -------------------------------------------------------------------- if (verbose) { cout << "Testing Usage Example" << endl << "=====================" << endl; } const double RATE = 10; const double DURATION = 1.0; const bsl::string MESSAGE = "1234567890"; const bsl::size_t MSGLEN = MESSAGE.length(); bsls::Stopwatch timer; bsl::ostringstream oss; timer.start(); heartbeat(oss, MESSAGE, RATE, DURATION - EPSILON); timer.stop(); double elapsed = timer.elapsedTime(); // The elapsed time should not be off by more than 15 ms. LOOP_ASSERT(elapsed, 1.0 - EPSILON <= elapsed && elapsed <= 1.0 + EPSILON); // The number of executed events should not be off by more than one. LOOP_ASSERT(oss.str().length(), RATE * DURATION * MSGLEN == oss.str().length()); if (veryVerbose) { P_(RATE); P(RATE * DURATION * (double) MESSAGE.length()); P_(elapsed); P(oss.str().length()); } } break; case 6: { // -------------------------------------------------------------------- // CONCERN: MULTI-THREADED TEST (LAG) // // Concerns: // - That the turnstile lags when multiple threads call 'waitTurn' at // lower than the configured rate. // // Plan: // Create a 'bslmt::Turnstile', 'mX', with a rate of 50. Create // multiple threads bound to a callback that calls 'waitTurn', // increments an atomic counter, and then sleeps. Start the threads. // Join the threads, and verify that the value of the counter is // less than the expected number of turns. // // Testing: // Concern: Multi-Threaded Test (Lag) // -------------------------------------------------------------------- if (verbose) { cout << "Concern: Multi-Threaded Test (Lag)" << endl << "==================================" << endl; } /* TBD -- bind const bsls::TimeInterval OFFSET(1.0); // turnstile start offset const double RATE = 50.0; const int NUM_TURNS = 50; const int NUM_THREADS = 3; bsls::TimeInterval stopTime(bsls::SystemTime::nowRealtimeClock()); stopTime.addMilliseconds(2 * OFFSET.totalMilliseconds()); Obj mX(RATE, OFFSET); const Obj& X = mX; bsl::vector<bslmt::ThreadUtil::Handle> handles; bsls::AtomicInt counter; bsls::TimeInterval sleepInterval(0.075); // 75ms bslmt::Barrier barrier(NUM_THREADS + 1); handles.reserve(NUM_THREADS); ASSERT(10 < sleepInterval.totalMilliseconds()); // Guarantee that the threads will sleep for (int i = 0; i < NUM_THREADS; ++i) { bslmt::ThreadUtil::Handle handle; ASSERT(0 == bslmt::ThreadUtil::create(&handle, bdlf::BindUtil::bind( &waitTurnAndSleepCallback, &mX, &counter, &barrier, sleepInterval, stopTime))); handles.push_back(handle); } barrier.wait(); // Cleanup threads for (int i = 0; i < (int) handles.size(); ++i) { ASSERT(0 == bslmt::ThreadUtil::join(handles[i])); } ASSERT(0 < X.lagTime()); ASSERT(NUM_TURNS > counter); if (NUM_TURNS <= counter) { P_(NUM_TURNS); P(counter); } if (veryVerbose) { P_(sleepInterval); P_(NUM_TURNS); P(counter); } */ } break; case 5: { // -------------------------------------------------------------------- // CONCERN: MULTI-THREADED TEST (NO LAG) // // Concerns: // - That calling 'waitTurn' from multiple threads is thread-safe. // // - That the turnstile does not lag when multiple threads call // 'waitTurn' at the configured rate. // // Plan: // Create a 'bslmt::Turnstile', 'mX', with a rate of 50. Create // multiple threads bound to a callback that calls 'waitTurn', and // then increments an atomic counter. Start the threads. Join the // threads, and verify that the value of the counter is at least the // expected number of turns. // // Testing: // Concern: Multi-Threaded Test (No Lag) // -------------------------------------------------------------------- if (verbose) { cout << "Concern: Multi-Threaded Test (No Lag)" << endl << "=====================================" << endl; } /* TBD -- bind const bsls::TimeInterval OFFSET(1.0); // turnstile start offset const double RATE = 8; const int NUM_TURNS = 8; const int NUM_THREADS = 3; bsls::TimeInterval stopTime(bsls::SystemTime::nowRealtimeClock()); stopTime.addMilliseconds(2 * OFFSET.totalMilliseconds()); Obj mX(RATE, OFFSET); const Obj& X = mX; bsl::vector<bslmt::ThreadUtil::Handle> handles; bsls::AtomicInt counter; bslmt::Barrier barrier(NUM_THREADS + 1); handles.reserve(NUM_THREADS); for (int i = 0; i < NUM_THREADS; ++i) { bslmt::ThreadUtil::Handle handle; ASSERT(0 == bslmt::ThreadUtil::create(&handle, bdlf::BindUtil::bind( &waitTurnCallback, &mX, &counter, &barrier, stopTime))); handles.push_back(handle); } barrier.wait(); // Cleanup threads for (int i = 0; i < (int) handles.size(); ++i) { ASSERT(0 == bslmt::ThreadUtil::join(handles[i])); } Int64 lt = X.lagTime(); LOOP_ASSERT(lt, 0 == lt); LOOP_ASSERT(counter, NUM_TURNS <= counter); */ } break; case 4: { // -------------------------------------------------------------------- // TESTING ALTERNATE CONSTRUCTORS // // Concerns: // - That calling 'waitTurn' after constructing a turnstile with // a specified start time blocks until that time. // // Plan: // Create a 'bslmt::Turnstile', 'mX', with a rate of 1, and a start // time offset of 1 second; and a non-modifiable reference to 'mX' // named 'X'. Call 'waitTurn' on 'mX' and 'lagTime' on 'X'. Verify // that the result of both calls is positive, indicating that the // caller is not lagging, and that some wait time is incurred. // Verify that the wait time is within 10ms of the expected maximum // wait time. // // Testing: // bslmt::Turnstile( // double rate, // const bsls::TimeInterval& startTime = bsls::TimeInterval(0)); // -------------------------------------------------------------------- if (verbose) { cout << "Testing Alternate Constructors" << endl << "==============================" << endl; } const double RATE = 1.0; const bsls::TimeInterval OFFSET(1.0); // turnstile start offset (1 sec) const double WT = 1.0 / RATE; // max wait time for each turn const Int64 WTUB = static_cast<Int64>(k_USPS * (WT + EPSILON)); // upper bound wait time const Int64 WTLB = static_cast<Int64>(k_USPS * (WT - EPSILON)); // lower bound wait time Obj mX(RATE, OFFSET); const Obj& X = mX; Int64 lt = X.lagTime(); Int64 wt = mX.waitTurn(); ASSERT(0 == lt); // not lagging since start time is offset ASSERT(0 < wt); // first turn cannot be taken immediately ASSERT(WTLB <= wt && wt <= WTUB); if (veryVerbose) { P_(WTLB); P_(WTUB); P_(wt); P(lt); } } break; case 3: { // -------------------------------------------------------------------- // TESTING FUNCTION 'reset' // // Concerns: // - That the next turn may be taken immediately after calling // 'reset'. // // - That 'reset' does not interrupt threads blocked on 'waitTurn'. // // - That calling 'waitTurn' after resetting a turnstile with // a specified start time blocks until that time. // // Plan: // Create a 'bslmt::Turnstile', 'mX', with a rate of 1, and a // non-modifiable reference to 'mX' named 'X'. Call 'waitTurn' on // 'mX' and 'lagTime' on 'X' to establish that the caller waits on // the turnstile and that the caller is not lagging. Then, call // 'reset' with the same rate. Call 'waitTurn' on 'mX', and verify // that the result is 0. Sleep for twice the maximum wait time, and // call 'waitTurn' and 'lagTime' again to establish that the caller // is lagging. Then, call 'reset' again with the same rate. Verify // that the result of 'waitTurn' is again 0. Finally, call 'reset' // with the same rate and an offset of 1 second. Call 'waitTime' on // 'mX' and 'lagTime' on 'X'. Verify that 'waitTime' returns a // positive value, and that the caller is not lagging. // // Testing: // void reset( // double rate, // const bsls::TimeInterval& startTime = bsls::TimeInterval(0)); // -------------------------------------------------------------------- if (verbose) { cout << "Testing Function 'reset'" << endl << "========================" << endl; } const double RATE = 1.0; const bsls::TimeInterval OFFSET(1.0); // turnstile reset offset (1 sec) Obj mX(RATE); const Obj& X = mX; ASSERT(0 <= X.lagTime()); // (likely) lagging on the first turn ASSERT(0 == mX.waitTurn()); // first turn can be taken immediately ASSERT(0 == X.lagTime()); // not lagging ASSERT(0 < mX.waitTurn()); // second turn incurs wait ASSERT(0 == X.lagTime()); // not lagging mX.reset(RATE); ASSERT(0 <= X.lagTime()); // (likely) lagging after 'reset' ASSERT(0 == mX.waitTurn()); // first turn can be taken immediately ASSERT(0 == X.lagTime()); // not lagging ASSERT(0 < mX.waitTurn()); // second turn incurs wait ASSERT(0 == X.lagTime()); // not lagging bslmt::ThreadUtil::microSleep(3 * k_USPS); ASSERT(0 == mX.waitTurn()); ASSERT(0 < X.lagTime()); // lagging after sleep mX.reset(RATE); ASSERT(0 <= X.lagTime()); // (likely) lagging after 'reset' ASSERT(0 == mX.waitTurn()); // first turn can be taken immediately ASSERT(0 == X.lagTime()); // not lagging ASSERT(0 < mX.waitTurn()); // second turn incurs wait ASSERT(0 == X.lagTime()); // not lagging mX.reset(RATE, OFFSET); ASSERT(0 == X.lagTime()); // not lagging after 'reset' ASSERT(0 < mX.waitTurn()); // first turn incurs wait ASSERT(0 == X.lagTime()); // not lagging ASSERT(0 < mX.waitTurn()); // second turn incurs wait ASSERT(0 == X.lagTime()); // not lagging } break; case 2: { // -------------------------------------------------------------------- // TESTING FUNCTION 'waitTurn' // // Concerns: // - That 'waitTurn' returns a positive value that is within a small // epsilon of the expected wait time when waiting is required. // // - That 'lagTime' returns 0 when turns are taken at or above the // specified rate. // // - That 'waitTurn' returns 0 when turns are taken more slowly than // the specified rate. // // - That 'lagTime' returns a positive value when turns are taken // more slowly than the specified rate. // // Plan: // Create a 'bslmt::Turnstile', 'mX', with a rate of 10.0, and a // non-modifiable reference to 'mX' named 'X'. In a loop, call // 'waitTurn' on 'mX' and 'lagTime' on 'X'. Verify that the result // of 'waitTime' is within 10ms of the expected maximum wait time, // and that the result of 'lagTime' is 0. Then, sleep for // twice the maximum wait time, and very that the result of // 'waitTime' is 0, and the result of 'lagTime' is positive. // // Testing: // bsls::Types::Int64 waitTurn(); // bsls::Types::Int64 lagTime() const; // -------------------------------------------------------------------- if (verbose) { cout << "Testing Function 'waitTurn'" << endl << "===========================" << endl; } // Turns are taken at or above the specified rate const double RATE = 5.0; const double WT = 1.0 / RATE; // max wait time for each turn const Int64 WTUB = k_USPS * (WT + EPSILON); // upper bound wait time const Int64 WTLB = k_USPS * (WT - EPSILON); // lower bound wait time if (verbose) { P_(RATE) P_(WT) P_(WTUB) P(WTLB); } Obj mX(RATE); const Obj& X = mX; int numTurns = RATE; ASSERT(0 <= X.lagTime()); // (likely) lagging on the first turn ASSERT(0 == mX.waitTurn()); // first turn can be taken immediately // First, burn a turn. This incurs a wait, but a smaller wait than the // maximum (since some processing has already been done). After taking // two turns, subsequent calls to 'waitTurn' should incur (close to) // the maximum wait time. mX.waitTurn(); do { Int64 wt = mX.waitTurn(); Int64 lt = X.lagTime(); LOOP3_ASSERT(WTLB, WTUB, wt, WTLB <= wt && wt <= WTUB); ASSERT(0 == lt); if (veryVerbose) { P_(WTLB); P_(WTUB); P_(wt); P(lt); } } while (--numTurns); // Turns are taken more slowly than the specified rate // 'X.lagTime()' does not report negative values, but suppose it did. // Call 'epsA' the amount of time we've spent doing stuff since the // last 'waitTurn'. Then 'X.lagTime() == - k_USPS * WT + epsA' at this // point. // Wait 2.25 times the period time int sleepTime = (int) (2.5 * k_USPS * WT); bslmt::ThreadUtil::microSleep(sleepTime); // At this point. 'X.lagTime() == 1.5 * k_USPS * WT + epsA'. Take one // turn. ASSERT(0 == mX.waitTurn()); // Now, 'X.lagTime() == 0.5 * k_USPS * WT + epsA'. Note that we can't // rely on system clocks having a small enough resolution to notice // 'epsA'. Verify that lagTime is positive. int lagTime = X.lagTime(); LOOP_ASSERT(lagTime, 0 < lagTime); if (verbose) { P_(sleepTime) P(lagTime); } } break; case 1: { // -------------------------------------------------------------------- // BREATHING TEST // // Concerns: // Exercise the basic functionality of the 'bslmt::Turnstile' class. // Ensure that a turnstile can be instantiated and destroyed. Also // exercise the primary manipulators and accessors. // // Plan: // Create a 'bslmt::Turnstile', 'mX', with a rate of 1, and a // non-modifiable reference to 'mX' named 'X'. Call 'waitTurn' on // 'mX' and 'lagTime' on 'X'. Destroy 'mX'. // // Testing: // Exercise basic functionality // -------------------------------------------------------------------- if (verbose) { cout << "BREATHING TEST" << endl << "==============" << endl; } const double RATE = 1.0; Obj mX(RATE); const Obj& X = mX; ASSERT(0 <= X.lagTime()); // (likely) lagging on the first turn ASSERT(0 == mX.waitTurn()); // first turn can be taken immediately } break; case -1: { // -------------------------------------------------------------------- // COMPARISON OF SLEEP TO TURNSTILE // // Concerns: // Compare the difference in elapsed time between 'sleep' and // 'bslmt::Turnstile' implementations of the heartbeat usage example // at various rates. // // Plan: // Call two functions, 'processWithTurnstile' and 'processWithSleep', // with various rates and durations. Compare the elapsed time // returned by these functions to the expected elapsed time. Verify // that the drift resulting from calling "sleep" in the // 'processWithSleep' function results in a greater difference from // the expected time that the elapsed time returned by the // 'bslmt::Turnstile' implementation. // // Testing: // Comparison of 'sleep' to 'bslmt::Turnstile' // -------------------------------------------------------------------- if (verbose) { cout << "Comparison of 'sleep' to 'bslmt::Turnstile'" << endl << "==========================================" << endl; } const struct { int d_line; // test line number double d_rate; // number of turns per second double d_duration; // duration of test in seconds } DATA[] = { //Line Rate Duration //---- ---- -------- { L_, 2, 1, }, { L_, 4, 1, }, { L_, 8, 1, }, { L_, 10, 1, }, { L_, 10, 2, }, { L_, 10, 4, }, { L_, 10, 8, }, { L_, 100, 1, }, { L_, 100, 2, }, { L_, 100, 4, }, { L_, 100, 8, }, { L_, 500, 1, }, { L_, 500, 2, }, { L_, 500, 4, }, { L_, 500, 8, }, { L_, 1000, 1, }, { L_, 1000, 2, }, { L_, 1000, 4, }, { L_, 1000, 8, }, }; const int DATA_SIZE = static_cast<int>(sizeof DATA / sizeof *DATA); for (int i = 0; i < DATA_SIZE; ++i) { int LINE = DATA[i].d_line; double RATE = DATA[i].d_rate; double DURATION = DATA[i].d_duration; double elapsed1 = 0.0; double elapsed2 = 0.0; processorWithTurnstile(RATE, DURATION, &elapsed1); processorWithSleep (RATE, DURATION, &elapsed2); bsl::printf("Line = %d, Rate = %f, Duration = %f\n" " elapsed1 = %.6f\n" " elapsed2 = %.6f\n\n" , LINE, RATE, DURATION , elapsed1, elapsed2); LOOP3_ASSERT(LINE, RATE, DURATION, elapsed1 <= elapsed2); } } break; case -2: { // -------------------------------------------------------------------- // SYSTEM CLOCK CHANGES DO NOT CAUSE HANGS // // Concerns: //: 1 Before recent changes to this component, a system clock change //: could cause the 'bslmt::Turnstile' to appear to hang. // // Plan: //: 1 Create a 'bslmt::Turnstile' which prints a message once a second, //: externally change the time, externally verify the messages do //: not stop printing. // // Testing: // 'bslmt::Turnstile' behavior is immune to system clock changes. // -------------------------------------------------------------------- if (verbose) { cout << "SYSTEM CLOCK CHANGES DO NOT CAUSE HANGS" << endl << "=======================================" << endl; } cout << endl << endl << "Modify your current system time." << endl << endl << "A message will be printed every second and if the" << endl << "messages stop printing before iteration 300 while" << endl << "you are changing the system time there is an issue." << endl << "You may use CNTRL-C to exit this test at any time." << endl << "The test will begin in three seconds; do not change" << endl << "the system time until after you see the first printed" << endl << "message." << endl; bslmt::ThreadUtil::microSleep(3 * k_USPS); bslmt::Turnstile turnstile(1.0); unsigned count = 0; while (count < 300) { turnstile.waitTurn(); ++count; cout << "turnstile iteration: " << count << endl; } } break; default: { cerr << "WARNING: CASE `" << test << "' NOT FOUND." << endl; testStatus = -1; } } if (testStatus > 0) { cerr << "Error, non-zero test status = " << testStatus << "." << endl; } return testStatus; }
int PipeControlChannel::createNamedPipe(const bsl::string& pipeName) { BALL_LOG_SET_CATEGORY(LOG_CATEGORY); // BSLS_ASSERT(0 == d_impl.d_unix.d_readFd); if (0 != d_impl.d_unix.d_readFd) { return -7; // RETURN } if (bdls::FilesystemUtil::exists(pipeName)) { // The pipe already exists, but it may have been left over from a // previous crash. Check whether there is a reader on the pipe, in // which case fail; otherwise unlink the pipe and continue. if (bdls::PipeUtil::isOpenForReading(pipeName)) { BALL_LOG_ERROR << "Named pipe " "'" << pipeName << "' " "is already in use by another process" << BALL_LOG_END; return -2; // RETURN } bdls::FilesystemUtil::remove(pipeName.c_str()); } // TBD: USE BDEU_PATHUTIL bsl::string dirname; if (0 == bdls::PathUtil::getDirname(&dirname, pipeName)) { if (!bdls::FilesystemUtil::exists(dirname)) { BALL_LOG_ERROR << "Named pipe directory " "'" << dirname << "' " "does not exist" << BALL_LOG_END; return -3; // RETURN } } const char *rawPipeName = pipeName.c_str(); int rc = mkfifo(rawPipeName, 0666); if (0 != rc) { int savedErrno = errno; BALL_LOG_ERROR << "Unable to create pipe " "'" << rawPipeName << "'" ": errno = " << savedErrno << " " "(" << bsl::strerror(savedErrno) << ")" << BALL_LOG_END; return -4; // RETURN } d_impl.d_unix.d_readFd = open(rawPipeName, O_RDONLY | O_NONBLOCK); if (-1 == d_impl.d_unix.d_readFd) { int savedErrno = errno; BALL_LOG_ERROR << "Cannot open pipe " "'" << rawPipeName << "' " "for reading" ": errno = " << savedErrno << " " "(" << bsl::strerror(savedErrno) << ")" << BALL_LOG_END; return -5; // RETURN } d_impl.d_unix.d_writeFd = open(rawPipeName, O_WRONLY); if (-1 == d_impl.d_unix.d_writeFd) { int savedErrno = errno; BALL_LOG_ERROR << "Failed to open named pipe " "'" << rawPipeName << "' " "for writing" ": errno = " << savedErrno << " " "(" << bsl::strerror(savedErrno) << ")" << BALL_LOG_END; return -6; // RETURN } BALL_LOG_TRACE << "Created named pipe '" << rawPipeName << "'" << BALL_LOG_END; return 0; }
int main(int argc, char *argv[]) { int test = argc > 1 ? bsl::atoi(argv[1]) : 0; int verbose = argc > 2; int veryVerbose = argc > 3; int veryVeryVerbose = argc > 4; cout << "TEST " << __FILE__ << " CASE " << test << endl;; switch (test) { case 0: // Zero is always the leading case. case 8: { // -------------------------------------------------------------------- // TESTING CONCERN: EOF IS STREAMED CORRECTLY // // Concerns: // * That 'xsputn' copies EOF when it appears within a buffer // boundary. // // * That 'xsputn' copies EOF when it appears as the first character // in a buffer (i.e., when it crosses a buffer boundary). // // Plan: // Iterate over a set of test vectors varying in buffer size and // length of data to write. For each test vector, instantiate a // 'btlb::PooledBlobBufferFactory', 'mF', allocate a 'btlb::Blob', // 'mC', and use 'mC' to instantiate a 'btlb::BlobStreamBuf', 'mX'. // Write the specified number of bytes to 'mX' using 'sputn', and // verify the length of 'mC'. Read the specified number of buffers // from 'mX', and verify the result, and the get area offset of 'mX'. // // Instantiate a 'btlb::PooledBlobBufferFactory', 'mF', allocate a // 'btlb::Blob', 'mC', and use 'mC' to instantiate a // 'btlb::BlobStreamBuf, 'mX'. Iterate over an input buffer whose // length is more than the size of 'mC', but less than twice the size // of 'mC'. On each iteration, write the input buffer into 'mX', // substituting the i'th character for EOF. Read back the data form // 'mX', and verify the result. // // Testing: // Concern: EOF is streamed correctly // -------------------------------------------------------------------- if (verbose) { cout << "Concern: EOF Is Streamed Correctly" << endl << "==================================" << endl; } bslma::TestAllocator ta(veryVeryVerbose); { const struct { int d_line; // source line number int d_bufferSize; // factory buffer size int d_dataLength; // length of data to read and write } DATA[] = { //Line Buffer Size Data Length //---- ----------- ----------- { L_, 1, 1, }, { L_, 1, 5, }, { L_, 2, 1, }, { L_, 2, 2, }, { L_, 2, 5, }, { L_, 3, 9, }, { L_, 37, 101, }, }; enum { k_DATA_SIZE = sizeof DATA / sizeof *DATA }; const bsl::ios_base::seekdir CUR = bsl::ios_base::cur; const bsl::ios_base::openmode OUT1 = bsl::ios_base::out; const bsl::ios_base::openmode IN1 = bsl::ios_base::in; const int EOF_VAL = btlb::InBlobStreamBuf::traits_type::eof(); for (int i = 0; i < k_DATA_SIZE; ++i) { const int LINE = DATA[i].d_line; const int BUFFER_SIZE = DATA[i].d_bufferSize; const int DATA_LENGTH = DATA[i].d_dataLength; if (verbose) { P_(i); P_(LINE); P_(BUFFER_SIZE); P(DATA_LENGTH); } testBlobBufferFactory fa(&ta, BUFFER_SIZE); fa.setGrowFlag(false); btlb::Blob blob(&fa, &ta); { btlb::InBlobStreamBuf in(&blob); btlb::OutBlobStreamBuf out(&blob); char *EOFS = (char *)ta.allocate(DATA_LENGTH); bsl::memset(EOFS, EOF_VAL, DATA_LENGTH); // Write out data. LOOP2_ASSERT(i, LINE, DATA_LENGTH == out.sputn(EOFS, DATA_LENGTH)); LOOP2_ASSERT(i, LINE, DATA_LENGTH == out.pubseekoff(0, CUR, OUT1)); LOOP2_ASSERT(i, LINE, DATA_LENGTH == blob.length()); // Read in data. char *result = (char *)ta.allocate(DATA_LENGTH); LOOP2_ASSERT(i, LINE, DATA_LENGTH == in.sgetn(result, DATA_LENGTH)); LOOP2_ASSERT(i, LINE, DATA_LENGTH == in.pubseekoff(0, CUR, IN1)); LOOP2_ASSERT(i, LINE, 0 == bsl::memcmp(EOFS, result, DATA_LENGTH)); ta.deallocate(EOFS); ta.deallocate(result); } } //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - enum { k_BUFFER_SIZE = 8, k_DATA_LENGTH = 12 }; ASSERT(k_DATA_LENGTH > k_BUFFER_SIZE); ASSERT(k_DATA_LENGTH < k_BUFFER_SIZE * 2); testBlobBufferFactory fa(&ta, k_BUFFER_SIZE); fa.setGrowFlag(false); char data[k_DATA_LENGTH]; char result[k_DATA_LENGTH]; bsl::memset(data, '*', k_DATA_LENGTH); for (int i = 0; i < k_DATA_LENGTH; ++i) { btlb::Blob blob(&fa, &ta); { btlb::InBlobStreamBuf in(&blob); btlb::OutBlobStreamBuf out(&blob); data[i] = EOF_VAL; LOOP_ASSERT(i, k_DATA_LENGTH == out.sputn(data, k_DATA_LENGTH)); LOOP_ASSERT(i, k_DATA_LENGTH == out.pubseekoff(0, CUR, OUT1)); LOOP_ASSERT(i, k_DATA_LENGTH == blob.length()); LOOP_ASSERT(i, k_DATA_LENGTH == in.sgetn(result, k_DATA_LENGTH)); LOOP_ASSERT(i, 0 == bsl::memcmp(data, result, k_DATA_LENGTH)); data[i] = '*'; } } } ASSERT(0 < ta.numAllocations()); ASSERT(0 == ta.numBytesInUse()); } break; case 7: { // -------------------------------------------------------------------- // TESTING 'xsgetn' AND 'xsputn' FUNCTIONS // // Concerns: // * That 'xsputn' returns the requested number of bytes when that // number is less than the current buffer capacity. // // * That 'xsputn' returns the requested number of bytes when that // number is greater than the current buffer capacity. // // * That 'xsgetn' returns the requested number of bytes when that // number is less than the current buffer capacity. // // * That 'xsgetn' returns the requested number of bytes when that // number is greater than the current buffer capacity. // // Plan: // Iterate over a set of test vectors varying in buffer size and // length of data to write. For each test vector, instantiate a // 'btlb::PooledBlobBufferFactory', 'mF', allocate a // 'btlb::Blob', 'mC', and use 'mC' to instantiate a // 'btlb::BlobStreamBuf', 'mX'. Write the specified // number of bytes to 'mX' using 'sputn', and verify the length of // 'mC'. Read the specified number of buffers from 'mX', and verify // the result, and the get area offset of 'mX'. // // Testing: // bsl::streamsize xsgetn(char_type *destination, // bsl::streamsize numChars); // bsl::streamsize xsputn(const char_type *source, // bsl::streamsize numChars); // -------------------------------------------------------------------- if (verbose) { cout << "Testing 'xsgetn' and 'xsputn' Functions" << endl << "=======================================" << endl; } bslma::TestAllocator ta(veryVeryVerbose); { const struct { int d_line; // source line number int d_bufferSize; // factory buffer size int d_dataLength; // length of data to read and write } DATA[] = { //Line Buffer Size Data Length //---- ----------- ----------- { L_, 1, 1, }, { L_, 1, 5, }, { L_, 2, 1, }, { L_, 2, 2, }, { L_, 2, 5, }, { L_, 3, 9, }, { L_, 37, 101, }, }; enum { k_DATA_SIZE = sizeof DATA / sizeof *DATA }; const bsl::ios_base::seekdir CUR = bsl::ios_base::cur; const bsl::ios_base::openmode OUT1 = bsl::ios_base::out; const bsl::ios_base::openmode IN1 = bsl::ios_base::in; for (int i = 0; i < k_DATA_SIZE; ++i) { const int LINE = DATA[i].d_line; const int k_BUFFER_SIZE = DATA[i].d_bufferSize; const int k_DATA_LENGTH = DATA[i].d_dataLength; if (verbose) { P_(i); P_(LINE); P_(k_BUFFER_SIZE); P(k_DATA_LENGTH); } testBlobBufferFactory fa(&ta, k_BUFFER_SIZE); fa.setGrowFlag(false); btlb::Blob blob(&fa, &ta); { btlb::InBlobStreamBuf in(&blob); btlb::OutBlobStreamBuf out(&blob); char *HASHMARKS = (char *)ta.allocate(k_DATA_LENGTH); bsl::memset(HASHMARKS, '#', k_DATA_LENGTH); // Write out data. LOOP2_ASSERT(i, LINE, k_DATA_LENGTH == out.sputn(HASHMARKS, k_DATA_LENGTH)); LOOP2_ASSERT(i, LINE, k_DATA_LENGTH == out.pubseekoff(0, CUR, OUT1)); LOOP2_ASSERT(i, LINE, k_DATA_LENGTH == blob.length()); // Read in data. char *result = (char *)ta.allocate(k_DATA_LENGTH); LOOP2_ASSERT(i, LINE, k_DATA_LENGTH == in.sgetn(result, k_DATA_LENGTH)); LOOP2_ASSERT(i, LINE, k_DATA_LENGTH == in.pubseekoff(0, CUR, IN1)); LOOP2_ASSERT(i, LINE, 0 == bsl::memcmp(HASHMARKS, result, k_DATA_LENGTH)); ta.deallocate(HASHMARKS); ta.deallocate(result); } } } ASSERT(0 < ta.numAllocations()); ASSERT(0 == ta.numBytesInUse()); } break; case 6: { // -------------------------------------------------------------------- // TESTING 'reset' FUNCTION // // Concerns: // * That 'reset' called with the default argument resets the get // and put areas, but does not affect the underlying buffer chain. // // * That 'reset' called with a buffer chain argument both resets // the underlying buffer chain to the specified buffer chain, and // resets the get and put areas. // // Plan: // Create two modifiable 'btlb::Blob' objects, 'mCa' // and 'mCb'. Create a modifiable 'btlb::BlobStreamBuf' // 'mX', instantiated with 'mCa', and a non-modifiable reference to // 'mX' named 'X'. Using 'X', verify that 'mX' is supported by // 'mCa'. Adjust the get and put areas by calling 'pubseekpos' on // 'mX'. Call 'reset' with the default argument on 'mX', and verify // using 'X', and by calling 'pubseekoff' on 'mX', that the get and // put areas have been reset. Call 'reset' with argument 'mCb' on // 'mX', and verify as before that the get and put areas have been // reset. Additionally verify that 'mX' is not supported by 'mCb'. // // Testing: // void reset(btlb::Blob *blob); // -------------------------------------------------------------------- if (verbose) { cout << "Testing 'reset' Function" << endl << "========================" << endl; } bslma::TestAllocator ta(veryVeryVerbose); if (verbose) cout << "\nTesting bcesb_OutBlobStreamBuf." << endl; { enum { k_BUFFER_SIZE_A = 16, // buffer size for factory "A" k_BUFFER_SIZE_B = 32, // buffer size for factory "B" k_SEEK_OFFSET = 37, // arbitrary offset k_DATA_LENGTH = 64 // amount of data to write to stream }; testBlobBufferFactory factoryA(&ta, k_BUFFER_SIZE_A); factoryA.setGrowFlag(false); btlb::Blob mCaI(&factoryA, &ta); btlb::Blob* mCa = &mCaI; ASSERT(0 == mCa->length()); ASSERT(0 == mCa->numBuffers()); testBlobBufferFactory factoryB(&ta, k_BUFFER_SIZE_B); factoryB.setGrowFlag(false); btlb::Blob mCbI(&factoryB, &ta); btlb::Blob* mCb = &mCbI; ASSERT(0 == mCb->length()); ASSERT(0 == mCb->numBuffers()); const bsl::ios_base::seekdir CUR = bsl::ios_base::cur; const bsl::ios_base::openmode OUT1 = bsl::ios_base::out; { btlb::OutBlobStreamBuf mX(mCa); const btlb::OutBlobStreamBuf& X = mX; ASSERT(X.data() == mCa); ASSERT(X.data() != mCb); ASSERT(0 == mX.pubseekoff(0, CUR, OUT1)); const bsl::string HASHMARKS(k_DATA_LENGTH, '#'); bsl::ostream out(&mX); out << HASHMARKS << flush; ASSERT(k_DATA_LENGTH == mX.pubseekoff(0, CUR, OUT1)); ASSERT(k_DATA_LENGTH == mCa->length()); ASSERT(4 == mCa->numBuffers()); ASSERT(0 == mCb->length()); ASSERT(0 == mCb->numBuffers()); mX.reset(); ASSERT(X.data() == mCa); ASSERT(X.data() != mCb); ASSERT(k_DATA_LENGTH == mX.pubseekoff(0, CUR, OUT1)); ASSERT(k_DATA_LENGTH == mCa->length()); ASSERT(4 == mCa->numBuffers()); ASSERT(0 == mCb->length()); ASSERT(0 == mCb->numBuffers()); out << HASHMARKS << flush; ASSERT(2 * k_DATA_LENGTH == mX.pubseekoff(0, CUR, OUT1)); ASSERT(8 == mCa->numBuffers()); ASSERT(0 == mCb->length()); ASSERT(0 == mCb->numBuffers()); mX.reset(mCb); ASSERT(X.data() != mCa); ASSERT(X.data() == mCb); ASSERT(0 == mX.pubseekoff(0, CUR, OUT1)); ASSERT(2 * k_DATA_LENGTH == mCa->length()); ASSERT(8 == mCa->numBuffers());; ASSERT(0 == mCb->length()); ASSERT(0 == mCb->numBuffers()); out << HASHMARKS << flush; ASSERT(k_DATA_LENGTH == mX.pubseekoff(0, CUR, OUT1)); ASSERT(2 * k_DATA_LENGTH == mCa->length()); ASSERT(8 == mCa->numBuffers());; ASSERT(k_DATA_LENGTH == mCb->length()); ASSERT(2 == mCb->numBuffers()); mCbI.removeAll(); ASSERT(0 == mCb->length()); ASSERT(0 == mCb->numBuffers()); ASSERT(mCb == X.data()); mX.reset(); ASSERT(mCa != X.data()); ASSERT(mCb == X.data()); } } if (verbose) cout << "\nTesting bcesb_InBlobStreamBuf." << endl; { enum { k_BUFFER_SIZE_A = 16, // buffer size for factory "A" k_BUFFER_SIZE_B = 32, // buffer size for factory "B" k_SEEK_OFFSET = 5 // arbitrary offset }; testBlobBufferFactory factoryA(&ta, k_BUFFER_SIZE_A); factoryA.setGrowFlag(false); btlb::Blob mCaI(&factoryA, &ta); btlb::Blob* mCa = &mCaI; ASSERT(0 == mCa->length()); ASSERT(0 == mCa->numBuffers()); testBlobBufferFactory factoryB(&ta, k_BUFFER_SIZE_B); factoryB.setGrowFlag(false); btlb::Blob mCbI(&factoryB, &ta); btlb::Blob* mCb = &mCbI; ASSERT(0 == mCb->length()); ASSERT(0 == mCb->numBuffers()); const bsl::ios_base::seekdir CUR = bsl::ios_base::cur; const bsl::ios_base::openmode IN1 = bsl::ios_base::in; { mCa->setLength(10); bsl::memset(mCa->buffer(0).data(), 1, 10); mCb->setLength(10); bsl::memset(mCb->buffer(0).data(), 2, 10); btlb::InBlobStreamBuf mX(mCa); const btlb::InBlobStreamBuf& X = mX; ASSERT(X.data() == mCa); ASSERT(X.data() != mCb); ASSERT(0 == mX.pubseekoff(0, CUR, IN1)); bsl::istream in(&mX); ASSERT(k_SEEK_OFFSET == mX.pubseekpos(k_SEEK_OFFSET, IN1)); char read; in >> read; ASSERT(1 == read); mX.reset(); ASSERT(X.data() == mCa); ASSERT(X.data() != mCb); ASSERT(0 == mX.pubseekoff(0, CUR, IN1)); in >> read; ASSERT(1 == read); mX.reset(mCb); ASSERT(X.data() != mCa); ASSERT(X.data() == mCb); ASSERT(0 == mX.pubseekoff(0, CUR, IN1)); in >> read; ASSERT(2 == read); ASSERT(k_SEEK_OFFSET == mX.pubseekpos(k_SEEK_OFFSET, IN1)); ASSERT(k_SEEK_OFFSET == mX.pubseekoff(0, CUR, IN1)); } } ASSERT(0 < ta.numAllocations()); ASSERT(0 == ta.numBytesInUse()); } break; case 5: { } break; case 4: { } break; case 3: { } break; case 2: { // -------------------------------------------------------------------- // TESTING PRIMARY MANIPULATORS (BOOTSTRAP) // // Concerns: // * That it is possible to instantiate a // 'btlb::BlobStreamBuf' object with a variety of // 'btlb::Blob' parameters. // // * That 'sync' and 'overflow' update the chain length. // // Plan: // Iterate over a set of test vectors varying in buffer size. For // each test vector, instantiate a modifiable // 'btlb::BlobStreamBuf', 'mX', and a non-modifiable // reference to 'mX' named 'X'. Write data of length 3 times the // specified buffer size to 'mX' in chunks of buffer size, 'sync'ing // 'mX' after each write, and verifying the chain length and number // of buffers using 'X'. // // Testing: // btlb::BlobStreamBuf(btlb::Blob *blob); // ~btlb::BlobStreamBuf(); // int_type overflow(int_type c = // bsl::streambuf::traits_type::eof()); // int sync(); // const btlb::Blob *data() const; // -------------------------------------------------------------------- if (verbose) { cout << "TESTING PRIMARY MANIPULATORS (BOOTSTRAP)" << endl << "========================================" << endl; } bslma::TestAllocator ta(veryVeryVerbose); { const struct { int d_line; // source line number int d_bufferSize; // factory buffer size } DATA[] = { //Line Buffer Size //---- ----------- { L_, 1, }, { L_, 2, }, { L_, 4, }, { L_, 11, }, { L_, 101, }, { L_, 1024, }, { L_, 4096, }, }; enum { k_DATA_SIZE = sizeof DATA / sizeof *DATA }; for (int i = 0; i < k_DATA_SIZE; ++i) { const int LINE = DATA[i].d_line; const int k_BUFFER_SIZE = DATA[i].d_bufferSize; testBlobBufferFactory fa(&ta, k_BUFFER_SIZE); fa.setGrowFlag(false); btlb::Blob blob(&fa, &ta); btlb::OutBlobStreamBuf mX(&blob); const btlb::OutBlobStreamBuf& X = mX; const bsl::string HASHMARKS(k_BUFFER_SIZE, '#'); if (verbose) { P_(i); P_(LINE); P(k_BUFFER_SIZE); } enum { k_NUM_ITERATIONS = 3 }; for (int j = 0; j < k_NUM_ITERATIONS; ++j) { const int PRE_PUT_LENGTH = j * k_BUFFER_SIZE; const int POST_PUT_LENGTH = PRE_PUT_LENGTH + 1; const int POST_SYNC_LENGTH = (j + 1) * k_BUFFER_SIZE; LOOP3_ASSERT(i, LINE, j, PRE_PUT_LENGTH == X.data()->length()); LOOP3_ASSERT(i, LINE, j, j == X.data()->numBuffers()); mX.sputn(HASHMARKS.c_str(), HASHMARKS.length()); LOOP3_ASSERT(i, LINE, j, POST_PUT_LENGTH == X.data()->length()); LOOP3_ASSERT(i, LINE, j, j + 1 == X.data()->numBuffers()); mX.pubsync(); LOOP3_ASSERT(i, LINE, j, POST_SYNC_LENGTH == X.data()->length()); LOOP3_ASSERT(i, LINE, j, j + 1 == X.data()->numBuffers()); } } } ASSERT(0 < ta.numAllocations()); ASSERT(0 == ta.numBytesInUse()); } break; case 1: { // -------------------------------------------------------------------- // BREATHING TEST: // Developers' Sandbox. // // Concerns: // // Plan: // // Tactics: // - Ad Hoc Test Data Selection Method // - Brute Force Implementation Technique // // Testing: // This "test" *exercises* basic functionality, but *tests* nothing. // -------------------------------------------------------------------- if (verbose) cout << endl << "BREATHING TEST" << endl << "==============" << endl; bslma::TestAllocator ta(veryVeryVerbose); if (verbose) cout << "\nTesting bcesb_InBlobStreamBuf." << endl; { typedef btlb::InBlobStreamBuf Obj; enum { k_MAX_k_BUFFER_SIZE = 20 }; for(int i = 0; i < k_MAX_k_BUFFER_SIZE; ++i) { const bsl::size_t k_BUFFER_SIZE = i + 1; testBlobBufferFactory fa(&ta, k_BUFFER_SIZE); //fa.setGrowFlag(false); btlb::Blob blob(&fa, &ta); { Obj mX(&blob); const Obj& X = mX; bsl::istream stream(&mX); ASSERT(stream.rdbuf() == &mX); if (verbose) { P_(i); P(k_BUFFER_SIZE); } { int j; ASSERT(!(stream >> j)); ASSERT(!stream); stream.clear(); } { int posInBuf = 0; int currentBuf = 0; blob.setLength(k_MAX_k_BUFFER_SIZE); for (int j = 0; j < k_MAX_k_BUFFER_SIZE; ++j) { if (posInBuf == blob.buffer(currentBuf).size()) { ++currentBuf; posInBuf = 0; } *(blob.buffer(currentBuf).data() + posInBuf) = 'A' + j; if (veryVerbose) { bsl::cout << "Wrote " << j << " at offset " << posInBuf << " in buffer " << currentBuf << bsl::endl; } ++posInBuf; } } { int j = 0; char c; while (stream >> c) { LOOP_ASSERT(j, c == 'A' + j); if (verbose) { T_; P_(c); P(j); } ASSERT(stream.rdbuf() == &mX); ASSERT(stream.unget()); if (j) { ASSERT(stream.unget()); ASSERT(stream >> c) LOOP_ASSERT(j, c == 'A' + j - 1); } else { ASSERT(!stream.unget()); stream.clear(); } ASSERT(stream >> c) LOOP_ASSERT(j, c == 'A' + j); ++j; } ASSERT(k_MAX_k_BUFFER_SIZE == j); } ASSERT(!stream); stream.clear(); stream.seekg(0); for (int j = 0; j < k_MAX_k_BUFFER_SIZE; ++j) { for (int k = 0; k < k_MAX_k_BUFFER_SIZE; ++k) { char c; stream.seekg(j); ASSERT(stream >> c); LOOP2_ASSERT(j, k, c == j + 'A'); stream.seekg(k); ASSERT(stream >> c); LOOP2_ASSERT(j, k, c == k + 'A'); } } bsl::string str; ASSERT(!(stream >> str)); stream.clear(); stream.seekg(0); ASSERT(stream >> str); ASSERT(k_MAX_k_BUFFER_SIZE == str.length()); ASSERT('A' == str[0]); ASSERT('A' + k_MAX_k_BUFFER_SIZE - 1 == str[k_MAX_k_BUFFER_SIZE - 1]); } } } if (verbose) cout << "\nTesting bcesb_OutBlobStreamBuf." << endl; { enum { k_MAX_k_BUFFER_SIZE = 20 }; for(int i = 0; i < k_MAX_k_BUFFER_SIZE; ++i) { const bsl::size_t k_BUFFER_SIZE = i + 1; testBlobBufferFactory fa(&ta, k_BUFFER_SIZE); //fa.setGrowFlag(false); btlb::Blob blob(&fa, &ta); { btlb::OutBlobStreamBuf outbuf(&blob); bsl::ostream ostream(&outbuf); btlb::InBlobStreamBuf inbuf(&blob); bsl::istream istream(&inbuf); if (verbose) { P_(i); P(k_BUFFER_SIZE); } ostream << 12345; ostream.flush(); int j; istream >> j; LOOP_ASSERT(i, 12345 == j); if (verbose) { T_; P_(i); P(j); } istream.clear(); istream.seekg(0); int k; istream >> k; LOOP_ASSERT(i, j == k); if (verbose) { T_; P_(i); P_(j); P(k); } istream.clear(); ostream.seekp(0); istream.seekg(0); bsl::string value; ostream << 654321 << bsl::flush; istream >> value; LOOP_ASSERT(i, "654321" == value); istream.clear(); ostream.seekp(0); istream.seekg(0); // Since we cannot truncate the streambuf, we must write at // least as many bytes as already exist in the buffer. int length = bsl::max(3 * (int)k_BUFFER_SIZE, blob.length()); const bsl::string HASHMARKS(length, '#'); bsl::string result; ostream << HASHMARKS << bsl::flush; istream >> result; LOOP_ASSERT(i, HASHMARKS == result); if (verbose) { T_; P_(i); P_(HASHMARKS.length()); P(result.length()); } } } } ASSERT(0 < ta.numAllocations()); ASSERT(0 == ta.numBytesInUse()); } break; default: { cerr << "WARNING: CASE `" << test << "' NOT FOUND." << endl; testStatus = -1; } }
inline void debugprint(const bsl::string &s) { printf("\"%s\"", s.c_str()); fflush(stdout); }