content: /*
   * Copyright (C) 2016 The Android Open Source Project
   *
   * 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.
   */
  package com.google.android.exoplayer2.text.webvtt;
  
  import static com.google.android.exoplayer2.text.webvtt.WebvttCssParser.parseNextToken;
  import static com.google.common.truth.Truth.assertThat;
  
  import androidx.test.ext.junit.runners.AndroidJUnit4;
  import com.google.android.exoplayer2.util.ParsableByteArray;
  import com.google.android.exoplayer2.util.Util;
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.HashSet;
  import java.util.List;
  import org.junit.Before;
  import org.junit.Test;
  import org.junit.runner.RunWith;
  
  /** Unit test for {@link WebvttCssParser}. */
  @RunWith(AndroidJUnit4.class)
  public final class WebvttCssParserTest {
  
    private WebvttCssParser parser;
  
    @Before
    public void setUp() {
      parser = new WebvttCssParser();
    }
  
    @Test
    public void skipWhitespacesAndComments() {
      // Skip only whitespaces
      String skipOnlyWhitespaces = " \t\r\n\f End of skip\n /*  */";
      assertSkipsToEndOfSkip("End of skip", skipOnlyWhitespaces);
  
      // Skip only comments.
      String skipOnlyComments = "/*A comment***//*/It even has spaces in it*/End of skip";
      assertSkipsToEndOfSkip("End of skip", skipOnlyComments);
  
      // Skip interleaved.
      String skipInterleaved = " /* We have comments and */\t\n/* whitespaces*/ End of skip";
      assertSkipsToEndOfSkip("End of skip", skipInterleaved);
  
      // Skip nothing.
      String skipNothing = "End of skip\n \t \r";
      assertSkipsToEndOfSkip("End of skip", skipNothing);
  
      // Skip everything.
      String skipEverything = "\t/* Comment */\n\r/* And another */";
      assertSkipsToEndOfSkip(null, skipEverything);
    }
  
    @Test
    public void getInputLimit() {
      // \r After 3 lines.
      String threeLinesThen3Cr = "One Line\nThen other\rAnd finally\r\r\r";
      assertInputLimit("", threeLinesThen3Cr);
  
      // \r\r After 3 lines
      String threeLinesThen2Cr = "One Line\nThen other\r\nAnd finally\r\r";
      assertInputLimit(null, threeLinesThen2Cr);
  
      // \n\n After 3 lines.
      String threeLinesThen2Lf = "One Line\nThen other\r\nAnd finally\n\nFinal\n\n\nLine";
      assertInputLimit("Final", threeLinesThen2Lf);
  
      // \r\n\n After 3 lines.
      String threeLinesThenCr2Lf = " \n \r\n \r\n\nFinal\n\n\nLine";
      assertInputLimit("Final", threeLinesThenCr2Lf);
  
      // Limit immediately.
      String immediateEmptyLine = "\nLine\nEnd";
      assertInputLimit("Line", immediateEmptyLine);
  
      // Empty string.
      assertInputLimit(null, "");
    }
  
    @Test
    public void parseMethodSimpleInput() {
      WebvttCssStyle expectedStyle = new WebvttCssStyle();
      String styleBlock1 = " ::cue { color : black; background-color: PapayaWhip }";
      expectedStyle.setFontColor(0xFF000000);
      expectedStyle.setBackgroundColor(0xFFFFEFD5);
      assertParserProduces(styleBlock1, expectedStyle);
  
      String styleBlock2 = " ::cue { color : black }\n\n::cue { color : invalid }";
      expectedStyle = new WebvttCssStyle();
      expectedStyle.setFontColor(0xFF000000);
      assertParserProduces(styleBlock2, expectedStyle);
  
      String styleBlock3 = "::cue {\n background-color\n:#00fFFe}";
      expectedStyle = new WebvttCssStyle();
      expectedStyle.setBackgroundColor(0xFF00FFFE);
      assertParserProduces(styleBlock3, expectedStyle);
    }
  
    @Test
    public void parseMethodMultipleRulesInBlockInput() {
      String styleBlock =
          "::cue {\n background-color\n:#00fFFe}      \n::cue {\n background-color\n:#00000000}\n";
      WebvttCssStyle expectedStyle = new WebvttCssStyle();
      expectedStyle.setBackgroundColor(0xFF00FFFE);
      WebvttCssStyle secondExpectedStyle = new WebvttCssStyle();
      secondExpectedStyle.setBackgroundColor(0x000000);
      assertParserProduces(styleBlock, expectedStyle, secondExpectedStyle);
    }
  
    @Test
    public void multiplePropertiesInBlock() {
      String styleBlock =
          "::cue(#id){text-decoration:underline; background-color:green;"
              + "color:red; font-family:Courier; font-weight:bold}";
      WebvttCssStyle expectedStyle = new WebvttCssStyle();
      expectedStyle.setTargetId("id");
      expectedStyle.setUnderline(true);
      expectedStyle.setBackgroundColor(0xFF008000);
      expectedStyle.setFontColor(0xFFFF0000);
      expectedStyle.setFontFamily("courier");
      expectedStyle.setBold(true);
  
      assertParserProduces(styleBlock, expectedStyle);
    }
  
    @Test
    public void rgbaColorExpression() {
      String styleBlock =
          "::cue(#rgb){background-color: rgba(\n10/* Ugly color */,11\t, 12\n,.1);"
              + "color:rgb(1,1,\n1)}";
      WebvttCssStyle expectedStyle = new WebvttCssStyle();
      expectedStyle.setTargetId("rgb");
      expectedStyle.setBackgroundColor(0x190A0B0C);
      expectedStyle.setFontColor(0xFF010101);
  
      assertParserProduces(styleBlock, expectedStyle);
    }
  
    @Test
    public void getNextToken() {
      String stringInput = " lorem:ipsum\n{dolor}#sit,amet;lorem:ipsum\r\t\f\ndolor(())\n";
      ParsableByteArray input = new ParsableByteArray(Util.getUtf8Bytes(stringInput));
      StringBuilder builder = new StringBuilder();
      assertThat(parseNextToken(input, builder)).isEqualTo("lorem");
      assertThat(parseNextToken(input, builder)).isEqualTo(":");
      assertThat(parseNextToken(input, builder)).isEqualTo("ipsum");
      assertThat(parseNextToken(input, builder)).isEqualTo("{");
      assertThat(parseNextToken(input, builder)).isEqualTo("dolor");
      assertThat(parseNextToken(input, builder)).isEqualTo("}");
      assertThat(parseNextToken(input, builder)).isEqualTo("#sit");
      assertThat(parseNextToken(input, builder)).isEqualTo(",");
      assertThat(parseNextToken(input, builder)).isEqualTo("amet");
      assertThat(parseNextToken(input, builder)).isEqualTo(";");
      assertThat(parseNextToken(input, builder)).isEqualTo("lorem");
      assertThat(parseNextToken(input, builder)).isEqualTo(":");
      assertThat(parseNextToken(input, builder)).isEqualTo("ipsum");
      assertThat(parseNextToken(input, builder)).isEqualTo("dolor");
      assertThat(parseNextToken(input, builder)).isEqualTo("(");
      assertThat(parseNextToken(input, builder)).isEqualTo("(");
      assertThat(parseNextToken(input, builder)).isEqualTo(")");
      assertThat(parseNextToken(input, builder)).isEqualTo(")");
      assertThat(parseNextToken(input, builder)).isNull();
    }
  
    @Test
    public void styleScoreSystem() {
      WebvttCssStyle style = new WebvttCssStyle();
      // Universal selector.
      assertThat(style.getSpecificityScore("", "", Collections.emptySet(), "")).isEqualTo(1);
      // Class match without tag match.
      style.setTargetClasses(new String[] {"class1", "class2"});
      assertThat(
              style.getSpecificityScore(
                  "", "", new HashSet<>(Arrays.asList("class1", "class2", "class3")), ""))
          .isEqualTo(8);
      // Class and tag match
      style.setTargetTagName("b");
      assertThat(
              style.getSpecificityScore(
                  "", "b", new HashSet<>(Arrays.asList("class1", "class2", "class3")), ""))
          .isEqualTo(10);
      // Class insufficiency.
      assertThat(
              style.getSpecificityScore("", "b", new HashSet<>(Arrays.asList("class1", "class")), ""))
          .isEqualTo(0);
      // Voice, classes and tag match.
      style.setTargetVoice("Manuel Cráneo");
      assertThat(
              style.getSpecificityScore(
                  "",
                  "b",
                  new HashSet<>(Arrays.asList("class1", "class2", "class3")),
                  "Manuel Cráneo"))
          .isEqualTo(14);
      // Voice mismatch.
      assertThat(
              style.getSpecificityScore(
                  null,
                  "b",
                  new HashSet<>(Arrays.asList("class1", "class2", "class3")),
                  "Manuel Craneo"))
          .isEqualTo(0);
      // Id, voice, classes and tag match.
      style.setTargetId("id");
      assertThat(
              style.getSpecificityScore(
                  "id",
                  "b",
                  new HashSet<>(Arrays.asList("class1", "class2", "class3")),
                  "Manuel Cráneo"))
          .isEqualTo(0x40000000 + 14);
      // Id mismatch.
      assertThat(
              style.getSpecificityScore(
                  "id1", "b", new HashSet<>(Arrays.asList("class1", "class2", "class3")), ""))
          .isEqualTo(0);
    }
  
    // Utility methods.
  
    private void assertSkipsToEndOfSkip(String expectedLine, String s) {
      ParsableByteArray input = new ParsableByteArray(Util.getUtf8Bytes(s));
      WebvttCssParser.skipWhitespaceAndComments(input);
      assertThat(input.readLine()).isEqualTo(expectedLine);
    }
  
    private void assertInputLimit(String expectedLine, String s) {
      ParsableByteArray input = new ParsableByteArray(Util.getUtf8Bytes(s));
      WebvttCssParser.skipStyleBlock(input);
      assertThat(input.readLine()).isEqualTo(expectedLine);
    }
  
    private void assertParserProduces(String styleBlock, WebvttCssStyle... expectedStyles) {
      ParsableByteArray input = new ParsableByteArray(Util.getUtf8Bytes(styleBlock));
      List<WebvttCssStyle> styles = parser.parseBlock(input);
      assertThat(styles.size()).isEqualTo(expectedStyles.length);
      for (int i = 0; i < expectedStyles.length; i++) {
        WebvttCssStyle expected = expectedStyles[i];
        WebvttCssStyle actualElem = styles.get(i);
        assertThat(actualElem.hasBackgroundColor()).isEqualTo(expected.hasBackgroundColor());
        if (expected.hasBackgroundColor()) {
          assertThat(actualElem.getBackgroundColor()).isEqualTo(expected.getBackgroundColor());
        }
        assertThat(actualElem.hasFontColor()).isEqualTo(expected.hasFontColor());
        if (expected.hasFontColor()) {
          assertThat(actualElem.getFontColor()).isEqualTo(expected.getFontColor());
        }
        assertThat(actualElem.getFontFamily()).isEqualTo(expected.getFontFamily());
        assertThat(actualElem.getFontSize()).isEqualTo(expected.getFontSize());
        assertThat(actualElem.getFontSizeUnit()).isEqualTo(expected.getFontSizeUnit());
        assertThat(actualElem.getStyle()).isEqualTo(expected.getStyle());
        assertThat(actualElem.isLinethrough()).isEqualTo(expected.isLinethrough());
        assertThat(actualElem.isUnderline()).isEqualTo(expected.isUnderline());
      }
    }
  }
  