001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase; 019 020import java.lang.reflect.InvocationTargetException; 021import java.lang.reflect.Method; 022import java.lang.reflect.Modifier; 023import java.util.stream.Stream; 024import org.apache.yetus.audience.InterfaceAudience; 025import org.junit.jupiter.api.extension.ExtensionConfigurationException; 026import org.junit.jupiter.api.extension.ExtensionContext; 027import org.junit.jupiter.api.extension.TestTemplateInvocationContext; 028import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider; 029import org.junit.jupiter.params.provider.Arguments; 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032 033/** 034 * The entry point class for supporting JUnit4 like Parameterized test, where we can use constructor 035 * to pass parameters. 036 * <p> 037 * JUnit5's {@link org.junit.jupiter.params.ParameterizedClass} will create separated test classes, 038 * which is different with JUnit4 and {@link org.junit.jupiter.params.ParameterizedTest} does not 039 * support passing parameters through constructors. 040 * <p> 041 * When you want to use this provider, annotation the test class with 042 * {@link HBaseParameterizedTestTemplate}, and provide a static method named "parameters" for 043 * providing the arguments. The method must have no parameter, and return a Stream<Arguments>. 044 * All the test method should be marked with {@link org.junit.jupiter.api.TestTemplate}, not 045 * {@link org.junit.jupiter.api.Test} or {@link org.junit.jupiter.params.ParameterizedTest}. 046 * @see HBaseParameterizedTestTemplate 047 * @see HBaseParameterizedInvocationContext 048 * @see HBaseParameterizedParameterResolver 049 */ 050@InterfaceAudience.Private 051public class HBaseParameterizedTemplateProvider implements TestTemplateInvocationContextProvider { 052 053 private static final Logger LOG = 054 LoggerFactory.getLogger(HBaseParameterizedTemplateProvider.class); 055 056 private static final String PARAMETERS_METHOD_NAME = "parameters"; 057 058 @Override 059 public boolean supportsTestTemplate(ExtensionContext context) { 060 return context.getTestClass() 061 .map(c -> c.isAnnotationPresent(HBaseParameterizedTestTemplate.class)).orElse(false); 062 } 063 064 private Method findParametersMethod(final Class<?> testClass) { 065 Class<?> clazz = testClass; 066 for (;;) { 067 try { 068 return clazz.getDeclaredMethod(PARAMETERS_METHOD_NAME); 069 } catch (NoSuchMethodException e) { 070 Class<?> parentClass = clazz.getSuperclass(); 071 LOG.debug("Can not find method {} in class {}, try its parent class {}", 072 PARAMETERS_METHOD_NAME, clazz, parentClass); 073 if (parentClass == null) { 074 throw new ExtensionConfigurationException("Can not find a static " 075 + PARAMETERS_METHOD_NAME + " method in class " + testClass + "or its super classes"); 076 } 077 clazz = parentClass; 078 } 079 } 080 081 } 082 083 @Override 084 public Stream<TestTemplateInvocationContext> 085 provideTestTemplateInvocationContexts(ExtensionContext context) { 086 Class<?> testClass = context.getRequiredTestClass(); 087 // get parameters 088 Method method = findParametersMethod(testClass); 089 if (!Modifier.isStatic(method.getModifiers())) { 090 throw new ExtensionConfigurationException(PARAMETERS_METHOD_NAME + " method must be static"); 091 } 092 if (method.getParameterCount() > 0) { 093 throw new ExtensionConfigurationException( 094 PARAMETERS_METHOD_NAME + " method must not have any parameters"); 095 } 096 097 Stream<Arguments> args; 098 try { 099 args = (Stream<Arguments>) method.invoke(null); 100 } catch (IllegalAccessException | InvocationTargetException e) { 101 throw new ExtensionConfigurationException("failed to invoke parameters method", e); 102 } 103 // get display name 104 String namePattern = testClass.getAnnotation(HBaseParameterizedTestTemplate.class).name(); 105 106 return args.map(arg -> new HBaseParameterizedInvocationContext(arg, namePattern)); 107 } 108 109}