| Index: third_party/WebKit/Source/core/page/EventSourceParserTest.cpp
|
| diff --git a/third_party/WebKit/Source/core/page/EventSourceParserTest.cpp b/third_party/WebKit/Source/core/page/EventSourceParserTest.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a175bd9f6d52cf9ad771575e1c02a28d7c01e8ad
|
| --- /dev/null
|
| +++ b/third_party/WebKit/Source/core/page/EventSourceParserTest.cpp
|
| @@ -0,0 +1,381 @@
|
| +// Copyright 2016 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "core/page/EventSourceParser.h"
|
| +
|
| +#include "core/page/EventSource.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +#include "wtf/text/CharacterNames.h"
|
| +
|
| +#include <string.h>
|
| +
|
| +namespace blink {
|
| +
|
| +namespace {
|
| +
|
| +struct EventOrReconnectionTimeSetting {
|
| + enum Type {
|
| + Event,
|
| + ReconnectionTimeSetting,
|
| + };
|
| +
|
| + EventOrReconnectionTimeSetting(const AtomicString& event, const String& data, const AtomicString& id)
|
| + : type(Event)
|
| + , event(event)
|
| + , data(data)
|
| + , id(id)
|
| + , reconnectionTime(0)
|
| + {
|
| + }
|
| + explicit EventOrReconnectionTimeSetting(unsigned long long reconnectionTime)
|
| + : type(ReconnectionTimeSetting)
|
| + , reconnectionTime(reconnectionTime)
|
| + {
|
| + }
|
| +
|
| + const Type type;
|
| + const AtomicString event;
|
| + const String data;
|
| + const AtomicString id;
|
| + const unsigned long long reconnectionTime;
|
| +};
|
| +
|
| +class Client : public GarbageCollectedFinalized<Client>, public EventSourceParser::Client {
|
| + USING_GARBAGE_COLLECTED_MIXIN(Client);
|
| +public:
|
| + ~Client() override {}
|
| + const Vector<EventOrReconnectionTimeSetting>& events() const { return m_events; }
|
| + void onMessageEvent(const AtomicString& event, const String& data, const AtomicString& id) override
|
| + {
|
| + m_events.append(EventOrReconnectionTimeSetting(event, data, id));
|
| + }
|
| + void onReconnectionTimeSet(unsigned long long reconnectionTime) override
|
| + {
|
| + m_events.append(EventOrReconnectionTimeSetting(reconnectionTime));
|
| + }
|
| +
|
| +private:
|
| + Vector<EventOrReconnectionTimeSetting> m_events;
|
| +};
|
| +
|
| +class StoppingClient : public GarbageCollectedFinalized<Client>, public EventSourceParser::Client {
|
| + USING_GARBAGE_COLLECTED_MIXIN(StoppingClient);
|
| +public:
|
| + ~StoppingClient() override {}
|
| + const Vector<EventOrReconnectionTimeSetting>& events() const { return m_events; }
|
| + void setParser(EventSourceParser* parser) { m_parser = parser; }
|
| + void onMessageEvent(const AtomicString& event, const String& data, const AtomicString& id) override
|
| + {
|
| + m_parser->stop();
|
| + m_events.append(EventOrReconnectionTimeSetting(event, data, id));
|
| + }
|
| + void onReconnectionTimeSet(unsigned long long reconnectionTime) override
|
| + {
|
| + m_events.append(EventOrReconnectionTimeSetting(reconnectionTime));
|
| + }
|
| +
|
| + DEFINE_INLINE_VIRTUAL_TRACE()
|
| + {
|
| + visitor->trace(m_parser);
|
| + EventSourceParser::Client::trace(visitor);
|
| + }
|
| +
|
| +private:
|
| + Member<EventSourceParser> m_parser;
|
| + Vector<EventOrReconnectionTimeSetting> m_events;
|
| +};
|
| +
|
| +class EventSourceParserTest : public ::testing::Test {
|
| +protected:
|
| + using Type = EventOrReconnectionTimeSetting::Type;
|
| + EventSourceParserTest()
|
| + : m_client(new Client)
|
| + , m_parser(new EventSourceParser(AtomicString(), m_client))
|
| + {
|
| + }
|
| + ~EventSourceParserTest() override {}
|
| +
|
| + void enqueue(const char* data) { m_parser->addBytes(data, strlen(data)); }
|
| + void enqueueOneByOne(const char* data)
|
| + {
|
| + const char*p = data;
|
| + while (*p != '\0')
|
| + m_parser->addBytes(p++, 1);
|
| + }
|
| +
|
| + const Vector<EventOrReconnectionTimeSetting>& events() { return m_client->events(); }
|
| +
|
| + EventSourceParser* parser() { return m_parser; }
|
| +
|
| + Persistent<Client> m_client;
|
| + Persistent<EventSourceParser> m_parser;
|
| +};
|
| +
|
| +TEST_F(EventSourceParserTest, EmptyMessageEventShouldNotBeDispatched)
|
| +{
|
| + enqueue("\n");
|
| +
|
| + EXPECT_EQ(0u, events().size());
|
| + EXPECT_EQ(String(), parser()->lastEventId());
|
| +}
|
| +
|
| +TEST_F(EventSourceParserTest, DispatchSimpleMessageEvent)
|
| +{
|
| + enqueue("data:hello\n\n");
|
| +
|
| + ASSERT_EQ(1u, events().size());
|
| + ASSERT_EQ(Type::Event, events()[0].type);
|
| + EXPECT_EQ("message", events()[0].event);
|
| + EXPECT_EQ("hello", events()[0].data);
|
| + EXPECT_EQ(String(), events()[0].id);
|
| + EXPECT_EQ(AtomicString(), parser()->lastEventId());
|
| +}
|
| +
|
| +TEST_F(EventSourceParserTest, ConstructWithLastEventId)
|
| +{
|
| + m_parser = new EventSourceParser("hoge", m_client);
|
| + EXPECT_EQ("hoge", parser()->lastEventId());
|
| +
|
| + enqueue("data:hello\n\n");
|
| +
|
| + ASSERT_EQ(1u, events().size());
|
| + ASSERT_EQ(Type::Event, events()[0].type);
|
| + EXPECT_EQ("message", events()[0].event);
|
| + EXPECT_EQ("hello", events()[0].data);
|
| + EXPECT_EQ("hoge", events()[0].id);
|
| + EXPECT_EQ("hoge", parser()->lastEventId());
|
| +}
|
| +
|
| +TEST_F(EventSourceParserTest, DispatchMessageEventWithLastEventId)
|
| +{
|
| + enqueue("id:99\ndata:hello\n");
|
| + EXPECT_EQ(String(), parser()->lastEventId());
|
| +
|
| + enqueue("\n");
|
| +
|
| + ASSERT_EQ(1u, events().size());
|
| + ASSERT_EQ(Type::Event, events()[0].type);
|
| + EXPECT_EQ("message", events()[0].event);
|
| + EXPECT_EQ("hello", events()[0].data);
|
| + EXPECT_EQ("99", events()[0].id);
|
| + EXPECT_EQ("99", parser()->lastEventId());
|
| +}
|
| +
|
| +TEST_F(EventSourceParserTest, DispatchMessageEventWithCustomEventType)
|
| +{
|
| + enqueue("event:foo\ndata:hello\n\n");
|
| +
|
| + ASSERT_EQ(1u, events().size());
|
| + ASSERT_EQ(Type::Event, events()[0].type);
|
| + EXPECT_EQ("foo", events()[0].event);
|
| + EXPECT_EQ("hello", events()[0].data);
|
| +}
|
| +
|
| +TEST_F(EventSourceParserTest, RetryTakesEffectEvenWhenNotDispatching)
|
| +{
|
| + enqueue("retry:999\n");
|
| + ASSERT_EQ(1u, events().size());
|
| + ASSERT_EQ(Type::ReconnectionTimeSetting, events()[0].type);
|
| + ASSERT_EQ(999u, events()[0].reconnectionTime);
|
| +}
|
| +
|
| +TEST_F(EventSourceParserTest, EventTypeShouldBeReset)
|
| +{
|
| + enqueue("event:foo\ndata:hello\n\ndata:bye\n\n");
|
| +
|
| + ASSERT_EQ(2u, events().size());
|
| + ASSERT_EQ(Type::Event, events()[0].type);
|
| + EXPECT_EQ("foo", events()[0].event);
|
| + EXPECT_EQ("hello", events()[0].data);
|
| +
|
| + ASSERT_EQ(Type::Event, events()[1].type);
|
| + EXPECT_EQ("message", events()[1].event);
|
| + EXPECT_EQ("bye", events()[1].data);
|
| +}
|
| +
|
| +TEST_F(EventSourceParserTest, DataShouldBeReset)
|
| +{
|
| + enqueue("data:hello\n\n\n");
|
| +
|
| + ASSERT_EQ(1u, events().size());
|
| + ASSERT_EQ(Type::Event, events()[0].type);
|
| + EXPECT_EQ("message", events()[0].event);
|
| + EXPECT_EQ("hello", events()[0].data);
|
| +}
|
| +
|
| +TEST_F(EventSourceParserTest, LastEventIdShouldNotBeReset)
|
| +{
|
| + enqueue("id:99\ndata:hello\n\ndata:bye\n\n");
|
| +
|
| + EXPECT_EQ("99", parser()->lastEventId());
|
| + ASSERT_EQ(2u, events().size());
|
| + ASSERT_EQ(Type::Event, events()[0].type);
|
| + EXPECT_EQ("message", events()[0].event);
|
| + EXPECT_EQ("hello", events()[0].data);
|
| + EXPECT_EQ("99", events()[0].id);
|
| +
|
| + ASSERT_EQ(Type::Event, events()[1].type);
|
| + EXPECT_EQ("message", events()[1].event);
|
| + EXPECT_EQ("bye", events()[1].data);
|
| + EXPECT_EQ("99", events()[1].id);
|
| +}
|
| +
|
| +TEST_F(EventSourceParserTest, VariousNewLinesShouldBeAllowed)
|
| +{
|
| + enqueueOneByOne("data:hello\r\n\rdata:bye\r\r");
|
| +
|
| + ASSERT_EQ(2u, events().size());
|
| + ASSERT_EQ(Type::Event, events()[0].type);
|
| + EXPECT_EQ("message", events()[0].event);
|
| + EXPECT_EQ("hello", events()[0].data);
|
| +
|
| + ASSERT_EQ(Type::Event, events()[1].type);
|
| + EXPECT_EQ("message", events()[1].event);
|
| + EXPECT_EQ("bye", events()[1].data);
|
| +}
|
| +
|
| +TEST_F(EventSourceParserTest, RetryWithEmptyValueShouldRestoreDefaultValue)
|
| +{
|
| + // TODO(yhirano): This is unspecified in the spec. We need to update
|
| + // the implementation or the spec.
|
| + enqueue("retry\n");
|
| + ASSERT_EQ(1u, events().size());
|
| + ASSERT_EQ(Type::ReconnectionTimeSetting, events()[0].type);
|
| + EXPECT_EQ(EventSource::defaultReconnectDelay, events()[0].reconnectionTime);
|
| +}
|
| +
|
| +TEST_F(EventSourceParserTest, NonDigitRetryShouldBeIgnored)
|
| +{
|
| + enqueue("retry:a0\n");
|
| + enqueue("retry:xi\n");
|
| + enqueue("retry:2a\n");
|
| + enqueue("retry:09a\n");
|
| + enqueue("retry:1\b\n");
|
| + enqueue("retry: 1234\n");
|
| + enqueue("retry:456 \n");
|
| +
|
| + EXPECT_EQ(0u, events().size());
|
| +}
|
| +
|
| +TEST_F(EventSourceParserTest, UnrecognizedFieldShouldBeIgnored)
|
| +{
|
| + enqueue("data:hello\nhoge:fuga\npiyo\n\n");
|
| +
|
| + ASSERT_EQ(1u, events().size());
|
| + ASSERT_EQ(Type::Event, events()[0].type);
|
| + EXPECT_EQ("message", events()[0].event);
|
| + EXPECT_EQ("hello", events()[0].data);
|
| +}
|
| +
|
| +TEST_F(EventSourceParserTest, CommentShouldBeIgnored)
|
| +{
|
| + enqueue("data:hello\n:event:a\n\n");
|
| +
|
| + ASSERT_EQ(1u, events().size());
|
| + ASSERT_EQ(Type::Event, events()[0].type);
|
| + EXPECT_EQ("message", events()[0].event);
|
| + EXPECT_EQ("hello", events()[0].data);
|
| +}
|
| +
|
| +TEST_F(EventSourceParserTest, BOMShouldBeIgnored)
|
| +{
|
| + // This line is recognized because "\xef\xbb\xbf" is a BOM.
|
| + enqueue("\xef\xbb\xbf" "data:hello\n");
|
| + // This line is ignored because "\xef\xbb\xbf" is part of the field name.
|
| + enqueue("\xef\xbb\xbf" "data:bye\n");
|
| + enqueue("\n");
|
| +
|
| + ASSERT_EQ(1u, events().size());
|
| + ASSERT_EQ(Type::Event, events()[0].type);
|
| + EXPECT_EQ("message", events()[0].event);
|
| + EXPECT_EQ("hello", events()[0].data);
|
| +}
|
| +
|
| +TEST_F(EventSourceParserTest, ColonlessLineShouldBeTreatedAsNameOnlyField)
|
| +{
|
| + enqueue("data:hello\nevent:a\nevent\n\n");
|
| +
|
| + ASSERT_EQ(1u, events().size());
|
| + ASSERT_EQ(Type::Event, events()[0].type);
|
| + EXPECT_EQ("message", events()[0].event);
|
| + EXPECT_EQ("hello", events()[0].data);
|
| +}
|
| +
|
| +TEST_F(EventSourceParserTest, AtMostOneLeadingSpaceCanBeSkipped)
|
| +{
|
| + enqueue("data: hello \nevent: type \n\n");
|
| +
|
| + ASSERT_EQ(1u, events().size());
|
| + ASSERT_EQ(Type::Event, events()[0].type);
|
| + EXPECT_EQ(" type ", events()[0].event);
|
| + EXPECT_EQ(" hello ", events()[0].data);
|
| +}
|
| +
|
| +TEST_F(EventSourceParserTest, DataShouldAccumulate)
|
| +{
|
| + enqueue("data\ndata:hello\ndata: world\ndata\n\n");
|
| +
|
| + ASSERT_EQ(1u, events().size());
|
| + ASSERT_EQ(Type::Event, events()[0].type);
|
| + EXPECT_EQ("message", events()[0].event);
|
| + EXPECT_EQ("\nhello\nworld\n", events()[0].data);
|
| +}
|
| +
|
| +TEST_F(EventSourceParserTest, EventShouldNotAccumulate)
|
| +{
|
| + enqueue("data:hello\nevent:a\nevent:b\n\n");
|
| +
|
| + ASSERT_EQ(1u, events().size());
|
| + ASSERT_EQ(Type::Event, events()[0].type);
|
| + EXPECT_EQ("b", events()[0].event);
|
| + EXPECT_EQ("hello", events()[0].data);
|
| +}
|
| +
|
| +TEST_F(EventSourceParserTest, FeedDataOneByOne)
|
| +{
|
| + enqueueOneByOne("data:hello\r\ndata:world\revent:a\revent:b\nid:4\n\nid:8\ndata:bye\r\n\r");
|
| +
|
| + ASSERT_EQ(2u, events().size());
|
| + ASSERT_EQ(Type::Event, events()[0].type);
|
| + EXPECT_EQ("b", events()[0].event);
|
| + EXPECT_EQ("hello\nworld", events()[0].data);
|
| + EXPECT_EQ("4", events()[0].id);
|
| +
|
| + ASSERT_EQ(Type::Event, events()[1].type);
|
| + EXPECT_EQ("message", events()[1].event);
|
| + EXPECT_EQ("bye", events()[1].data);
|
| + EXPECT_EQ("8", events()[1].id);
|
| +}
|
| +
|
| +TEST_F(EventSourceParserTest, InvalidUTF8Sequence)
|
| +{
|
| + enqueue("data:\xffhello\xc2\n\n");
|
| +
|
| + ASSERT_EQ(1u, events().size());
|
| + ASSERT_EQ(Type::Event, events()[0].type);
|
| + EXPECT_EQ("message", events()[0].event);
|
| + String expected = String() + replacementCharacter + "hello" + replacementCharacter;
|
| + EXPECT_EQ(expected, events()[0].data);
|
| +}
|
| +
|
| +TEST(EventSourceParserStoppingTest, Stop)
|
| +{
|
| + StoppingClient* client = new StoppingClient();
|
| + EventSourceParser* parser = new EventSourceParser(AtomicString(), client);
|
| + client->setParser(parser);
|
| +
|
| + const char input[] = "data:hello\n\ndata:bye\n\n";
|
| + parser->addBytes(input, strlen(input));
|
| +
|
| + const auto& events = client->events();
|
| +
|
| + ASSERT_EQ(1u, events.size());
|
| + ASSERT_EQ(EventOrReconnectionTimeSetting::Type::Event, events[0].type);
|
| + EXPECT_EQ("message", events[0].event);
|
| + EXPECT_EQ("hello", events[0].data);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +} // namespace blink
|
|
|