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.archunit;
16  
17  import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
18  import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.constructors;
19  import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.fields;
20  import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.methods;
21  import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
22  
23  import java.lang.reflect.Executable;
24  import java.util.function.Supplier;
25  
26  import org.apache.commons.lang3.StringUtils;
27  import org.htmlunit.corejs.javascript.Scriptable;
28  import org.htmlunit.javascript.configuration.JsxClass;
29  import org.htmlunit.javascript.configuration.JsxClasses;
30  import org.htmlunit.javascript.configuration.JsxConstant;
31  import org.htmlunit.javascript.configuration.JsxConstructor;
32  import org.htmlunit.javascript.configuration.JsxFunction;
33  import org.htmlunit.javascript.configuration.JsxGetter;
34  import org.htmlunit.javascript.configuration.JsxSetter;
35  import org.junit.runner.RunWith;
36  
37  import com.tngtech.archunit.base.DescribedPredicate;
38  import com.tngtech.archunit.core.domain.JavaClass;
39  import com.tngtech.archunit.core.domain.JavaMethod;
40  import com.tngtech.archunit.core.domain.JavaModifier;
41  import com.tngtech.archunit.core.importer.ImportOption;
42  import com.tngtech.archunit.junit.AnalyzeClasses;
43  import com.tngtech.archunit.junit.ArchTest;
44  import com.tngtech.archunit.junit.ArchUnitRunner;
45  import com.tngtech.archunit.lang.ArchCondition;
46  import com.tngtech.archunit.lang.ArchRule;
47  import com.tngtech.archunit.lang.ConditionEvents;
48  import com.tngtech.archunit.lang.SimpleConditionEvent;
49  
50  /**
51   * Architecture tests.
52   *
53   * @author Ronald Brill
54   */
55  @RunWith(ArchUnitRunner.class)
56  @AnalyzeClasses(packages = "org.htmlunit", importOptions = ImportOption.DoNotIncludeTests.class)
57  public class ArchitectureTest {
58  
59      /**
60       * Utility classes should be placed in 'org.htmlunit.util'.
61       */
62      @ArchTest
63      public static final ArchRule utilsPackageRule = classes()
64          .that().haveNameMatching(".*Util.?")
65          .and().doNotHaveFullyQualifiedName("org.htmlunit.cssparser.util.ParserUtils")
66          .and().doNotHaveFullyQualifiedName("org.htmlunit.http.HttpUtils")
67  
68          .and().doNotHaveFullyQualifiedName("org.htmlunit.platform.font.AwtFontUtil")
69          .and().doNotHaveFullyQualifiedName("org.htmlunit.platform.font.FontUtil")
70          .and().doNotHaveFullyQualifiedName("org.htmlunit.platform.font.NoOpFontUtil")
71  
72          .and().doNotHaveFullyQualifiedName("org.htmlunit.csp.Utils")
73          .and().doNotHaveFullyQualifiedName("org.htmlunit.cyberneko.util.StringUtils")
74  
75          .and().resideOutsideOfPackage("org.htmlunit.jetty.util..")
76          .and().doNotHaveFullyQualifiedName("org.htmlunit.jetty.websocket.api.util.QuoteUtil")
77          .and().doNotHaveFullyQualifiedName("org.htmlunit.jetty.websocket.common.util.ReflectUtils")
78          .and().doNotHaveFullyQualifiedName("org.htmlunit.jetty.websocket.common.util.TextUtil")
79  
80          .should().resideInAPackage("org.htmlunit.util");
81  
82      /**
83       * Do not use awt if not really needed (because not available on android).
84       */
85      @ArchTest
86      public static final ArchRule awtPackageRule = noClasses()
87          .that()
88              .resideOutsideOfPackage("org.htmlunit.platform..")
89              .and().resideOutsideOfPackage("org.htmlunit.corejs.javascript.tools..")
90              .and().resideOutsideOfPackage("org.htmlunit.jetty..")
91          .should().dependOnClassesThat().resideInAnyPackage("java.awt..");
92  
93      /**
94       * The jetty websocket stuff is only used by one class.
95       */
96      @ArchTest
97      public static final ArchRule webSocketPackageRule = noClasses()
98              .that()
99                  .resideOutsideOfPackage("org.htmlunit.jetty..")
100                 .and().doNotHaveFullyQualifiedName("org.htmlunit.websocket.JettyWebSocketAdapter")
101                 .and().doNotHaveFullyQualifiedName("org.htmlunit.websocket.JettyWebSocketAdapter$JettyWebSocketAdapterFactory")
102                 .and().doNotHaveFullyQualifiedName("org.htmlunit.websocket.JettyWebSocketAdapter$JettyWebSocketAdapterImpl")
103             .should()
104                 .dependOnClassesThat().resideInAnyPackage("org.htmlunit.jetty..");
105 
106     /**
107      * JsxClasses are always in the javascript package.
108      */
109     @ArchTest
110     public static final ArchRule jsxClassAnnotationPackages = classes()
111             .that().areAnnotatedWith(JsxClass.class)
112             .should().resideInAPackage("..javascript..");
113 
114     /**
115      * Every JsxConstant should be public static final.
116      *
117      * AbstractJavaScriptConfiguration.process(ClassConfiguration, String, SupportedBrowser)
118      * stores the value.
119      */
120     @ArchTest
121     public static final ArchRule jsxConstant = fields()
122             .that().areAnnotatedWith(JsxConstant.class)
123             .should().haveModifier(JavaModifier.PUBLIC)
124             .andShould().haveModifier(JavaModifier.STATIC)
125             .andShould().haveModifier(JavaModifier.FINAL);
126 
127     /**
128      * Every JsxConstant should be a string, int, or long.
129      */
130     @ArchTest
131     public static final ArchRule jsxConstantType = fields()
132             .that().areAnnotatedWith(JsxConstant.class)
133             .should().haveRawType(String.class)
134             .orShould().haveRawType("int")
135             .orShould().haveRawType("long");
136 
137     /**
138      * JsxGetter/Setter/Functions are always in the javascript package.
139      */
140     @ArchTest
141     public static final ArchRule jsxAnnotationPackages = methods()
142             .that().areAnnotatedWith(JsxGetter.class)
143                     .or().areAnnotatedWith(JsxSetter.class)
144                     .or().areAnnotatedWith(JsxFunction.class)
145                     .or().areAnnotatedWith(JsxConstructor.class)
146                     .or().areAnnotatedWith(JsxConstant.class)
147             .should().beDeclaredInClassesThat().resideInAPackage("..javascript..");
148 
149     /**
150      * JsxGetter/Setter/Functions only valid in classes annotated as JsxClass.
151      */
152     @ArchTest
153     public static final ArchRule jsxAnnotationJsxClass = methods()
154             .that().areAnnotatedWith(JsxGetter.class)
155                     .or().areAnnotatedWith(JsxSetter.class)
156                     .or().areAnnotatedWith(JsxFunction.class)
157                     .or().areAnnotatedWith(JsxConstructor.class)
158                     .or().areAnnotatedWith(JsxConstant.class)
159             .should().beDeclaredInClassesThat().areAnnotatedWith(JsxClass.class)
160             .orShould().beDeclaredInClassesThat().areAnnotatedWith(JsxClasses.class);
161 
162     private static final DescribedPredicate<? super JavaClass> isAssignableToScriptable =
163             new DescribedPredicate<JavaClass>("@is not assignable to Scriptable") {
164                 @Override
165                 public boolean test(final JavaClass javaClass) {
166                     // we have to build a more complex implemenation because
167                     // javaClass.isAssignableTo(Scriptable.class);
168                     // checks also all superclasses
169                     // Therefore we have to switch back to the real java class.
170                     try {
171                         if (javaClass.isPrimitive()) {
172                             return false;
173                         }
174 
175                         return Scriptable.class.isAssignableFrom(Class.forName(javaClass.getFullName()));
176                     }
177                     catch (final ClassNotFoundException e) {
178                         throw new RuntimeException(e.getMessage(), e);
179                     }
180                 }
181             };
182 
183     /**
184      * JsxGetter should only return Scriptable's.
185      */
186     @ArchTest
187     public static final ArchRule jsxGetterReturnType = methods()
188             .that()
189                 .areAnnotatedWith(JsxGetter.class)
190                 .and().doNotHaveFullName("org.htmlunit.javascript.host.History.getState()")
191                 .and().doNotHaveFullName("org.htmlunit.javascript.host.Navigator.getDoNotTrack()")
192                 .and().doNotHaveFullName("org.htmlunit.javascript.host.URL.getOrigin()")
193                 .and().doNotHaveFullName("org.htmlunit.javascript.host.Window.getClientInformation()")
194                 .and().doNotHaveFullName("org.htmlunit.javascript.host.Window.getControllers()")
195                 .and().doNotHaveFullName("org.htmlunit.javascript.host.Window.getEvent()")
196                 .and().doNotHaveFullName("org.htmlunit.javascript.host.Window.getFrames_js()")
197                 .and().doNotHaveFullName("org.htmlunit.javascript.host.Window.getLength()")
198                 .and().doNotHaveFullName("org.htmlunit.javascript.host.Window.getOpener()")
199                 .and().doNotHaveFullName("org.htmlunit.javascript.host.Window.getParent()")
200                 .and().doNotHaveFullName("org.htmlunit.javascript.host.Window.getSelf()")
201                 .and().doNotHaveFullName("org.htmlunit.javascript.host.Window.getTop()")
202                 .and().doNotHaveFullName("org.htmlunit.javascript.host.dom.AbstractRange.getEndContainer()")
203                 .and().doNotHaveFullName("org.htmlunit.javascript.host.dom.AbstractRange.getStartContainer()")
204                 .and().doNotHaveFullName("org.htmlunit.javascript.host.dom.CharacterData.getData()")
205                 .and().doNotHaveFullName("org.htmlunit.javascript.host.dom.DOMException.getCode()")
206                 .and().doNotHaveFullName("org.htmlunit.javascript.host.dom.DOMException.getFilename()")
207                 .and().doNotHaveFullName("org.htmlunit.javascript.host.dom.DOMException.getLineNumber()")
208                 .and().doNotHaveFullName("org.htmlunit.javascript.host.dom.DOMException.getMessage()")
209                 .and().doNotHaveFullName("org.htmlunit.javascript.host.dom.Document.getActiveElement()")
210                 .and().doNotHaveFullName("org.htmlunit.javascript.host.dom.Document.getDefaultView()")
211                 .and().doNotHaveFullName("org.htmlunit.javascript.host.dom.Document.getHead()")
212                 .and().doNotHaveFullName("org.htmlunit.javascript.host.dom.Node.getParentNode()")
213                 .and().doNotHaveFullName("org.htmlunit.javascript.host.dom.NodeIterator.getFilter()")
214                 .and().doNotHaveFullName("org.htmlunit.javascript.host.dom.Range.getCommonAncestorContainer()")
215                 .and().doNotHaveFullName("org.htmlunit.javascript.host.dom.TreeWalker.getFilter()")
216                 .and().doNotHaveFullName("org.htmlunit.javascript.host.event.BeforeUnloadEvent.getReturnValue()")
217                 .and().doNotHaveFullName("org.htmlunit.javascript.host.event.CustomEvent.getDetail()")
218                 .and().doNotHaveFullName("org.htmlunit.javascript.host.event.Event.getReturnValue()")
219                 .and().doNotHaveFullName("org.htmlunit.javascript.host.event.Event.getSrcElement()")
220                 .and().doNotHaveFullName("org.htmlunit.javascript.host.event.Event.getTarget()")
221                 .and().doNotHaveFullName("org.htmlunit.javascript.host.event.InputEvent.getData()")
222                 .and().doNotHaveFullName("org.htmlunit.javascript.host.event.InputEvent.getInputType()")
223                 .and().doNotHaveFullName("org.htmlunit.javascript.host.event.MessageEvent.getData()")
224                 .and().doNotHaveFullName("org.htmlunit.javascript.host.event.MessageEvent.getPorts()")
225                 .and().doNotHaveFullName("org.htmlunit.javascript.host.event.PopStateEvent.getState()")
226                 .and().doNotHaveFullName("org.htmlunit.javascript.host.event.ProgressEvent.getLoaded()")
227                 .and().doNotHaveFullName("org.htmlunit.javascript.host.event.TextEvent.getData()")
228                 .and().doNotHaveFullName("org.htmlunit.javascript.host.file.FileReader.getResult()")
229                 .and().doNotHaveFullName("org.htmlunit.javascript.host.html.HTMLButtonElement.getValue()")
230                 .and().doNotHaveFullName("org.htmlunit.javascript.host.html.HTMLDataElement.getValue()")
231                 .and().doNotHaveFullName("org.htmlunit.javascript.host.html.HTMLInputElement.getValue()")
232                 .and().doNotHaveFullName("org.htmlunit.javascript.host.html.HTMLMeterElement.getValue()")
233                 .and().doNotHaveFullName("org.htmlunit.javascript.host.html.HTMLOptionElement.getValue()")
234                 .and().doNotHaveFullName("org.htmlunit.javascript.host.html.HTMLParamElement.getValue()")
235                 .and().doNotHaveFullName("org.htmlunit.javascript.host.html.HTMLProgressElement.getValue()")
236                 .and().doNotHaveFullName("org.htmlunit.javascript.host.html.HTMLSelectElement.getValue()")
237                 .and().doNotHaveFullName("org.htmlunit.javascript.host.html.HTMLTextAreaElement.getValue()")
238                 .and().doNotHaveFullName("org.htmlunit.javascript.host.worker.DedicatedWorkerGlobalScope.getSelf()")
239                 .and().doNotHaveFullName("org.htmlunit.javascript.host.xml.XMLHttpRequest.getResponse()")
240                 .and().doNotHaveFullName("org.htmlunit.javascript.host.xml.XMLHttpRequest.getResponseXML()")
241 
242             .should().haveRawReturnType(String.class)
243             .orShould().haveRawReturnType("int")
244             .orShould().haveRawReturnType(Integer.class)
245             .orShould().haveRawReturnType("long")
246             .orShould().haveRawReturnType("double")
247             .orShould().haveRawReturnType(Double.class)
248             .orShould().haveRawReturnType("boolean")
249             .orShould().haveRawReturnType(isAssignableToScriptable);
250 
251     /**
252      * JsxSetter should only return void.
253      */
254     @ArchTest
255     public static final ArchRule jsxSetterReturnType = methods()
256             .that()
257                 .areAnnotatedWith(JsxSetter.class)
258             .should().haveRawReturnType("void");
259 
260     /**
261      * JsxFunctions should only return Scriptable's.
262      */
263     @ArchTest
264     public static final ArchRule jsxFunctionReturnType = methods()
265             .that()
266                 .areAnnotatedWith(JsxFunction.class)
267                 .and().doNotHaveFullName("org.htmlunit.javascript.host.External.isSearchProviderInstalled()")
268                 .and().doNotHaveFullName("org.htmlunit.javascript.host.SimpleArray.item(int)")
269                 .and().doNotHaveFullName("org.htmlunit.javascript.host.SimpleArray.namedItem(java.lang.String)")
270                 .and().doNotHaveFullName("org.htmlunit.javascript.host.Storage.getItem(java.lang.String)")
271                 .and().doNotHaveFullName("org.htmlunit.javascript.host.Window.setInterval(org.htmlunit.corejs.javascript.Context, org.htmlunit.corejs.javascript.Scriptable, org.htmlunit.corejs.javascript.Scriptable, [Ljava.lang.Object;, org.htmlunit.corejs.javascript.Function)")
272                 .and().doNotHaveFullName("org.htmlunit.javascript.host.Window.setTimeout(org.htmlunit.corejs.javascript.Context, org.htmlunit.corejs.javascript.Scriptable, org.htmlunit.corejs.javascript.Scriptable, [Ljava.lang.Object;, org.htmlunit.corejs.javascript.Function)")
273                 .and().doNotHaveFullName("org.htmlunit.javascript.host.css.CSSRuleList.item(int)")
274                 .and().doNotHaveFullName("org.htmlunit.javascript.host.css.StyleSheetList.item(int)")
275                 .and().doNotHaveFullName("org.htmlunit.javascript.host.dom.NodeList.item(java.lang.Object)")
276                 .and().doNotHaveFullName("org.htmlunit.javascript.host.html.HTMLAllCollection.item(java.lang.Object)")
277                 .and().doNotHaveFullName("org.htmlunit.javascript.host.html.HTMLCollection.item(java.lang.Object)")
278                 .and().doNotHaveFullName("org.htmlunit.javascript.host.html.HTMLOptionsCollection.item(int)")
279                 .and().doNotHaveFullName("org.htmlunit.javascript.host.html.HTMLSelectElement.item(int)")
280                 .and().doNotHaveFullName("org.htmlunit.javascript.host.intl.V8BreakIterator.resolvedOptions()")
281                 .and().doNotHaveFullName("org.htmlunit.javascript.host.performance.PerformanceNavigation.toJSON()")
282                 .and().doNotHaveFullName("org.htmlunit.javascript.host.worker.DedicatedWorkerGlobalScope.setInterval(org.htmlunit.corejs.javascript.Context, org.htmlunit.corejs.javascript.Scriptable, org.htmlunit.corejs.javascript.Scriptable, [Ljava.lang.Object;, org.htmlunit.corejs.javascript.Function)")
283                 .and().doNotHaveFullName("org.htmlunit.javascript.host.worker.DedicatedWorkerGlobalScope.setTimeout(org.htmlunit.corejs.javascript.Context, org.htmlunit.corejs.javascript.Scriptable, org.htmlunit.corejs.javascript.Scriptable, [Ljava.lang.Object;, org.htmlunit.corejs.javascript.Function)")
284                 .and().doNotHaveFullName("org.htmlunit.javascript.host.xml.XSLTProcessor.getParameter(java.lang.String, java.lang.String)")
285 
286             .should().haveRawReturnType(String.class)
287             .orShould().haveRawReturnType("int")
288             .orShould().haveRawReturnType("long")
289             .orShould().haveRawReturnType("double")
290             .orShould().haveRawReturnType("boolean")
291             .orShould().haveRawReturnType("void")
292             .orShould().haveRawReturnType(isAssignableToScriptable);
293 
294     /**
295      * JsxConstructor should not used for constructors.
296      */
297     @ArchTest
298     public static final ArchRule jsxAnnotationJsxConstructor = constructors()
299             .should().notBeAnnotatedWith(JsxConstructor.class);
300 
301     /**
302      * JsxConstructor should have name jsConstructor.
303      */
304     @ArchTest
305     public static final ArchRule jsxAnnotationJsxConstructorNaming = methods()
306             .that().areAnnotatedWith(JsxConstructor.class)
307             .should().haveName("jsConstructor");
308 
309     /**
310      * JsxGetter/Setter/Functions should not use a short as parameter.
311      */
312     @ArchTest
313     public static final ArchRule jsxAnnotationParameterType = methods()
314             .that().areAnnotatedWith(JsxGetter.class)
315                     .or().areAnnotatedWith(JsxSetter.class)
316                     .or().areAnnotatedWith(JsxFunction.class)
317             .should().notHaveRawParameterTypes("short")
318             .andShould().notHaveRawParameterTypes("float");
319 
320     /**
321      * Every JsxGetter has to be named get... or is....
322      */
323     @ArchTest
324     public static final ArchRule jsxGetterAnnotationStartsWithGet = methods()
325             .that().areAnnotatedWith(JsxGetter.class)
326             .should().haveNameStartingWith("get")
327             .orShould().haveNameStartingWith("is");
328 
329     private static final DescribedPredicate<JavaMethod> haveJsxGetterWithNonDefaultPropertyName =
330             new DescribedPredicate<JavaMethod>("@JsxGetter has a non default propertyName") {
331                 @Override
332                 public boolean test(final JavaMethod method) {
333                     return StringUtils.isNotEmpty(method.getAnnotationOfType(JsxGetter.class).propertyName());
334                 }
335             };
336 
337     /**
338      * Every JsxGetter with propertyName defined has to end in '_js'.
339      */
340     @ArchTest
341     public static final ArchRule jsxGetterAnnotationPostfix = methods()
342             .that().areAnnotatedWith(JsxGetter.class)
343             .and(haveJsxGetterWithNonDefaultPropertyName)
344             .should().haveNameEndingWith("_js");
345 
346     /**
347      * Every JsxGetter has to be named get... or is....
348      */
349     @ArchTest
350     public static final ArchRule jsxSetterAnnotationStartsWithSet = methods()
351             .that().areAnnotatedWith(JsxSetter.class)
352             .should().haveNameStartingWith("set");
353 
354     private static final DescribedPredicate<JavaMethod> haveJsxSetterWithNonDefaultPropertyName =
355             new DescribedPredicate<JavaMethod>("@JsxSetter has a non default propertyName") {
356                 @Override
357                 public boolean test(final JavaMethod method) {
358                     return StringUtils.isNotEmpty(method.getAnnotationOfType(JsxSetter.class).propertyName());
359                 }
360             };
361 
362     /**
363      * Every JsxSetter with propertyName defined has to end in '_js'.
364      */
365     @ArchTest
366     public static final ArchRule jsxSetterAnnotationPostfix = methods()
367             .that().areAnnotatedWith(JsxSetter.class)
368             .and(haveJsxSetterWithNonDefaultPropertyName)
369             .should().haveNameEndingWith("_js");
370 
371     private static final ArchCondition<JavaMethod> hasMatchingGetter =
372             new ArchCondition<JavaMethod>("every @JsxSetter needs a matching @JsxGetter") {
373                 @Override
374                 public void check(final JavaMethod method, final ConditionEvents events) {
375                     String getterName = "g" + method.getName().substring(1);
376                     if (method.getOwner().tryGetMethod(getterName).isPresent()) {
377                         return;
378                     }
379 
380                     getterName = "is" + method.getName().substring(3);
381                     if (method.getOwner().tryGetMethod(getterName).isPresent()) {
382                         return;
383                     }
384 
385                     events.add(SimpleConditionEvent.violated(method,
386                             "No matching JsxGetter found for " + method.getFullName()));
387                 }
388             };
389 
390     /**
391      * Every @JsxSetter needs a matching @JsxGetter.
392      */
393     @ArchTest
394     public static final ArchRule setterNeedsMatchingGetter = methods()
395             .that().areAnnotatedWith(JsxSetter.class)
396             .should(hasMatchingGetter);
397 
398     /**
399      * Do not overwrite toString for javascript, use jsToString and define the
400      * functionName in the @JsxFunction annotation.
401      */
402     @ArchTest
403     public static final ArchRule jsToString = methods()
404             .that().areAnnotatedWith(JsxFunction.class)
405             .should().haveNameNotMatching("toString");
406 
407     /**
408      * Make sure to not use java.lang.reflect.Executable.
409      */
410     @ArchTest
411     public static final ArchRule android7Executable = noClasses()
412             .should().dependOnClassesThat().haveFullyQualifiedName(Executable.class.getName());
413 
414     /**
415      * Make sure to not use {@link ThreadLocal#withInitial(java.util.function.Supplier)}.
416      */
417     @ArchTest
418     public static final ArchRule android7ThreadLocalWithInitial = noClasses()
419             .should().callMethod(ThreadLocal.class, "withInitial", Supplier.class);
420 
421     /**
422      * Make sure to not use org.w3c.dom.traversal.TreeWalker.
423      */
424     @ArchTest
425     public static final ArchRule androidTreeWalker = noClasses()
426             .that()
427                 .doNotHaveFullyQualifiedName("org.htmlunit.html.HtmlDomTreeWalker")
428                 .and().doNotHaveFullyQualifiedName("org.htmlunit.platform.dom.traversal.DomTreeWalker")
429                 .and().doNotHaveFullyQualifiedName("org.htmlunit.SgmlPage")
430             .should().dependOnClassesThat().haveFullyQualifiedName("org.w3c.dom.traversal.TreeWalker");
431 
432     /**
433      * Make sure to not use org.w3c.dom.traversal.DocumentTraversal.
434      */
435     @ArchTest
436     public static final ArchRule androidDocumentTraversal = noClasses()
437             .should().dependOnClassesThat().haveFullyQualifiedName("org.w3c.dom.traversal.DocumentTraversal");
438 
439     /**
440      * Make sure to not use javax.imageio.
441      */
442     @ArchTest
443     public static final ArchRule androidRanges = noClasses()
444         .should().dependOnClassesThat().resideInAnyPackage("org.w3c.dom.ranges..");
445 
446 
447     /**
448      * Make sure to not use javax.imageio.
449      */
450     @ArchTest
451     public static final ArchRule androidImageio = noClasses()
452          .that()
453             .doNotHaveFullyQualifiedName("org.htmlunit.platform.image.ImageIOImageData")
454             .and().doNotHaveFullyQualifiedName("org.htmlunit.platform.canvas.rendering.AwtRenderingBackend")
455             .and().doNotHaveFullyQualifiedName("org.htmlunit.platform.canvas.rendering.AwtRenderingBackend")
456             .and().resideOutsideOfPackage("org.htmlunit.jetty..")
457         .should().dependOnClassesThat().resideInAnyPackage("javax.imageio..");
458 
459     /**
460      * Make sure to not use Xerces.
461      */
462     @ArchTest
463     public static final ArchRule xerces = noClasses()
464         .that()
465             .doNotHaveFullyQualifiedName("org.htmlunit.platform.util.XmlUtilsXercesHelper")
466         .should().dependOnClassesThat().resideInAnyPackage("org.apache.xerces..");
467 
468     /**
469      * Make sure to not use jdk - Xerces.
470      */
471     @ArchTest
472     public static final ArchRule jdkXerces = noClasses()
473         .that()
474             .doNotHaveFullyQualifiedName("org.htmlunit.platform.util.XmlUtilsSunXercesHelper")
475         .should().dependOnClassesThat().resideInAnyPackage("com.sun.org.apache.xerces..");
476 
477     /**
478      * Make sure the httpclient is only accessed from the adapter classes.
479      */
480     @ArchTest
481     public static final ArchRule httpClient = noClasses()
482         .that()
483             .doNotHaveFullyQualifiedName("org.htmlunit.HttpWebConnection")
484             .and().areNotInnerClasses()
485             .and().areNotMemberClasses()
486 
487             .and().doNotHaveFullyQualifiedName("org.htmlunit.WebClient")
488             .and().doNotHaveFullyQualifiedName("org.htmlunit.WebRequest")
489             .and().doNotHaveFullyQualifiedName("org.htmlunit.util.Cookie")
490             .and().doNotHaveFullyQualifiedName("org.htmlunit.DefaultCredentialsProvider")
491 
492             .and().resideOutsideOfPackage("org.htmlunit.httpclient..")
493         .should().dependOnClassesThat().resideInAnyPackage("org.apache.http..");
494 
495     /**
496      * Make sure the HttpWebConnection is the only entry into the HttpClient adapter.
497      */
498     @ArchTest
499     public static final ArchRule httpWebConnection = noClasses()
500         .that()
501             .doNotHaveFullyQualifiedName("org.htmlunit.HttpWebConnection")
502             .and().areNotInnerClasses()
503             .and().areNotMemberClasses()
504 
505             .and().doNotHaveFullyQualifiedName("org.htmlunit.WebClient")
506             .and().doNotHaveFullyQualifiedName("org.htmlunit.WebRequest")
507             .and().doNotHaveFullyQualifiedName("org.htmlunit.javascript.host.xml.XMLHttpRequest")
508             .and().doNotHaveFullyQualifiedName("org.htmlunit.DefaultCredentialsProvider")
509 
510             .and().resideOutsideOfPackage("org.htmlunit.httpclient..")
511         .should().dependOnClassesThat().resideInAnyPackage("org.htmlunit.httpclient..");
512 
513     /**
514      * Do not use core-js dependencies outside of the adapter.
515      */
516     @ArchTest
517     public static final ArchRule corejsPackageRule = noClasses()
518         .that()
519             .doNotHaveFullyQualifiedName("org.htmlunit.WebConsole")
520             .and().doNotHaveFullyQualifiedName("org.htmlunit.WebConsole$1")
521             .and().doNotHaveFullyQualifiedName("org.htmlunit.ScriptException")
522             .and().doNotHaveFullyQualifiedName("org.htmlunit.html.DomElement")
523             .and().doNotHaveFullyQualifiedName("org.htmlunit.html.HtmlDialog")
524             .and().doNotHaveFullyQualifiedName("org.htmlunit.html.HtmlDialog$1")
525             .and().doNotHaveFullyQualifiedName("org.htmlunit.html.HtmlInput")
526             .and().doNotHaveFullyQualifiedName("org.htmlunit.html.HtmlPage")
527             .and().doNotHaveFullyQualifiedName("org.htmlunit.util.WebClientUtils")
528 
529             .and().resideOutsideOfPackage("org.htmlunit.javascript..")
530 
531             .and().resideOutsideOfPackage("org.htmlunit.corejs..")
532         .should().dependOnClassesThat().resideInAnyPackage("org.htmlunit.corejs..");
533 
534     /**
535      * Do not use core-js ScriptRuntime outside of the JavaScriptEngine.
536      */
537     @ArchTest
538     public static final ArchRule corejsScriptRuntimeRule = noClasses()
539         .that()
540             .doNotHaveFullyQualifiedName("org.htmlunit.javascript.host.URLSearchParams")
541 
542             .and().doNotHaveFullyQualifiedName("org.htmlunit.javascript.HtmlUnitContextFactory")
543             .and().doNotHaveFullyQualifiedName("org.htmlunit.javascript.JavaScriptEngine")
544             .and().doNotHaveFullyQualifiedName("org.htmlunit.javascript.JavaScriptEngine$2")
545             .and().resideOutsideOfPackage("org.htmlunit.corejs..")
546 
547         .should().dependOnClassesThat().haveFullyQualifiedName("org.htmlunit.corejs.javascript.ScriptRuntime");
548 
549     /**
550      * Do not use core-js org.htmlunit.corejs.javascript.Undefined.instance directly.
551      */
552     @ArchTest
553     public static final ArchRule corejsUndefinedRule = noClasses()
554         .that()
555             .doNotHaveFullyQualifiedName("org.htmlunit.javascript.JavaScriptEngine")
556             .and().resideOutsideOfPackage("org.htmlunit.corejs..")
557 
558         .should().dependOnClassesThat().haveFullyQualifiedName("org.htmlunit.corejs.javascript.Undefined");
559 
560     /**
561      * Do not use core-js ScriptRuntime outside of the JavaScriptEngine.
562      */
563     @ArchTest
564     public static final ArchRule javaScriptEngineRule = noClasses()
565         .that()
566             .resideOutsideOfPackage("org.htmlunit.javascript..")
567 
568             .and().doNotHaveFullyQualifiedName("org.htmlunit.WebClient")
569             .and().doNotHaveFullyQualifiedName("org.htmlunit.ScriptResult")
570             .and().doNotHaveFullyQualifiedName("org.htmlunit.html.DomElement")
571             .and().doNotHaveFullyQualifiedName("org.htmlunit.html.HtmlPage")
572             .and().doNotHaveFullyQualifiedName("org.htmlunit.util.DebuggingWebConnection")
573 
574         .should().dependOnClassesThat().haveFullyQualifiedName("org.htmlunit.javascript.JavaScriptEngine");
575 
576     /**
577      * Do not use jetty.
578      */
579     @ArchTest
580     public static final ArchRule jettyPackageRule = noClasses()
581         .should().dependOnClassesThat().resideInAnyPackage("org.eclipse.jetty..");
582 }