View Javadoc
1   /*
2    * Copyright (c) 2002-2025 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit;
16  
17  import java.io.StringReader;
18  import java.util.concurrent.CountDownLatch;
19  import java.util.concurrent.atomic.AtomicInteger;
20  
21  import org.htmlunit.WebClient.PooledCSS3Parser;
22  import org.htmlunit.cssparser.parser.CSSOMParser;
23  import org.htmlunit.cssparser.parser.InputSource;
24  import org.htmlunit.cssparser.parser.javacc.CSS3Parser;
25  import org.junit.jupiter.api.Test;
26  
27  /**
28   * Tests for {@link WebClient} and its CSS3Parser pool.
29   *
30   * @author René Schwietzke
31   * @author Ronald Brill
32   */
33  public class WebClient9Test extends SimpleWebTestCase {
34  
35      /**
36       * Ensure that we always get fresh parser when there
37       * is none and we already fetched one.
38       */
39      @Test
40      public void newParsersWhenNotReturning() {
41          try (WebClient webClient = new WebClient()) {
42              // ask for a parser
43              final CSS3Parser p1 = webClient.getCSS3Parser();
44  
45              // if we ask again, it is not the same
46              final CSS3Parser p2 = webClient.getCSS3Parser();
47  
48              assertFalse(p1 == p2);
49          }
50      }
51  
52      /**
53       * When we close a parser, we get it again when
54       * asking the next time.
55       */
56      @SuppressWarnings("resource")
57      @Test
58      public void closedParserIsPooled() {
59          try (WebClient webClient = new WebClient()) {
60              // ask for a parser
61              final PooledCSS3Parser p1;
62              final PooledCSS3Parser p2;
63  
64              try (PooledCSS3Parser p = webClient.getCSS3Parser()) {
65                  assertNotNull(p);
66                  p1 = p;
67              }
68              try (PooledCSS3Parser p = webClient.getCSS3Parser()) {
69                  assertNotNull(p);
70                  p2 = p;
71                  assertTrue(p == p1);
72              }
73              try (PooledCSS3Parser p = webClient.getCSS3Parser()) {
74                  assertNotNull(p);
75                  assertTrue(p == p2);
76                  assertTrue(p1 == p2);
77              }
78          }
79      }
80  
81      /**
82       * We can nest and get properly different parsers.
83       */
84      @SuppressWarnings("resource")
85      @Test
86      public void nestingWorks() {
87          try (WebClient webClient = new WebClient()) {
88              final PooledCSS3Parser p1;
89              final PooledCSS3Parser p2;
90  
91              try (PooledCSS3Parser p11 = webClient.getCSS3Parser()) {
92                  try (PooledCSS3Parser p21 = webClient.getCSS3Parser()) {
93                      assertNotNull(p11);
94                      assertNotNull(p21);
95                      assertFalse(p11 == p21);
96  
97                      // keep them
98                      p1 = p11;
99                      p2 = p21;
100                 }
101             }
102 
103             try (PooledCSS3Parser p11 = webClient.getCSS3Parser()) {
104                 try (PooledCSS3Parser p21 = webClient.getCSS3Parser()) {
105                     assertNotNull(p11);
106                     assertNotNull(p21);
107                     assertFalse(p11 == p21);
108 
109                     assertTrue(p11 == p1);
110                     assertTrue(p21 == p2);
111                 }
112             }
113         }
114     }
115 
116     /**
117      * Take one, returned it, need two... get another new one
118      */
119     @SuppressWarnings("resource")
120     @Test
121     public void flow1_2() {
122         try (WebClient webClient = new WebClient()) {
123             final PooledCSS3Parser p1;
124 
125             try (PooledCSS3Parser p11 = webClient.getCSS3Parser()) {
126                 p1 = p11;
127             }
128 
129             try (PooledCSS3Parser p11 = webClient.getCSS3Parser()) {
130                 assertNotNull(p11);
131                 assertTrue(p11 == p1);
132 
133                 try (PooledCSS3Parser p21 = webClient.getCSS3Parser()) {
134                     assertNotNull(p21);
135                     assertFalse(p21 == p11);
136                 }
137             }
138         }
139     }
140 
141     /**
142      * @throws Exception if the test fails
143      */
144     @Test
145     public void multithreading() throws InterruptedException {
146         final String css = "body { background-color: green; }";
147 
148         final int numberOfThreads = 10;
149 
150         final AtomicInteger errorCount = new AtomicInteger();
151         final CountDownLatch latch = new CountDownLatch(numberOfThreads);
152 
153         final WebClient webClient = new WebClient();
154         for (int i = 0; i < numberOfThreads; i++) {
155             new Thread(() -> {
156                 try {
157                     for (int j = 0; j < 100_000; j++) {
158                         try (PooledCSS3Parser pooledParser = webClient.getCSS3Parser()) {
159                             assertNotNull(pooledParser);
160 
161                             try (InputSource source = new InputSource(new StringReader(css))) {
162                                 final CSSOMParser parser = new CSSOMParser(pooledParser);
163                                 parser.parseStyleSheet(source, null);
164                             }
165                         }
166                     }
167                 }
168                 catch (final Exception e) {
169                     errorCount.addAndGet(1);
170                 }
171                 latch.countDown();
172             }).start();
173         }
174 
175         latch.await();
176         webClient.close();
177 
178         assertEquals(0, errorCount.get());
179     }
180 }