001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.bct; 018 019import static org.apache.juneau.commons.lang.TriState.*; 020import static org.apache.juneau.commons.utils.Shorts.*; 021import static org.apache.juneau.commons.utils.ThrowableUtils.*; 022import static org.apache.juneau.bct.BctConfiguration.*; 023 024import java.util.*; 025 026import org.junit.jupiter.api.extension.*; 027 028/** 029 * JUnit 5 extension that processes {@link BctConfig} annotations. 030 * 031 * <p> 032 * This extension automatically sets BCT settings before test execution and clears them after. 033 * It supports both class-level and method-level annotations, with method-level taking precedence. 034 */ 035public class BctConfigExtension implements BeforeEachCallback, AfterEachCallback { 036 037 @Override 038 public void beforeEach(ExtensionContext context) throws Exception { 039 var al = getAnnotations(context); 040 041 clear(); 042 043 al.stream().map(x -> x.sortMaps()).filter(x -> neq(x, UNSET)).findFirst().ifPresent(x -> set(BCT_SORT_MAPS, x == TRUE)); 044 al.stream().map(x -> x.sortCollections()).filter(x -> neq(x, UNSET)).findFirst().ifPresent(x -> set(BCT_SORT_COLLECTIONS, x == TRUE)); 045 al.stream().map(x -> x.beanConverter()).filter(x -> neq(x, BeanConverter.class)).findFirst().map(x -> eq(x, BasicBeanConverter.class) ? null : x).ifPresent(x -> setConverter(x)); 046 } 047 048 @SuppressWarnings({ 049 "java:S3011" // Reflection access needed for test configuration 050 }) 051 private static void setConverter(Class<? extends BeanConverter> x) { 052 safe(()->{ 053 var c = x.getDeclaredConstructor(); 054 c.setAccessible(true); 055 set(c.newInstance()); 056 }, e -> rex(e, "Failed to instantiate BeanConverter: %s. It must have a no-arg constructor.", x.getName())); // HTT - requires invalid converter without no-arg constructor 057 } 058 059 @Override 060 public void afterEach(ExtensionContext context) throws Exception { 061 clear(); 062 } 063 064 private static List<BctConfig> getAnnotations(ExtensionContext context) { 065 var l = new ArrayList<BctConfig>(); 066 context.getTestMethod().map(x -> x.getAnnotation(BctConfig.class)).ifPresent(l::add); 067 context.getTestClass().map(x -> x.getAnnotation(BctConfig.class)).ifPresent(l::add); 068 return l; 069 } 070}