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