// Copyright 2020 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "core/fxcrt/css/cfx_csssyntaxparser.h"

#include "testing/gtest/include/gtest/gtest.h"

// These tests cover the "declaration only" mode of the CSS Syntax Parser
// (i.e. inline style="" attribute). The cfx_cssstylesheet_unitttest.cpp
// file covers the full-up selector { ... } parsing.

TEST(CSSSyntaxParserTest, ParseEmpty) {
  const wchar_t* input = L"";
  CFX_CSSSyntaxParser parser(input);
  parser.SetParseOnlyDeclarations();
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse());
}

TEST(CSSSyntaxParserTest, ParseBlank) {
  const wchar_t* input = L"  \n\r\t";
  CFX_CSSSyntaxParser parser(input);
  parser.SetParseOnlyDeclarations();
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse());
}

TEST(CSSSyntaxParserTest, ParseMissingColon) {
  const wchar_t* input = L"foo ";
  CFX_CSSSyntaxParser parser(input);
  parser.SetParseOnlyDeclarations();
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse());
}

TEST(CSSSyntaxParserTest, ParseMissingValue) {
  const wchar_t* input = L"foo: ";
  CFX_CSSSyntaxParser parser(input);
  parser.SetParseOnlyDeclarations();
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
  EXPECT_EQ(L"foo", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse());
}

TEST(CSSSyntaxParserTest, ParseSingleProp1) {
  const wchar_t* input = L"foo:bar";
  CFX_CSSSyntaxParser parser(input);
  parser.SetParseOnlyDeclarations();
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
  EXPECT_EQ(L"foo", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
            parser.DoSyntaxParse());
  EXPECT_EQ(L"bar", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse());
}

TEST(CSSSyntaxParserTest, ParseSingleProp2) {
  const wchar_t* input = L"foo:bar;";
  CFX_CSSSyntaxParser parser(input);
  parser.SetParseOnlyDeclarations();
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
  EXPECT_EQ(L"foo", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
            parser.DoSyntaxParse());
  EXPECT_EQ(L"bar", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse());
}

TEST(CSSSyntaxParserTest, ParseMissingColonMultiple) {
  const wchar_t* input = L"foo:bar; baz";
  CFX_CSSSyntaxParser parser(input);
  parser.SetParseOnlyDeclarations();
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
  EXPECT_EQ(L"foo", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
            parser.DoSyntaxParse());
  EXPECT_EQ(L"bar", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse());
}

TEST(CSSSyntaxParserTest, ParseMissingPropertyMultiple) {
  const wchar_t* input = L"foo:bar; baz: ";
  CFX_CSSSyntaxParser parser(input);
  parser.SetParseOnlyDeclarations();
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
  EXPECT_EQ(L"foo", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
            parser.DoSyntaxParse());
  EXPECT_EQ(L"bar", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
  EXPECT_EQ(L"baz", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse());
}

TEST(CSSSyntaxParserTest, ParseMultipleProp1) {
  const wchar_t* input = L"foo : bar; baz : bam";
  CFX_CSSSyntaxParser parser(input);
  parser.SetParseOnlyDeclarations();
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
  EXPECT_EQ(L"foo", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
            parser.DoSyntaxParse());
  EXPECT_EQ(L"bar", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
  EXPECT_EQ(L"baz", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
            parser.DoSyntaxParse());
  EXPECT_EQ(L"bam", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse());
}

TEST(CSSSyntaxParserTest, ParseMultipleProp2) {
  const wchar_t* input = L"foo:bar;baz:bam;";
  CFX_CSSSyntaxParser parser(input);
  parser.SetParseOnlyDeclarations();
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
  EXPECT_EQ(L"foo", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
            parser.DoSyntaxParse());
  EXPECT_EQ(L"bar", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
  EXPECT_EQ(L"baz", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
            parser.DoSyntaxParse());
  EXPECT_EQ(L"bam", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kEOS, parser.DoSyntaxParse());
}

TEST(CSSSyntaxParserTest, ParseOpenBrace1) {
  const wchar_t* input = L"{a:3}";
  CFX_CSSSyntaxParser parser(input);
  parser.SetParseOnlyDeclarations();

  // TODO(tsepez): these should fail on stray punctuation.
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
  EXPECT_EQ(L"{a", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
            parser.DoSyntaxParse());
  EXPECT_EQ(L"3", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kError, parser.DoSyntaxParse());
}

TEST(CSSSyntaxParserTest, ParseOpenBrace2) {
  const wchar_t* input = L"foo {a:3}";
  CFX_CSSSyntaxParser parser(input);
  parser.SetParseOnlyDeclarations();

  // TODO(tsepez): these should fail on stray punctuation.
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
  EXPECT_EQ(L"foo {a", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
            parser.DoSyntaxParse());
  EXPECT_EQ(L"3", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kError, parser.DoSyntaxParse());
}

TEST(CSSSyntaxParserTest, ParseOpenBrace3) {
  const wchar_t* input = L"foo: bar {a:3}";
  CFX_CSSSyntaxParser parser(input);
  parser.SetParseOnlyDeclarations();

  // TODO(tsepez): these should fail on stray punctuation.
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
  EXPECT_EQ(L"foo", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
            parser.DoSyntaxParse());
  EXPECT_EQ(L"bar {a:3", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kError, parser.DoSyntaxParse());
}

TEST(CSSSyntaxParserTest, ParseOpenBrace4) {
  const wchar_t* input = L"foo: bar; {a:3}";
  CFX_CSSSyntaxParser parser(input);
  parser.SetParseOnlyDeclarations();
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
  EXPECT_EQ(L"foo", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
            parser.DoSyntaxParse());
  EXPECT_EQ(L"bar", parser.GetCurrentString());

  // TODO(tsepez): these should fail on stray punctuation.
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
  EXPECT_EQ(L"{a", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
            parser.DoSyntaxParse());
  EXPECT_EQ(L"3", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kError, parser.DoSyntaxParse());
}

TEST(CSSSyntaxParserTest, ParseCloseBrace1) {
  const wchar_t* input = L"} foo:bar";
  CFX_CSSSyntaxParser parser(input);
  parser.SetParseOnlyDeclarations();
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kError, parser.DoSyntaxParse());
}

TEST(CSSSyntaxParserTest, ParseCloseBrace2) {
  const wchar_t* input = L"foo}:bar";
  CFX_CSSSyntaxParser parser(input);
  parser.SetParseOnlyDeclarations();
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kError, parser.DoSyntaxParse());
}

TEST(CSSSyntaxParserTest, ParseCloseBrace3) {
  const wchar_t* input = L"foo:bar}";
  CFX_CSSSyntaxParser parser(input);
  parser.SetParseOnlyDeclarations();
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
  EXPECT_EQ(L"foo", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
            parser.DoSyntaxParse());
  EXPECT_EQ(L"bar", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kError, parser.DoSyntaxParse());
}

TEST(CSSSyntaxParserTest, ParseCloseBrace4) {
  const wchar_t* input = L"foo:bar;}";
  CFX_CSSSyntaxParser parser(input);
  parser.SetParseOnlyDeclarations();
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyName, parser.DoSyntaxParse());
  EXPECT_EQ(L"foo", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kPropertyValue,
            parser.DoSyntaxParse());
  EXPECT_EQ(L"bar", parser.GetCurrentString());
  EXPECT_EQ(CFX_CSSSyntaxParser::Status::kError, parser.DoSyntaxParse());
}
