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;
030
031/**
032 * The entry point class for supporting JUnit4 like Parameterized test, where we can use constructor
033 * to pass parameters.
034 * <p>
035 * JUnit5's {@link org.junit.jupiter.params.ParameterizedClass} will create separated test classes,
036 * which is different with JUnit4 and {@link org.junit.jupiter.params.ParameterizedTest} does not
037 * support passing parameters through constructors.
038 * <p>
039 * When you want to use this provider, annotation the test class with
040 * {@link HBaseParameterizedTestTemplate}, and provide a static method named "parameters" for
041 * providing the arguments. The method must have no parameter, and return a Stream&lt;Arguments&gt;.
042 * All the test method should be marked with {@link org.junit.jupiter.api.TestTemplate}, not
043 * {@link org.junit.jupiter.api.Test} or {@link org.junit.jupiter.params.ParameterizedTest}.
044 * @see HBaseParameterizedTestTemplate
045 * @see HBaseParameterizedInvocationContext
046 * @see HBaseParameterizedParameterResolver
047 */
048@InterfaceAudience.Private
049public class HBaseParameterizedTemplateProvider implements TestTemplateInvocationContextProvider {
050
051  private static final String PARAMETERS_METHOD_NAME = "parameters";
052
053  @Override
054  public boolean supportsTestTemplate(ExtensionContext context) {
055    return context.getTestClass()
056      .map(c -> c.isAnnotationPresent(HBaseParameterizedTestTemplate.class)).orElse(false);
057  }
058
059  @Override
060  public Stream<TestTemplateInvocationContext>
061    provideTestTemplateInvocationContexts(ExtensionContext context) {
062    Class<?> testClass = context.getRequiredTestClass();
063    // get parameters
064    Method method;
065    try {
066      method = testClass.getDeclaredMethod(PARAMETERS_METHOD_NAME);
067    } catch (NoSuchMethodException e) {
068      throw new ExtensionConfigurationException(
069        "Test class must declare static " + PARAMETERS_METHOD_NAME + " method");
070    }
071
072    if (!Modifier.isStatic(method.getModifiers())) {
073      throw new ExtensionConfigurationException(PARAMETERS_METHOD_NAME + " method must be static");
074    }
075    if (method.getParameterCount() > 0) {
076      throw new ExtensionConfigurationException(
077        PARAMETERS_METHOD_NAME + " method must not have any parameters");
078    }
079
080    Stream<Arguments> args;
081    try {
082      args = (Stream<Arguments>) method.invoke(null);
083    } catch (IllegalAccessException | InvocationTargetException e) {
084      throw new ExtensionConfigurationException("failed to invoke parameters method", e);
085    }
086    // get display name
087    String namePattern = testClass.getAnnotation(HBaseParameterizedTestTemplate.class).name();
088
089    return args.map(arg -> new HBaseParameterizedInvocationContext(arg, namePattern));
090  }
091
092}