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.junit.bct.annotations;
018
019import static org.apache.juneau.commons.utils.ThrowableUtils.*;
020import static org.apache.juneau.commons.utils.Utils.*;
021import static org.apache.juneau.junit.bct.BctConfiguration.*;
022import static org.apache.juneau.commons.lang.TriState.*;
023
024import java.util.*;
025
026import org.apache.juneau.junit.bct.*;
027import org.junit.jupiter.api.extension.*;
028
029/**
030 * JUnit 5 extension that processes {@link BctConfig} annotations.
031 *
032 * <p>
033 * This extension automatically sets BCT settings before test execution and clears them after.
034 * It supports both class-level and method-level annotations, with method-level taking precedence.
035 */
036public class BctConfigExtension implements BeforeEachCallback, AfterEachCallback {
037
038   @Override
039   public void beforeEach(ExtensionContext context) throws Exception {
040      var al = getAnnotations(context);
041
042      if (al.isEmpty())
043         return;
044
045      clear();
046
047      al.stream().map(x -> x.sortMaps()).filter(x -> neq(x, UNSET)).findFirst().ifPresent(x -> set(BCT_SORT_MAPS, x == TRUE));
048      al.stream().map(x -> x.sortCollections()).filter(x -> neq(x, UNSET)).findFirst().ifPresent(x -> set(BCT_SORT_COLLECTIONS, x == TRUE));
049      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));
050   }
051
052   private static void setConverter(Class<? extends BeanConverter> x) {
053      safe(()->{
054         var c = x.getDeclaredConstructor();
055         c.setAccessible(true);
056         set(c.newInstance());
057      }, e -> rex(e, "Failed to instantiate BeanConverter: {0}. It must have a no-arg constructor.", x.getName()));
058   }
059
060   @Override
061   public void afterEach(ExtensionContext context) throws Exception {
062      var al = getAnnotations(context);
063
064      if (al.isEmpty())
065         return;
066
067      clear();
068   }
069
070   private static List<BctConfig> getAnnotations(ExtensionContext context) {
071      var l = new ArrayList<BctConfig>();
072      context.getTestMethod().map(x -> x.getAnnotation(BctConfig.class)).ifPresent(x -> l.add(x));
073      context.getTestClass().map(x -> x.getAnnotation(BctConfig.class)).ifPresent(x -> l.add(x));
074      return l;
075   }
076}