From 7d10ad262505cf58141134aff13b98583a8d5ebb Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 17 Sep 2015 22:51:58 +0200 Subject: [PATCH] CBuffer: add CMessage-based API --- Makefile.in | 2 +- include/znc/Buffer.h | 9 ++- include/znc/Chan.h | 2 + include/znc/IRCNetwork.h | 31 ++++++--- include/znc/Query.h | 2 + src/Buffer.cpp | 45 ++++++++----- src/Client.cpp | 4 +- src/IRCSock.cpp | 69 +++++++++++++++----- test/BufferTest.cpp | 132 +++++++++++++++++++++++++++++++++++++++ 9 files changed, 256 insertions(+), 40 deletions(-) create mode 100644 test/BufferTest.cpp diff --git a/Makefile.in b/Makefile.in index bd4d371a..f485a98e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -54,7 +54,7 @@ BIN_SRCS := src/main.cpp LIB_OBJS := $(patsubst %cpp,%o,$(LIB_SRCS)) BIN_OBJS := $(patsubst %cpp,%o,$(BIN_SRCS)) TESTS := StringTest ConfigTest UtilsTest ThreadTest NickTest ClientTest NetworkTest \ - MessageTest ModulesTest IRCSockTest QueryTest + MessageTest ModulesTest IRCSockTest QueryTest BufferTest TESTS := $(addprefix test/,$(addsuffix .o,$(TESTS))) CLEAN := znc src/*.o test/*.o core core.* .version_extra .depend modules/.depend \ unittest $(LIBZNC) diff --git a/include/znc/Buffer.h b/include/znc/Buffer.h index f1271d26..3755b775 100644 --- a/include/znc/Buffer.h +++ b/include/znc/Buffer.h @@ -31,13 +31,17 @@ class CBufLine { public: CBufLine() : CBufLine("") { throw 0; } // shouldn't be called, but is needed for compilation CBufLine(const CMessage& Format, const CString& sText = ""); + /// @deprecated CBufLine(const CString& sFormat, const CString& sText = "", const timeval* ts = nullptr, const MCString& mssTags = MCString::EmptyMap); ~CBufLine(); CMessage ToMessage(const CClient& Client, const MCString& mssParams) const; /// @deprecated Use ToMessage() instead CString GetLine(const CClient& Client, const MCString& mssParams) const; + /// @deprecated void UpdateTime(); + bool Equals(const CMessage& Format) const { return m_Message.Equals(Format); } + // Setters void SetFormat(const CString& sFormat) { m_Message.Parse(sFormat); } void SetText(const CString& sText) { m_sText = sText; } @@ -46,6 +50,7 @@ public: // !Setters // Getters + const CString& GetCommand() const { return m_Message.GetCommand(); } CString GetFormat() const { return m_Message.ToString(CMessage::ExcludeTags); } const CString& GetText() const { return m_sText; } timeval GetTime() const { return m_Message.GetTime(); } @@ -63,7 +68,9 @@ public: CBuffer(unsigned int uLineCount = 100); ~CBuffer(); - // TODO: CMessage-based API + size_type AddLine(const CMessage& Format, const CString& sText = ""); + size_type UpdateLine(const CString& sCommand, const CMessage& Format, const CString& sText = ""); + size_type UpdateExactLine(const CMessage& Format, const CString& sText = ""); size_type AddLine(const CString& sFormat, const CString& sText = "", const timeval* ts = nullptr, const MCString& mssTags = MCString::EmptyMap); /// Same as AddLine, but replaces a line whose format string starts with sMatch if there is one. diff --git a/include/znc/Chan.h b/include/znc/Chan.h index 033c5af0..b086cb2c 100644 --- a/include/znc/Chan.h +++ b/include/znc/Chan.h @@ -98,6 +98,8 @@ public: bool SetBufferCount(unsigned int u, bool bForce = false) { m_bHasBufferCountSet = true; return m_Buffer.SetLineCount(u, bForce); } void InheritBufferCount(unsigned int u, bool bForce = false) { if (!m_bHasBufferCountSet) m_Buffer.SetLineCount(u, bForce); } void ResetBufferCount(); + size_t AddBuffer(const CMessage& Format, const CString& sText = "") { return m_Buffer.AddLine(Format, sText); } + /// @deprecated size_t AddBuffer(const CString& sFormat, const CString& sText = "", const timeval* ts = nullptr, const MCString& mssTags = MCString::EmptyMap) { return m_Buffer.AddLine(sFormat, sText, ts, mssTags); } void ClearBuffer() { m_Buffer.Clear(); } void SendBuffer(CClient* pClient); diff --git a/include/znc/IRCNetwork.h b/include/znc/IRCNetwork.h index 0d0e18c8..32981af1 100644 --- a/include/znc/IRCNetwork.h +++ b/include/znc/IRCNetwork.h @@ -155,19 +155,36 @@ public: bool PutIRC(const CString& sLine); // Buffers - void AddRawBuffer(const CString& sFormat, const CString& sText = "") { m_RawBuffer.AddLine(sFormat, sText); } - void UpdateRawBuffer(const CString& sMatch, const CString& sFormat, const CString& sText = "") { m_RawBuffer.UpdateLine(sMatch, sFormat, sText); } - void UpdateExactRawBuffer(const CString& sFormat, const CString& sText = "") { m_RawBuffer.UpdateExactLine(sFormat, sText); } + void AddRawBuffer(const CMessage& Format, const CString& sText = "") { m_RawBuffer.AddLine(Format, sText); } + void UpdateRawBuffer(const CString& sCommand, const CMessage& Format, const CString& sText = "") { m_RawBuffer.UpdateLine(sCommand, Format, sText); } + void UpdateExactRawBuffer(const CMessage& Format, const CString& sText = "") { m_RawBuffer.UpdateExactLine(Format, sText); } void ClearRawBuffer() { m_RawBuffer.Clear(); } - void AddMotdBuffer(const CString& sFormat, const CString& sText = "") { m_MotdBuffer.AddLine(sFormat, sText); } - void UpdateMotdBuffer(const CString& sMatch, const CString& sFormat, const CString& sText = "") { m_MotdBuffer.UpdateLine(sMatch, sFormat, sText); } + /// @deprecated + void AddRawBuffer(const CString& sFormat, const CString& sText = "") { m_RawBuffer.AddLine(sFormat, sText); } + /// @deprecated + void UpdateRawBuffer(const CString& sMatch, const CString& sFormat, const CString& sText = "") { m_RawBuffer.UpdateLine(sMatch, sFormat, sText); } + /// @deprecated + void UpdateExactRawBuffer(const CString& sFormat, const CString& sText = "") { m_RawBuffer.UpdateExactLine(sFormat, sText); } + + void AddMotdBuffer(const CMessage& Format, const CString& sText = "") { m_MotdBuffer.AddLine(Format, sText); } + void UpdateMotdBuffer(const CString& sCommand, const CMessage& Format, const CString& sText = "") { m_MotdBuffer.UpdateLine(sCommand, Format, sText); } void ClearMotdBuffer() { m_MotdBuffer.Clear(); } - void AddNoticeBuffer(const CString& sFormat, const CString& sText = "") { m_NoticeBuffer.AddLine(sFormat, sText); } - void UpdateNoticeBuffer(const CString& sMatch, const CString& sFormat, const CString& sText = "") { m_NoticeBuffer.UpdateLine(sMatch, sFormat, sText); } + /// @deprecated + void AddMotdBuffer(const CString& sFormat, const CString& sText = "") { m_MotdBuffer.AddLine(sFormat, sText); } + /// @deprecated + void UpdateMotdBuffer(const CString& sMatch, const CString& sFormat, const CString& sText = "") { m_MotdBuffer.UpdateLine(sMatch, sFormat, sText); } + + void AddNoticeBuffer(const CMessage& Format, const CString& sText = "") { m_NoticeBuffer.AddLine(Format, sText); } + void UpdateNoticeBuffer(const CString& sCommand, const CMessage& Format, const CString& sText = "") { m_NoticeBuffer.UpdateLine(sCommand, Format, sText); } void ClearNoticeBuffer() { m_NoticeBuffer.Clear(); } + /// @deprecated + void AddNoticeBuffer(const CString& sFormat, const CString& sText = "") { m_NoticeBuffer.AddLine(sFormat, sText); } + /// @deprecated + void UpdateNoticeBuffer(const CString& sMatch, const CString& sFormat, const CString& sText = "") { m_NoticeBuffer.UpdateLine(sMatch, sFormat, sText); } + void ClearQueryBuffer(); // !Buffers diff --git a/include/znc/Query.h b/include/znc/Query.h index 8612cd88..ae63e332 100644 --- a/include/znc/Query.h +++ b/include/znc/Query.h @@ -38,6 +38,8 @@ public: const CBuffer& GetBuffer() const { return m_Buffer; } unsigned int GetBufferCount() const { return m_Buffer.GetLineCount(); } bool SetBufferCount(unsigned int u, bool bForce = false) { return m_Buffer.SetLineCount(u, bForce); } + size_t AddBuffer(const CMessage& Format, const CString& sText = "") { return m_Buffer.AddLine(Format, sText); } + /// @deprecated size_t AddBuffer(const CString& sFormat, const CString& sText = "", const timeval* ts = nullptr, const MCString& mssTags = MCString::EmptyMap) { return m_Buffer.AddLine(sFormat, sText, ts, mssTags); } void ClearBuffer() { m_Buffer.Clear(); } void SendBuffer(CClient* pClient); diff --git a/src/Buffer.cpp b/src/Buffer.cpp index 6c0ace84..34f69bb7 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -89,7 +89,7 @@ CBuffer::CBuffer(unsigned int uLineCount) : m_uLineCount(uLineCount) { CBuffer::~CBuffer() {} -CBuffer::size_type CBuffer::AddLine(const CString& sFormat, const CString& sText, const timeval* ts, const MCString& mssTags) { +CBuffer::size_type CBuffer::AddLine(const CMessage& Format, const CString& sText) { if (!m_uLineCount) { return 0; } @@ -98,31 +98,46 @@ CBuffer::size_type CBuffer::AddLine(const CString& sFormat, const CString& sText erase(begin()); } - push_back(CBufLine(sFormat, sText, ts, mssTags)); + push_back(CBufLine(Format, sText)); return size(); } -CBuffer::size_type CBuffer::UpdateLine(const CString& sMatch, const CString& sFormat, const CString& sText) { +CBuffer::size_type CBuffer::UpdateLine(const CString& sCommand, const CMessage& Format, const CString& sText) { for (CBufLine& Line : *this) { - if (Line.GetFormat().compare(0, sMatch.length(), sMatch) == 0) { - Line.SetFormat(sFormat); - Line.SetText(sText); - Line.UpdateTime(); + if (Line.GetCommand().Equals(sCommand)) { + Line = CBufLine(Format, sText); return size(); } } - return AddLine(sFormat, sText); + return AddLine(Format, sText); +} + +CBuffer::size_type CBuffer::UpdateExactLine(const CMessage& Format, const CString& sText) { + for (CBufLine& Line : *this) { + if (Line.Equals(Format)) { + return size(); + } + } + + return AddLine(Format, sText); +} + +CBuffer::size_type CBuffer::AddLine(const CString& sFormat, const CString& sText, const timeval* ts, const MCString& mssTags) { + CMessage Message(sFormat); + if (ts) { + Message.SetTime(*ts); + } + Message.SetTags(mssTags); + return AddLine(Message, sText); +} + +CBuffer::size_type CBuffer::UpdateLine(const CString& sMatch, const CString& sFormat, const CString& sText) { + return UpdateLine(CMessage(sMatch).GetCommand(), CMessage(sFormat), sText); } CBuffer::size_type CBuffer::UpdateExactLine(const CString& sFormat, const CString& sText) { - for (const CBufLine& Line : *this) { - if (Line.GetFormat() == sFormat && Line.GetText() == sText) { - return size(); - } - } - - return AddLine(sFormat, sText); + return UpdateExactLine(CMessage(sFormat, sText)); } const CBufLine& CBuffer::GetBufLine(unsigned int uIdx) const { diff --git a/src/Client.cpp b/src/Client.cpp index 2e0220bc..4cb1be80 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -1104,13 +1104,13 @@ void CClient::AddBuffer(const T& Message) if (m_pNetwork->IsChan(sTarget)) { CChan* pChan = m_pNetwork->FindChan(sTarget); if (pChan && (!pChan->AutoClearChanBuffer() || !m_pNetwork->IsUserOnline())) { - pChan->AddBuffer(Format.ToString(CMessage::ExcludeTags), Message.GetText(), &Message.GetTime(), Message.GetTags()); + pChan->AddBuffer(Format, Message.GetText()); } } else if (Message.GetType() != CMessage::Type::Notice) { if (!m_pUser->AutoClearQueryBuffer() || !m_pNetwork->IsUserOnline()) { CQuery* pQuery = m_pNetwork->AddQuery(sTarget); if (pQuery) { - pQuery->AddBuffer(Format.ToString(CMessage::ExcludeTags), Message.GetText(), &Message.GetTime(), Message.GetTags()); + pQuery->AddBuffer(Format, Message.GetText()); } } } diff --git a/src/IRCSock.cpp b/src/IRCSock.cpp index 18935b8e..191805e4 100644 --- a/src/IRCSock.cpp +++ b/src/IRCSock.cpp @@ -297,7 +297,12 @@ bool CIRCSock::OnActionMessage(CActionMessage& Message) { const CNick& Nick = Message.GetNick(); CQuery* pQuery = m_pNetwork->AddQuery(Nick.GetNick()); if (pQuery) { - pQuery->AddBuffer(":" + _NAMEDFMT(Nick.GetNickMask()) + " PRIVMSG {target} :\001ACTION {text}\001", Message.GetText(), &Message.GetTime(), Message.GetTags()); + CActionMessage Format; + Format.Clone(Message); + Format.SetNick(_NAMEDFMT(Nick.GetNickMask())); + Format.SetTarget("{target}"); + Format.SetText("{text}"); + pQuery->AddBuffer(Format, Message.GetText()); } } } else { @@ -309,7 +314,12 @@ bool CIRCSock::OnActionMessage(CActionMessage& Message) { if (bResult) return true; if (!pChan->AutoClearChanBuffer() || !m_pNetwork->IsUserOnline() || pChan->IsDetached()) { - pChan->AddBuffer(":" + _NAMEDFMT(Message.GetNick().GetNickMask()) + " PRIVMSG " + _NAMEDFMT(pChan->GetName()) + " :\001ACTION {text}\001", Message.GetText(), &Message.GetTime(), Message.GetTags()); + CActionMessage Format; + Format.Clone(Message); + Format.SetNick(_NAMEDFMT(Message.GetNick().GetNickMask())); + Format.SetTarget(_NAMEDFMT(pChan->GetName())); + Format.SetText("{text}"); + pChan->AddBuffer(Format, Message.GetText()); } } } @@ -599,7 +609,12 @@ bool CIRCSock::OnNoticeMessage(CNoticeMessage& Message) { if (!m_pNetwork->IsUserOnline()) { // If the user is detached, add to the buffer - m_pNetwork->AddNoticeBuffer(":" + _NAMEDFMT(Message.GetNick().GetNickMask()) + " NOTICE {target} :{text}", Message.GetText()); + CNoticeMessage Format; + Format.Clone(Message); + Format.SetNick(CNick(_NAMEDFMT(Message.GetNick().GetNickMask()))); + Format.SetTarget("{target}"); + Format.SetText("{text}"); + m_pNetwork->AddNoticeBuffer(Format, Message.GetText()); } return false; @@ -612,7 +627,12 @@ bool CIRCSock::OnNoticeMessage(CNoticeMessage& Message) { if (bResult) return true; if (!pChan->AutoClearChanBuffer() || !m_pNetwork->IsUserOnline() || pChan->IsDetached()) { - pChan->AddBuffer(":" + _NAMEDFMT(Message.GetNick().GetNickMask()) + " NOTICE " + _NAMEDFMT(pChan->GetName()) + " :{text}", Message.GetText(), &Message.GetTime(), Message.GetTags()); + CNoticeMessage Format; + Format.Clone(Message); + Format.SetNick(_NAMEDFMT(Message.GetNick().GetNickMask())); + Format.SetTarget(_NAMEDFMT(pChan->GetName())); + Format.SetText("{text}"); + pChan->AddBuffer(Format, Message.GetText()); } } @@ -620,13 +640,22 @@ bool CIRCSock::OnNoticeMessage(CNoticeMessage& Message) { } } +static CMessage BufferMessage(const CNumericMessage& Message) { + CMessage Format(Message); + Format.SetNick(CNick(_NAMEDFMT(Message.GetNick().GetHostMask()))); + Format.SetParam(0, "{target}"); + unsigned uParams = Format.GetParams().size(); + for (unsigned int i = 1; i < uParams; ++i) { + Format.SetParam(i, _NAMEDFMT(Format.GetParam(i))); + } + return Format; +} + bool CIRCSock::OnNumericMessage(CNumericMessage& Message) { const CString& sCmd = Message.GetCommand(); CString sServer = Message.GetNick().GetHostMask(); unsigned int uRaw = Message.GetCode(); CString sNick = Message.GetParam(0); - CString sRest = Message.GetParams(1); - CString sTmp; bool bResult = false; IRCSOCKMODULECALL(OnNumericMessage(Message), &bResult); @@ -664,7 +693,7 @@ bool CIRCSock::OnNumericMessage(CNumericMessage& Message) { IRCSOCKMODULECALL(OnIRCConnected(), NOTHING); m_pNetwork->ClearRawBuffer(); - m_pNetwork->AddRawBuffer(":" + _NAMEDFMT(sServer) + " " + sCmd + " {target} " + _NAMEDFMT(sRest)); + m_pNetwork->AddRawBuffer(BufferMessage(Message)); m_pNetwork->IRCConnected(); @@ -672,7 +701,7 @@ bool CIRCSock::OnNumericMessage(CNumericMessage& Message) { } case 5: ParseISupport(Message); - m_pNetwork->UpdateExactRawBuffer(":" + _NAMEDFMT(sServer) + " " + sCmd + " {target} " + _NAMEDFMT(sRest)); + m_pNetwork->UpdateExactRawBuffer(BufferMessage(Message)); break; case 10: { // :irc.server.com 010 nick : CString sHost = Message.GetParam(1); @@ -694,8 +723,7 @@ bool CIRCSock::OnNumericMessage(CNumericMessage& Message) { case 255: // client count case 265: // local users case 266: // global users - sTmp = ":" + _NAMEDFMT(sServer) + " " + sCmd; - m_pNetwork->UpdateRawBuffer(sTmp, sTmp + " {target} " + _NAMEDFMT(sRest)); + m_pNetwork->UpdateRawBuffer(sCmd, BufferMessage(Message)); break; case 305: m_pNetwork->SetIRCAway(false); @@ -869,7 +897,7 @@ bool CIRCSock::OnNumericMessage(CNumericMessage& Message) { case 372: // motd case 376: // end motd if (m_pNetwork->GetIRCServer().Equals(sServer)) { - m_pNetwork->AddMotdBuffer(":" + _NAMEDFMT(sServer) + " " + sCmd + " {target} " + _NAMEDFMT(sRest)); + m_pNetwork->AddMotdBuffer(BufferMessage(Message)); } break; case 437: @@ -1008,7 +1036,12 @@ bool CIRCSock::OnTextMessage(CTextMessage& Message) { const CNick& Nick = Message.GetNick(); CQuery* pQuery = m_pNetwork->AddQuery(Nick.GetNick()); if (pQuery) { - pQuery->AddBuffer(":" + _NAMEDFMT(Nick.GetNickMask()) + " PRIVMSG {target} :{text}", Message.GetText(), &Message.GetTime(), Message.GetTags()); + CTextMessage Format; + Format.Clone(Message); + Format.SetNick(_NAMEDFMT(Nick.GetNickMask())); + Format.SetTarget("{target}"); + Format.SetText("{text}"); + pQuery->AddBuffer(Format, Message.GetText()); } } } else { @@ -1020,7 +1053,12 @@ bool CIRCSock::OnTextMessage(CTextMessage& Message) { if (bResult) return true; if (!pChan->AutoClearChanBuffer() || !m_pNetwork->IsUserOnline() || pChan->IsDetached()) { - pChan->AddBuffer(":" + _NAMEDFMT(Message.GetNick().GetNickMask()) + " PRIVMSG " + _NAMEDFMT(pChan->GetName()) + " :{text}", Message.GetText(), &Message.GetTime(), Message.GetTags()); + CTextMessage Format; + Format.Clone(Message); + Format.SetNick(_NAMEDFMT(Message.GetNick().GetNickMask())); + Format.SetTarget(_NAMEDFMT(pChan->GetName())); + Format.SetText("{text}"); + pChan->AddBuffer(Format, Message.GetText()); } } } @@ -1051,7 +1089,10 @@ bool CIRCSock::OnWallopsMessage(CMessage& Message) { CString sMsg = Message.GetParam(0); if (!m_pNetwork->IsUserOnline()) { - m_pNetwork->AddNoticeBuffer(":" + _NAMEDFMT(Message.GetNick().GetNickMask()) + " WALLOPS :{text}", sMsg); + CMessage Format(Message); + Format.SetNick(CNick(_NAMEDFMT(Message.GetNick().GetHostMask()))); + Format.SetParam(0, "{text}"); + m_pNetwork->AddNoticeBuffer(Format, sMsg); } return false; } diff --git a/test/BufferTest.cpp b/test/BufferTest.cpp new file mode 100644 index 00000000..990ae7cb --- /dev/null +++ b/test/BufferTest.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2004-2015 ZNC, see the NOTICE file for details. + * + * 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 +#include +#include +#include + +using ::testing::SizeIs; +using ::testing::ContainerEq; + +class BufferTest : public ::testing::Test { +protected: + void SetUp() { CZNC::CreateInstance(); } + void TearDown() { CZNC::DestroyInstance(); } +}; + +TEST_F(BufferTest, BufLine) { + CBuffer buffer(1); + buffer.AddLine(CMessage("@key=value :nick PRIVMSG {target} {text}"), "hello there"); + const CBufLine& line = buffer.GetBufLine(0); + EXPECT_THAT(line.GetTags(), ContainerEq(MCString{{"key","value"}})); + EXPECT_EQ(":nick PRIVMSG {target} {text}", line.GetFormat()); + EXPECT_EQ("hello there", line.GetText()); + EXPECT_EQ("PRIVMSG", line.GetCommand()); +} + +TEST_F(BufferTest, LineCount) { + CBuffer buffer(123); + EXPECT_EQ(123u, buffer.GetLineCount()); + + EXPECT_EQ(500u, CZNC::Get().GetMaxBufferSize()); + EXPECT_FALSE(buffer.SetLineCount(1000, false)); + EXPECT_EQ(123u, buffer.GetLineCount()); + EXPECT_TRUE(buffer.SetLineCount(500, false)); + EXPECT_EQ(500u, buffer.GetLineCount()); + EXPECT_TRUE(buffer.SetLineCount(1000, true)); + EXPECT_EQ(1000u, buffer.GetLineCount()); +} + +TEST_F(BufferTest, AddLine) { + CBuffer buffer(2); + EXPECT_TRUE(buffer.IsEmpty()); + EXPECT_EQ(0u, buffer.Size()); + + EXPECT_EQ(1u, buffer.AddLine(CMessage("PRIVMSG nick :msg1"))); + EXPECT_FALSE(buffer.IsEmpty()); + EXPECT_EQ(1u, buffer.Size()); + EXPECT_EQ("PRIVMSG nick :msg1", buffer.GetBufLine(0).GetFormat()); + + EXPECT_EQ(2u, buffer.AddLine(CMessage("PRIVMSG nick :msg2"))); + EXPECT_FALSE(buffer.IsEmpty()); + EXPECT_EQ(2u, buffer.Size()); + EXPECT_EQ("PRIVMSG nick :msg1", buffer.GetBufLine(0).GetFormat()); + EXPECT_EQ("PRIVMSG nick :msg2", buffer.GetBufLine(1).GetFormat()); + + EXPECT_EQ(2u, buffer.AddLine(CMessage("PRIVMSG nick :msg3"))); + EXPECT_FALSE(buffer.IsEmpty()); + EXPECT_EQ(2u, buffer.Size()); + EXPECT_EQ("PRIVMSG nick :msg2", buffer.GetBufLine(0).GetFormat()); + EXPECT_EQ("PRIVMSG nick :msg3", buffer.GetBufLine(1).GetFormat()); + + buffer.SetLineCount(1); + EXPECT_FALSE(buffer.IsEmpty()); + EXPECT_EQ(1u, buffer.Size()); + EXPECT_EQ("PRIVMSG nick :msg3", buffer.GetBufLine(0).GetFormat()); + + buffer.Clear(); + EXPECT_TRUE(buffer.IsEmpty()); + EXPECT_EQ(0u, buffer.Size()); + + buffer.SetLineCount(0); + buffer.AddLine(CMessage("TEST")); + EXPECT_TRUE(buffer.IsEmpty()); + EXPECT_EQ(0u, buffer.Size()); +} + +TEST_F(BufferTest, UpdateLine) { + CBuffer buffer(50); + + VCString lines = { + ":irc.server.com 001 nick :Welcome to the fake Internet Relay Chat Network nick", + ":irc.server.com 002 nick :Your host is irc.server.com[12.34.56.78/6697], running version ircd-fake-1.2.3", + ":irc.server.com 003 nick :This server was created Mon Jul 6 2015 at 16:53:49 UTC", + ":irc.server.com 004 nick irc.server.com ircd-fake-1.2.3 DOQRSZaghilopswz CFILMPQSbcefgijklmnopqrstvz bkloveqjfI", + ":irc.server.com 005 nick CHANTYPES=# EXCEPTS INVEX CHANMODES=eIbq,k,flj,CFLMPQScgimnprstz CHANLIMIT=#:120 PREFIX=(ov)@+ MAXLIST=bqeI:100 MODES=4 NETWORK=fake KNOCK STATUSMSG=@+ CALLERID=g :are supported by this server", + ":irc.server.com 005 nick CASEMAPPING=rfc1459 CHARSET=ascii NICKLEN=16 CHANNELLEN=50 TOPICLEN=390 ETRACE CPRIVMSG CNOTICE DEAF=D MONITOR=100 FNC TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: :are supported by this server", + ":irc.server.com 005 nick EXTBAN=$,ajrxz WHOX CLIENTVER=3.0 SAFELIST ELIST=CTU :are supported by this server", + ":irc.server.com 251 nick :There are 160 users and 92457 invisible on 26 servers", + ":irc.server.com 252 nick 25 :IRC Operators online", + ":irc.server.com 253 nick 10 :unknown connection(s)", + ":irc.server.com 254 nick 63434 :channels formed", + ":irc.server.com 255 nick :I have 9455 clients and 1 servers", + ":irc.server.com 265 nick 9455 9682 :Current local users 9455, max 9682", + ":irc.server.com 266 nick 92617 96692 :Current global users 92617, max 96692", + ":irc.server.com 250 nick :Highest connection count: 9683 (9682 clients) (854623 connections received)" + }; + EXPECT_THAT(lines, SizeIs(15)); + + for (const CString& line : lines) { + buffer.AddLine(line); + } + EXPECT_EQ(15u, buffer.Size()); + + EXPECT_EQ(15u, buffer.UpdateLine("002", CMessage(":irc.server.net 002 nick :Your host is irc.server.net[11.22.33.44/6697], running version ircd-fake-3.2.1"))); + EXPECT_EQ(":irc.server.net 002 nick :Your host is irc.server.net[11.22.33.44/6697], running version ircd-fake-3.2.1", buffer.GetBufLine(1).GetFormat()); + + EXPECT_EQ(15u, buffer.UpdateLine("252", CMessage(":irc.server.com 252 nick 100 :IRC Operators online"))); + EXPECT_EQ(":irc.server.com 252 nick 100 :IRC Operators online", buffer.GetBufLine(8).GetFormat()); + + EXPECT_EQ(16u, buffer.UpdateLine("123", CMessage(":irc.server.com 123 nick foo bar"))); + EXPECT_EQ(":irc.server.com 123 nick foo bar", buffer.GetBufLine(15).GetFormat()); + + EXPECT_EQ(16u, buffer.UpdateExactLine(CMessage(":irc.server.com 005 nick EXTBAN=$,ajrxz WHOX CLIENTVER=3.0 SAFELIST ELIST=CTU :are supported by this server"))); + EXPECT_EQ(":irc.server.com 005 nick EXTBAN=$,ajrxz WHOX CLIENTVER=3.0 SAFELIST ELIST=CTU :are supported by this server", buffer.GetBufLine(6).GetFormat()); + + EXPECT_EQ(17u, buffer.UpdateExactLine(CMessage(":irc.server.com 005 nick FOO=bar :are supported by this server"))); + EXPECT_EQ(":irc.server.com 005 nick FOO=bar :are supported by this server", buffer.GetBufLine(16).GetFormat()); +}