Index: lldb/trunk/lldb.xcodeproj/project.pbxproj =================================================================== --- lldb/trunk/lldb.xcodeproj/project.pbxproj +++ lldb/trunk/lldb.xcodeproj/project.pbxproj @@ -867,6 +867,7 @@ 2689004F13353E0400698AC0 /* StreamFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BC7E9210F1B85900F91463 /* StreamFile.cpp */; }; AFC2DCF91E6E318000283714 /* StreamGDBRemote.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AFC2DCF81E6E318000283714 /* StreamGDBRemote.cpp */; }; 26764CA21E48F547008D3573 /* StreamString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26764CA11E48F547008D3573 /* StreamString.cpp */; }; + 58EAC73F2106A07B0029571E /* StreamTeeTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 58EAC73D2106A0740029571E /* StreamTeeTest.cpp */; }; 33E5E8471A674FB60024ED68 /* StringConvert.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 33E5E8411A672A240024ED68 /* StringConvert.cpp */; }; 2689011113353E8200698AC0 /* StringExtractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2660D9F611922A1300958FBD /* StringExtractor.cpp */; }; 2689011213353E8200698AC0 /* StringExtractorGDBRemote.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2676A093119C93C8008A98EF /* StringExtractorGDBRemote.cpp */; }; @@ -2885,6 +2886,7 @@ 26764CA11E48F547008D3573 /* StreamString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StreamString.cpp; path = source/Utility/StreamString.cpp; sourceTree = ""; }; 26764CA31E48F550008D3573 /* StreamString.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = StreamString.h; path = include/lldb/Utility/StreamString.h; sourceTree = ""; }; 26764CA41E48F566008D3573 /* StreamTee.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = StreamTee.h; path = include/lldb/Utility/StreamTee.h; sourceTree = ""; }; + 58EAC73D2106A0740029571E /* StreamTeeTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StreamTeeTest.cpp; sourceTree = ""; }; 33E5E8411A672A240024ED68 /* StringConvert.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StringConvert.cpp; sourceTree = ""; }; 33E5E8451A6736D30024ED68 /* StringConvert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StringConvert.h; path = include/lldb/Host/StringConvert.h; sourceTree = SOURCE_ROOT; }; 2660D9F611922A1300958FBD /* StringExtractor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = StringExtractor.cpp; path = source/Utility/StringExtractor.cpp; sourceTree = ""; }; @@ -3495,6 +3497,7 @@ 2321F9421BDD343A00BA9A93 /* Utility */ = { isa = PBXGroup; children = ( + 58EAC73D2106A0740029571E /* StreamTeeTest.cpp */, 7F94D7172040A13A006EE3EA /* CleanUpTest.cpp */, 23E2E5161D903689006F38BB /* ArchSpecTest.cpp */, 9A3D43C81F3150D200EB767C /* ConstStringTest.cpp */, @@ -7426,6 +7429,7 @@ 23CB15371D66DA9300EDDDE1 /* PythonTestSuite.cpp in Sources */, 23E2E5321D903832006F38BB /* BreakpointIDTest.cpp in Sources */, 4CEC86A7204738EB009B37B1 /* TestPPC64InstEmulation.cpp in Sources */, + 58EAC73F2106A07B0029571E /* StreamTeeTest.cpp in Sources */, 23CB15381D66DA9300EDDDE1 /* PythonExceptionStateTests.cpp in Sources */, 9A3D43D81F3151C400EB767C /* NameMatchesTest.cpp in Sources */, 23CB15391D66DA9300EDDDE1 /* DataExtractorTest.cpp in Sources */, Index: lldb/trunk/unittests/Utility/CMakeLists.txt =================================================================== --- lldb/trunk/unittests/Utility/CMakeLists.txt +++ lldb/trunk/unittests/Utility/CMakeLists.txt @@ -13,6 +13,7 @@ LogTest.cpp NameMatchesTest.cpp StatusTest.cpp + StreamTeeTest.cpp StringExtractorTest.cpp StructuredDataTest.cpp TildeExpressionResolverTest.cpp Index: lldb/trunk/unittests/Utility/StreamTeeTest.cpp =================================================================== --- lldb/trunk/unittests/Utility/StreamTeeTest.cpp +++ lldb/trunk/unittests/Utility/StreamTeeTest.cpp @@ -0,0 +1,197 @@ +//===-- StreamTeeTest.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Utility/StreamTee.h" +#include "lldb/Utility/StreamString.h" +#include "gtest/gtest.h" + +using namespace lldb_private; + +TEST(StreamTeeTest, DefaultConstructor) { + // Test the default constructor. + StreamTee tee; + ASSERT_EQ(0U, tee.GetNumStreams()); +} + +TEST(StreamTeeTest, Constructor1Stream) { + // Test the constructor for a single stream. + lldb::StreamSP s1(std::make_shared()); + StreamTee tee(s1); + + ASSERT_EQ(1U, tee.GetNumStreams()); + EXPECT_EQ(s1, tee.GetStreamAtIndex(0U)); +} + +TEST(StreamTeeTest, Constructor2Streams) { + // Test the constructor for two streams. + lldb::StreamSP s1(std::make_shared()); + lldb::StreamSP s2(std::make_shared()); + StreamTee tee(s1, s2); + + ASSERT_EQ(2U, tee.GetNumStreams()); + EXPECT_EQ(s1, tee.GetStreamAtIndex(0U)); + EXPECT_EQ(s2, tee.GetStreamAtIndex(1U)); +} + +TEST(StreamTeeTest, CopyConstructor) { + // Test the copy constructor. + lldb::StreamSP s1(std::make_shared()); + lldb::StreamSP s2(std::make_shared()); + StreamTee tee1(s1, s2); + StreamTee tee2(tee1); + + ASSERT_EQ(2U, tee2.GetNumStreams()); + EXPECT_EQ(s1, tee2.GetStreamAtIndex(0U)); + EXPECT_EQ(s2, tee2.GetStreamAtIndex(1U)); +} + +TEST(StreamTeeTest, Assignment) { + // Test the assignment of StreamTee. + lldb::StreamSP s1(std::make_shared()); + lldb::StreamSP s2(std::make_shared()); + StreamTee tee1(s1, s2); + StreamTee tee2 = tee1; + + ASSERT_EQ(2U, tee2.GetNumStreams()); + EXPECT_EQ(s1, tee2.GetStreamAtIndex(0U)); + EXPECT_EQ(s2, tee2.GetStreamAtIndex(1U)); +} + +TEST(StreamTeeTest, Write) { + // Test that write is sent out to all children. + auto ss1 = new StreamString(); + auto ss2 = new StreamString(); + lldb::StreamSP s1(ss1); + lldb::StreamSP s2(ss2); + StreamTee tee(s1, s2); + + tee << "foo"; + tee.Flush(); + + ASSERT_EQ(2U, tee.GetNumStreams()); + EXPECT_EQ("foo", ss1->GetString().str()); + EXPECT_EQ("foo", ss2->GetString().str()); + + tee << "bar"; + tee.Flush(); + EXPECT_EQ("foobar", ss1->GetString().str()); + EXPECT_EQ("foobar", ss2->GetString().str()); +} + +namespace { + struct FlushTestStream : public Stream { + unsigned m_flush_count = false; + void Flush() override { + ++m_flush_count; + } + size_t Write(const void *src, size_t src_len) override { return src_len; } + }; +} + +TEST(StreamTeeTest, Flush) { + // Check that Flush is distributed to all streams. + auto fs1 = new FlushTestStream(); + auto fs2 = new FlushTestStream(); + lldb::StreamSP s1(fs1); + lldb::StreamSP s2(fs2); + StreamTee tee(s1, s2); + + tee << "foo"; + tee.Flush(); + + ASSERT_EQ(2U, tee.GetNumStreams()); + EXPECT_EQ(1U, fs1->m_flush_count); + EXPECT_EQ(1U, fs2->m_flush_count); + + tee << "bar"; + tee.Flush(); + EXPECT_EQ(2U, fs1->m_flush_count); + EXPECT_EQ(2U, fs2->m_flush_count); +} + +TEST(StreamTeeTest, AppendStream) { + // Append new streams to our StreamTee. + auto ss1 = new StreamString(); + auto ss2 = new StreamString(); + lldb::StreamSP s1(ss1); + lldb::StreamSP s2(ss2); + + StreamTee tee; + + ASSERT_EQ(0U, tee.GetNumStreams()); + + tee.AppendStream(s1); + ASSERT_EQ(1U, tee.GetNumStreams()); + EXPECT_EQ(s1, tee.GetStreamAtIndex(0U)); + + tee.AppendStream(s2); + ASSERT_EQ(2U, tee.GetNumStreams()); + EXPECT_EQ(s1, tee.GetStreamAtIndex(0U)); + EXPECT_EQ(s2, tee.GetStreamAtIndex(1U)); +} + +TEST(StreamTeeTest, GetStreamAtIndexOutOfBounds) { + // The index we check for is not in the bounds of the StreamTee. + lldb::StreamSP s1(std::make_shared()); + StreamTee tee(s1); + + ASSERT_EQ(1U, tee.GetNumStreams()); + EXPECT_EQ(lldb::StreamSP(), tee.GetStreamAtIndex(1)); +} + +TEST(StreamTeeTest, GetStreamAtIndexOutOfBoundsEmpty) { + // Same as above, but with an empty StreamTee. + StreamTee tee; + ASSERT_EQ(0U, tee.GetNumStreams()); + EXPECT_EQ(lldb::StreamSP(), tee.GetStreamAtIndex(0U)); + EXPECT_EQ(lldb::StreamSP(), tee.GetStreamAtIndex(1U)); +} + +TEST(StreamTeeTest, SetStreamAtIndexOverwrite) { + // We overwrite an existing stream at a given index. + lldb::StreamSP s1(std::make_shared()); + StreamTee tee(s1); + + ASSERT_EQ(1U, tee.GetNumStreams()); + EXPECT_EQ(s1, tee.GetStreamAtIndex(0U)); + EXPECT_EQ(lldb::StreamSP(), tee.GetStreamAtIndex(1U)); + + lldb::StreamSP s2(std::make_shared()); + tee.SetStreamAtIndex(0U, s2); + EXPECT_EQ(1U, tee.GetNumStreams()); + EXPECT_EQ(s2, tee.GetStreamAtIndex(0U)); + EXPECT_EQ(lldb::StreamSP(), tee.GetStreamAtIndex(1)); +} + +TEST(StreamTeeTest, SetStreamAtIndexOutOfBounds) { + // We place a new stream out of the bounds of the current StreamTee. + lldb::StreamSP s1(std::make_shared()); + StreamTee tee(s1); + + ASSERT_EQ(1U, tee.GetNumStreams()); + EXPECT_EQ(s1, tee.GetStreamAtIndex(0U)); + EXPECT_EQ(lldb::StreamSP(), tee.GetStreamAtIndex(1U)); + + // Place a new stream out of bounds of the current array. The StreamTee should + // resize itself until it can contain this index. + lldb::StreamSP s2(std::make_shared()); + tee.SetStreamAtIndex(4U, s2); + // Check that the vector has been resized. + EXPECT_EQ(5U, tee.GetNumStreams()); + // Is our stream at the right place? + EXPECT_EQ(s2, tee.GetStreamAtIndex(4U)); + + // Existing stream should still be there. + EXPECT_EQ(s1, tee.GetStreamAtIndex(0U)); + // Other elements are all invalid StreamSPs. + EXPECT_EQ(lldb::StreamSP(), tee.GetStreamAtIndex(1U)); + EXPECT_EQ(lldb::StreamSP(), tee.GetStreamAtIndex(2U)); + EXPECT_EQ(lldb::StreamSP(), tee.GetStreamAtIndex(3U)); + EXPECT_EQ(lldb::StreamSP(), tee.GetStreamAtIndex(5U)); +}