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.htmlunit.junit.BrowserRunner;
26  import org.junit.Test;
27  import org.junit.runner.RunWith;
28  
29  /**
30   * Tests for {@link WebClient} and its CSS3Parser pool.
31   *
32   * @author René Schwietzke
33   * @author Ronald Brill
34   */
35  @RunWith(BrowserRunner.class)
36  public class WebClient9Test extends SimpleWebTestCase {
37  
38      /**
39       * Ensure that we always get fresh parser when there
40       * is none and we already fetched one.
41       */
42      @Test
43      public void newParsersWhenNotReturning() {
44          try (WebClient webClient = new WebClient()) {
45              // ask for a parser
46              final CSS3Parser p1 = webClient.getCSS3Parser();
47  
48              // if we ask again, it is not the same
49              final CSS3Parser p2 = webClient.getCSS3Parser();
50  
51              assertFalse(p1 == p2);
52          }
53      }
54  
55      /**
56       * When we close a parser, we get it again when
57       * asking the next time.
58       */
59      @SuppressWarnings("resource")
60      @Test
61      public void closedParserIsPooled() {
62          try (WebClient webClient = new WebClient()) {
63              // ask for a parser
64              final PooledCSS3Parser p1;
65              final PooledCSS3Parser p2;
66  
67              try (PooledCSS3Parser p = webClient.getCSS3Parser()) {
68                  assertNotNull(p);
69                  p1 = p;
70              }
71              try (PooledCSS3Parser p = webClient.getCSS3Parser()) {
72                  assertNotNull(p);
73                  p2 = p;
74                  assertTrue(p == p1);
75              }
76              try (PooledCSS3Parser p = webClient.getCSS3Parser()) {
77                  assertNotNull(p);
78                  assertTrue(p == p2);
79                  assertTrue(p1 == p2);
80              }
81          }
82      }
83  
84      /**
85       * We can nest and get properly different parsers.
86       */
87      @SuppressWarnings("resource")
88      @Test
89      public void nestingWorks() {
90          try (WebClient webClient = new WebClient()) {
91              final PooledCSS3Parser p1;
92              final PooledCSS3Parser p2;
93  
94              try (PooledCSS3Parser p11 = webClient.getCSS3Parser()) {
95                  try (PooledCSS3Parser p21 = webClient.getCSS3Parser()) {
96                      assertNotNull(p11);
97                      assertNotNull(p21);
98                      assertFalse(p11 == p21);
99  
100                     // keep them
101                     p1 = p11;
102                     p2 = p21;
103                 }
104             }
105 
106             try (PooledCSS3Parser p11 = webClient.getCSS3Parser()) {
107                 try (PooledCSS3Parser p21 = webClient.getCSS3Parser()) {
108                     assertNotNull(p11);
109                     assertNotNull(p21);
110                     assertFalse(p11 == p21);
111 
112                     assertTrue(p11 == p1);
113                     assertTrue(p21 == p2);
114                 }
115             }
116         }
117     }
118 
119     /**
120      * Take one, returned it, need two... get another new one
121      */
122     @SuppressWarnings("resource")
123     @Test
124     public void flow1_2() {
125         try (WebClient webClient = new WebClient()) {
126             final PooledCSS3Parser p1;
127 
128             try (PooledCSS3Parser p11 = webClient.getCSS3Parser()) {
129                 p1 = p11;
130             }
131 
132             try (PooledCSS3Parser p11 = webClient.getCSS3Parser()) {
133                 assertNotNull(p11);
134                 assertTrue(p11 == p1);
135 
136                 try (PooledCSS3Parser p21 = webClient.getCSS3Parser()) {
137                     assertNotNull(p21);
138                     assertFalse(p21 == p11);
139                 }
140             }
141         }
142     }
143 
144     /**
145      * @throws Exception if the test fails
146      */
147     @Test
148     public void multithreading() throws InterruptedException {
149         final String css = "body { background-color: green; }";
150 
151         final int numberOfThreads = 10;
152 
153         final AtomicInteger errorCount = new AtomicInteger();
154         final CountDownLatch latch = new CountDownLatch(numberOfThreads);
155 
156         final WebClient webClient = new WebClient();
157         for (int i = 0; i < numberOfThreads; i++) {
158             new Thread(() -> {
159                 try {
160                     for (int j = 0; j < 100_000; j++) {
161                         try (PooledCSS3Parser pooledParser = webClient.getCSS3Parser()) {
162                             assertNotNull(pooledParser);
163 
164                             try (InputSource source = new InputSource(new StringReader(css))) {
165                                 final CSSOMParser parser = new CSSOMParser(pooledParser);
166                                 parser.parseStyleSheet(source, null);
167                             }
168                         }
169                     }
170                 }
171                 catch (final Exception e) {
172                     errorCount.addAndGet(1);
173                 }
174                 latch.countDown();
175             }).start();
176         }
177 
178         latch.await();
179         webClient.close();
180 
181         assertEquals(0, errorCount.get());
182     }
183 }