content: /**
   * Copyright (c) 2016-present, RxJava Contributors.
   *
   * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
   * compliance with the License. You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software distributed under the License is
   * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
   * the License for the specific language governing permissions and limitations under the License.
   */
  
  package io.reactivex.interop;
  
  import java.lang.reflect.*;
  import java.util.*;
  import java.util.concurrent.*;
  
  import org.junit.Test;
  import org.reactivestreams.*;
  
  import hu.akarnokd.reactivestreams.extensions.RelaxedSubscriber;
  import io.reactivex.common.*;
  import io.reactivex.common.exceptions.TestException;
  import io.reactivex.common.functions.*;
  import io.reactivex.common.internal.functions.Functions;
  import io.reactivex.flowable.*;
  import io.reactivex.observable.*;
  import io.reactivex.observable.Observable;
  import io.reactivex.observable.Observer;
  
  /**
   * Check that static and instance methods validate their parameters against
   * null and invalid values properly.
   */
  public class ParamValidationCheckerTest {
  
      @Test(timeout = 30000)
      public void checkFlowable() {
          checkClass(Flowable.class);
      }
  
      @Test(timeout = 30000)
      public void checkObservable() {
          checkClass(Observable.class);
      }
  
      @Test(timeout = 30000)
      public void checkSingle() {
          checkClass(Single.class);
      }
  
      @Test(timeout = 30000)
      public void checkMaybe() {
          checkClass(Maybe.class);
      }
  
      @Test(timeout = 30000)
      public void checkCompletable() {
          checkClass(Completable.class);
      }
  
      @Test(timeout = 30000)
      public void checkParallelFlowable() {
          checkClass(ParallelFlowable.class);
      }
  
      @Test(timeout = 30000)
      public void checkInterop() {
          checkClass(RxJava3Interop.class);
      }
  
      // ---------------------------------------------------------------------------------------
      // ---------------------------------------------------------------------------------------
  
      static Map<String, List<ParamOverride>> overrides;
  
      static Map<String, List<ParamIgnore>> ignores;
  
      static Map<Class<?>, Object> defaultValues;
  
      static Map<Class<?>, List<Object>> defaultInstances;
  
      static {
          overrides = new HashMap<String, List<ParamOverride>>();
  
          // ***********************************************************************************************************************
  
          // zero index allowed
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.NON_NEGATIVE, "elementAt", Long.TYPE));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.NON_NEGATIVE, "elementAt", Long.TYPE, Object.class));
  
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.NON_NEGATIVE, "elementAtOrError", Long.TYPE));
  
          // negative skip count is ignored
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "skip", Long.TYPE));
          // negative skip time is considered as zero skip time
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "skip", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "skip", Long.TYPE, TimeUnit.class, Scheduler.class));
  
          // can start with zero initial request
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.NON_NEGATIVE, "test", Long.TYPE));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.NON_NEGATIVE, "test", Long.TYPE, Boolean.TYPE));
  
          // negative timeout time is considered as zero timeout time
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "timeout", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "timeout", Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "timeout", Long.TYPE, TimeUnit.class, Publisher.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "timeout", Long.TYPE, TimeUnit.class, Scheduler.class, Publisher.class));
  
          // negative buffer time is considered as zero buffer time
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "buffer", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "buffer", Long.TYPE, TimeUnit.class, Integer.TYPE));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "buffer", Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "buffer", Long.TYPE, TimeUnit.class, Scheduler.class, Integer.TYPE));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "buffer", Long.TYPE, TimeUnit.class, Scheduler.class, Integer.TYPE, Callable.class, Boolean.TYPE));
  
          // negative time/skip is considered zero time/skip
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "buffer", Long.TYPE, Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "buffer", Long.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "buffer", Long.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class, Callable.class));
          addOverride(new ParamOverride(Flowable.class, 1, ParamMode.ANY, "buffer", Long.TYPE, Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Flowable.class, 1, ParamMode.ANY, "buffer", Long.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Flowable.class, 1, ParamMode.ANY, "buffer", Long.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class, Callable.class));
  
          // negative timeout is allowed
          addOverride(new ParamOverride(Flowable.class, 1, ParamMode.ANY, "fromFuture", Future.class, Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Flowable.class, 1, ParamMode.ANY, "fromFuture", Future.class, Long.TYPE, TimeUnit.class, Scheduler.class));
  
          // null default is allowed
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "blockingLast", Object.class));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "timer", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "timer", Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "interval", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "interval", Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "interval", Long.TYPE, Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "interval", Long.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Flowable.class, 1, ParamMode.ANY, "interval", Long.TYPE, Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Flowable.class, 1, ParamMode.ANY, "interval", Long.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "delay", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "delay", Long.TYPE, TimeUnit.class, Boolean.TYPE));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "delay", Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "delay", Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE));
  
          // null default is allowed
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "blockingMostRecent", Object.class));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "delaySubscription", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "delaySubscription", Long.TYPE, TimeUnit.class, Scheduler.class));
  
          // null default is allowed
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "blockingFirst", Object.class));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "debounce", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "debounce", Long.TYPE, TimeUnit.class, Scheduler.class));
  
          // null Action allowed
          addOverride(new ParamOverride(Flowable.class, 1, ParamMode.ANY, "onBackpressureBuffer", Long.TYPE, Action.class, BackpressureOverflowStrategy.class));
  
          // zero repeat is allowed
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.NON_NEGATIVE, "repeat", Long.TYPE));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "replay", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "replay", Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Flowable.class, 1, ParamMode.ANY, "replay", Integer.TYPE, Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Flowable.class, 1, ParamMode.ANY, "replay", Integer.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Flowable.class, 1, ParamMode.ANY, "replay", Function.class, Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Flowable.class, 1, ParamMode.ANY, "replay", Function.class, Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Flowable.class, 2, ParamMode.ANY, "replay", Function.class, Integer.TYPE, Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Flowable.class, 2, ParamMode.ANY, "replay", Function.class, Integer.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class));
  
          // zero retry is allowed
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.NON_NEGATIVE, "retry", Long.TYPE));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.NON_NEGATIVE, "retry", Long.TYPE, Predicate.class));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "throttleWithTimeout", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "throttleWithTimeout", Long.TYPE, TimeUnit.class, Scheduler.class));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "take", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "take", Long.TYPE, TimeUnit.class, Scheduler.class));
  
          // zero retry is allowed
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.NON_NEGATIVE, "take", Long.TYPE));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "sample", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "sample", Long.TYPE, TimeUnit.class, Boolean.TYPE));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "sample", Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "sample", Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "takeLast", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "takeLast", Long.TYPE, TimeUnit.class, Boolean.TYPE));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "takeLast", Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "takeLast", Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "takeLast", Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE, Integer.TYPE));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.NON_NEGATIVE, "takeLast", Long.TYPE, Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.NON_NEGATIVE, "takeLast", Long.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.NON_NEGATIVE, "takeLast", Long.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE, Integer.TYPE));
          addOverride(new ParamOverride(Flowable.class, 1, ParamMode.ANY, "takeLast", Long.TYPE, Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Flowable.class, 1, ParamMode.ANY, "takeLast", Long.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Flowable.class, 1, ParamMode.ANY, "takeLast", Long.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE, Integer.TYPE));
  
          // take last 0 is allowed
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.NON_NEGATIVE, "takeLast", Integer.TYPE));
  
          // skip last 0 is allowed
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.NON_NEGATIVE, "skipLast", Integer.TYPE));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "skipLast", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "skipLast", Long.TYPE, TimeUnit.class, Boolean.TYPE));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "skipLast", Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "skipLast", Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "skipLast", Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE, Integer.TYPE));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "throttleFirst", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "throttleFirst", Long.TYPE, TimeUnit.class, Scheduler.class));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "throttleLast", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "throttleLast", Long.TYPE, TimeUnit.class, Scheduler.class));
  
  
          // negative buffer time is considered as zero buffer time
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "window", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "window", Long.TYPE, TimeUnit.class, Long.TYPE));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "window", Long.TYPE, TimeUnit.class, Long.TYPE, Boolean.TYPE));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "window", Long.TYPE, TimeUnit.class, Scheduler.class, Long.TYPE));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "window", Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "window", Long.TYPE, TimeUnit.class, Scheduler.class, Long.TYPE, Boolean.TYPE));
          addOverride(new ParamOverride(Flowable.class, 0, ParamMode.ANY, "window", Long.TYPE, TimeUnit.class, Scheduler.class, Long.TYPE, Boolean.TYPE, Integer.TYPE));
  
          // ***********************************************************************************************************************
  
          // negative timeout time is considered as zero timeout time
          addOverride(new ParamOverride(Completable.class, 0, ParamMode.ANY, "timeout", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Completable.class, 0, ParamMode.ANY, "timeout", Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Completable.class, 0, ParamMode.ANY, "timeout", Long.TYPE, TimeUnit.class, CompletableSource.class));
          addOverride(new ParamOverride(Completable.class, 0, ParamMode.ANY, "timeout", Long.TYPE, TimeUnit.class, Scheduler.class, CompletableSource.class));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Completable.class, 0, ParamMode.ANY, "timer", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Completable.class, 0, ParamMode.ANY, "timer", Long.TYPE, TimeUnit.class, Scheduler.class));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Completable.class, 0, ParamMode.ANY, "delay", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Completable.class, 0, ParamMode.ANY, "delay", Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Completable.class, 0, ParamMode.ANY, "delay", Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE));
  
          // zero repeat is allowed
          addOverride(new ParamOverride(Completable.class, 0, ParamMode.NON_NEGATIVE, "repeat", Long.TYPE));
  
          // zero retry is allowed
          addOverride(new ParamOverride(Completable.class, 0, ParamMode.NON_NEGATIVE, "retry", Long.TYPE));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Completable.class, 0, ParamMode.ANY, "blockingGet", Long.TYPE, TimeUnit.class));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Completable.class, 0, ParamMode.ANY, "blockingAwait", Long.TYPE, TimeUnit.class));
  
          // ***********************************************************************************************************************
  
          // negative timeout time is considered as zero timeout time
          addOverride(new ParamOverride(Maybe.class, 0, ParamMode.ANY, "timeout", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Maybe.class, 0, ParamMode.ANY, "timeout", Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Maybe.class, 0, ParamMode.ANY, "timeout", Long.TYPE, TimeUnit.class, MaybeSource.class));
          addOverride(new ParamOverride(Maybe.class, 0, ParamMode.ANY, "timeout", Long.TYPE, TimeUnit.class, Scheduler.class, MaybeSource.class));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Maybe.class, 0, ParamMode.ANY, "timer", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Maybe.class, 0, ParamMode.ANY, "timer", Long.TYPE, TimeUnit.class, Scheduler.class));
  
          // negative timeout is allowed
          addOverride(new ParamOverride(Maybe.class, 1, ParamMode.ANY, "fromFuture", Future.class, Long.TYPE, TimeUnit.class));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Maybe.class, 0, ParamMode.ANY, "delay", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Maybe.class, 0, ParamMode.ANY, "delay", Long.TYPE, TimeUnit.class, Scheduler.class));
  
          // zero repeat is allowed
          addOverride(new ParamOverride(Maybe.class, 0, ParamMode.NON_NEGATIVE, "repeat", Long.TYPE));
  
          // zero retry is allowed
          addOverride(new ParamOverride(Maybe.class, 0, ParamMode.NON_NEGATIVE, "retry", Long.TYPE));
          addOverride(new ParamOverride(Maybe.class, 0, ParamMode.NON_NEGATIVE, "retry", Long.TYPE, Predicate.class));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Maybe.class, 0, ParamMode.ANY, "delaySubscription", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Maybe.class, 0, ParamMode.ANY, "delaySubscription", Long.TYPE, TimeUnit.class, Scheduler.class));
  
          // ***********************************************************************************************************************
  
          // negative timeout time is considered as zero timeout time
          addOverride(new ParamOverride(Single.class, 0, ParamMode.ANY, "timeout", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Single.class, 0, ParamMode.ANY, "timeout", Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Single.class, 0, ParamMode.ANY, "timeout", Long.TYPE, TimeUnit.class, SingleSource.class));
          addOverride(new ParamOverride(Single.class, 0, ParamMode.ANY, "timeout", Long.TYPE, TimeUnit.class, Scheduler.class, SingleSource.class));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Single.class, 0, ParamMode.ANY, "timer", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Single.class, 0, ParamMode.ANY, "timer", Long.TYPE, TimeUnit.class, Scheduler.class));
  
          // negative timeout is allowed
          addOverride(new ParamOverride(Single.class, 1, ParamMode.ANY, "fromFuture", Future.class, Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Single.class, 1, ParamMode.ANY, "fromFuture", Future.class, Long.TYPE, TimeUnit.class, Scheduler.class));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Single.class, 0, ParamMode.ANY, "delay", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Single.class, 0, ParamMode.ANY, "delay", Long.TYPE, TimeUnit.class, Scheduler.class));
  
  
          // zero repeat is allowed
          addOverride(new ParamOverride(Single.class, 0, ParamMode.NON_NEGATIVE, "repeat", Long.TYPE));
  
          // zero retry is allowed
          addOverride(new ParamOverride(Single.class, 0, ParamMode.NON_NEGATIVE, "retry", Long.TYPE));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Single.class, 0, ParamMode.ANY, "delaySubscription", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Single.class, 0, ParamMode.ANY, "delaySubscription", Long.TYPE, TimeUnit.class, Scheduler.class));
  
          // ***********************************************************************************************************************
  
          // zero index allowed
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.NON_NEGATIVE, "elementAt", Long.TYPE));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.NON_NEGATIVE, "elementAt", Long.TYPE, Object.class));
  
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.NON_NEGATIVE, "elementAtOrError", Long.TYPE));
  
          // negative skip count is ignored
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "skip", Long.TYPE));
          // negative skip time is considered as zero skip time
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "skip", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "skip", Long.TYPE, TimeUnit.class, Scheduler.class));
  
          // negative timeout time is considered as zero timeout time
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "timeout", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "timeout", Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "timeout", Long.TYPE, TimeUnit.class, ObservableSource.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "timeout", Long.TYPE, TimeUnit.class, Scheduler.class, ObservableSource.class));
  
          // negative buffer time is considered as zero buffer time
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "buffer", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "buffer", Long.TYPE, TimeUnit.class, Integer.TYPE));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "buffer", Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "buffer", Long.TYPE, TimeUnit.class, Scheduler.class, Integer.TYPE));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "buffer", Long.TYPE, TimeUnit.class, Scheduler.class, Integer.TYPE, Callable.class, Boolean.TYPE));
  
          // negative time/skip is considered zero time/skip
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "buffer", Long.TYPE, Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "buffer", Long.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "buffer", Long.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class, Callable.class));
          addOverride(new ParamOverride(Observable.class, 1, ParamMode.ANY, "buffer", Long.TYPE, Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Observable.class, 1, ParamMode.ANY, "buffer", Long.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Observable.class, 1, ParamMode.ANY, "buffer", Long.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class, Callable.class));
  
          // negative timeout is allowed
          addOverride(new ParamOverride(Observable.class, 1, ParamMode.ANY, "fromFuture", Future.class, Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Observable.class, 1, ParamMode.ANY, "fromFuture", Future.class, Long.TYPE, TimeUnit.class, Scheduler.class));
  
          // null default is allowed
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "blockingLast", Object.class));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "timer", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "timer", Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "interval", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "interval", Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "interval", Long.TYPE, Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "interval", Long.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Observable.class, 1, ParamMode.ANY, "interval", Long.TYPE, Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Observable.class, 1, ParamMode.ANY, "interval", Long.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "delay", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "delay", Long.TYPE, TimeUnit.class, Boolean.TYPE));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "delay", Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "delay", Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE));
  
          // null default is allowed
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "blockingMostRecent", Object.class));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "delaySubscription", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "delaySubscription", Long.TYPE, TimeUnit.class, Scheduler.class));
  
          // null default is allowed
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "blockingFirst", Object.class));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "debounce", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "debounce", Long.TYPE, TimeUnit.class, Scheduler.class));
  
          // zero repeat is allowed
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.NON_NEGATIVE, "repeat", Long.TYPE));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "replay", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "replay", Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Observable.class, 1, ParamMode.ANY, "replay", Integer.TYPE, Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Observable.class, 1, ParamMode.ANY, "replay", Integer.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Observable.class, 1, ParamMode.ANY, "replay", Function.class, Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Observable.class, 1, ParamMode.ANY, "replay", Function.class, Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Observable.class, 2, ParamMode.ANY, "replay", Function.class, Integer.TYPE, Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Observable.class, 2, ParamMode.ANY, "replay", Function.class, Integer.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class));
  
          // zero retry is allowed
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.NON_NEGATIVE, "retry", Long.TYPE));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.NON_NEGATIVE, "retry", Long.TYPE, Predicate.class));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "throttleWithTimeout", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "throttleWithTimeout", Long.TYPE, TimeUnit.class, Scheduler.class));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "take", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "take", Long.TYPE, TimeUnit.class, Scheduler.class));
  
          // zero retry is allowed
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.NON_NEGATIVE, "take", Long.TYPE));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "sample", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "sample", Long.TYPE, TimeUnit.class, Boolean.TYPE));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "sample", Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "sample", Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "takeLast", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "takeLast", Long.TYPE, TimeUnit.class, Boolean.TYPE));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "takeLast", Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "takeLast", Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "takeLast", Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE, Integer.TYPE));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.NON_NEGATIVE, "takeLast", Long.TYPE, Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.NON_NEGATIVE, "takeLast", Long.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.NON_NEGATIVE, "takeLast", Long.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE, Integer.TYPE));
          addOverride(new ParamOverride(Observable.class, 1, ParamMode.ANY, "takeLast", Long.TYPE, Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Observable.class, 1, ParamMode.ANY, "takeLast", Long.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Observable.class, 1, ParamMode.ANY, "takeLast", Long.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE, Integer.TYPE));
  
          // take last 0 is allowed
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.NON_NEGATIVE, "takeLast", Integer.TYPE));
  
          // skip last 0 is allowed
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.NON_NEGATIVE, "skipLast", Integer.TYPE));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "skipLast", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "skipLast", Long.TYPE, TimeUnit.class, Boolean.TYPE));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "skipLast", Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "skipLast", Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "skipLast", Long.TYPE, TimeUnit.class, Scheduler.class, Boolean.TYPE, Integer.TYPE));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "throttleFirst", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "throttleFirst", Long.TYPE, TimeUnit.class, Scheduler.class));
  
          // negative time is considered as zero time
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "throttleLast", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "throttleLast", Long.TYPE, TimeUnit.class, Scheduler.class));
  
  
          // negative buffer time is considered as zero buffer time
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "window", Long.TYPE, TimeUnit.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "window", Long.TYPE, TimeUnit.class, Long.TYPE));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "window", Long.TYPE, TimeUnit.class, Long.TYPE, Boolean.TYPE));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "window", Long.TYPE, TimeUnit.class, Scheduler.class, Long.TYPE));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "window", Long.TYPE, TimeUnit.class, Scheduler.class));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "window", Long.TYPE, TimeUnit.class, Scheduler.class, Long.TYPE, Boolean.TYPE));
          addOverride(new ParamOverride(Observable.class, 0, ParamMode.ANY, "window", Long.TYPE, TimeUnit.class, Scheduler.class, Long.TYPE, Boolean.TYPE, Integer.TYPE));
  
          // zero index allowed
          addOverride(new ParamOverride(RxJava3Interop.class, 1, ParamMode.NON_NEGATIVE, "elementAt", Flowable.class, Long.TYPE));
          addOverride(new ParamOverride(RxJava3Interop.class, 1, ParamMode.NON_NEGATIVE, "elementAt", Flowable.class, Long.TYPE, Object.class));
  
          addOverride(new ParamOverride(RxJava3Interop.class, 1, ParamMode.NON_NEGATIVE, "elementAtOrError", Flowable.class, Long.TYPE));
  
          // -----------------------------------------------------------------------------------
  
          ignores = new HashMap<String, List<ParamIgnore>>();
  
          // needs special param validation due to (long)start + end - 1 <= Integer.MAX_VALUE
          addIgnore(new ParamIgnore(Flowable.class, "range", Integer.TYPE, Integer.TYPE));
          addIgnore(new ParamIgnore(Flowable.class, "rangeLong", Long.TYPE, Long.TYPE));
          addIgnore(new ParamIgnore(Flowable.class, "intervalRange", Long.TYPE, Long.TYPE, Long.TYPE, TimeUnit.class));
          addIgnore(new ParamIgnore(Flowable.class, "intervalRange", Long.TYPE, Long.TYPE, Long.TYPE, Long.TYPE, TimeUnit.class));
          addIgnore(new ParamIgnore(Flowable.class, "intervalRange", Long.TYPE, Long.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class));
          addIgnore(new ParamIgnore(Flowable.class, "intervalRange", Long.TYPE, Long.TYPE, Long.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class));
  
          addIgnore(new ParamIgnore(Flowable.class, "unsafeCreate", Publisher.class));
  
          // needs special param validation due to (long)start + end - 1 <= Integer.MAX_VALUE
          addIgnore(new ParamIgnore(Observable.class, "range", Integer.TYPE, Integer.TYPE));
          addIgnore(new ParamIgnore(Observable.class, "rangeLong", Long.TYPE, Long.TYPE));
          addIgnore(new ParamIgnore(Observable.class, "intervalRange", Long.TYPE, Long.TYPE, Long.TYPE, TimeUnit.class));
          addIgnore(new ParamIgnore(Observable.class, "intervalRange", Long.TYPE, Long.TYPE, Long.TYPE, Long.TYPE, TimeUnit.class));
          addIgnore(new ParamIgnore(Observable.class, "intervalRange", Long.TYPE, Long.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class));
          addIgnore(new ParamIgnore(Observable.class, "intervalRange", Long.TYPE, Long.TYPE, Long.TYPE, Long.TYPE, TimeUnit.class, Scheduler.class));
  
          addIgnore(new ParamIgnore(Observable.class, "unsafeCreate", ObservableSource.class));
  
          addIgnore(new ParamIgnore(Maybe.class, "unsafeCreate", MaybeSource.class));
  
          addIgnore(new ParamIgnore(Single.class, "unsafeCreate", SingleSource.class));
  
          addIgnore(new ParamIgnore(Completable.class, "unsafeCreate", CompletableSource.class));
  
          addIgnore(new ParamIgnore(RxJava3Interop.class, "when", Scheduler.class, Function.class));
  
          // -----------------------------------------------------------------------------------
  
          defaultValues = new HashMap<Class<?>, Object>();
  
          defaultValues.put(Publisher.class, new NeverPublisher());
          defaultValues.put(Flowable.class, new NeverPublisher());
  
          defaultValues.put(ObservableSource.class, new NeverObservable());
          defaultValues.put(Observable.class, new NeverObservable());
  
          defaultValues.put(SingleSource.class, new NeverSingle());
          defaultValues.put(Single.class, new NeverSingle());
  
          defaultValues.put(MaybeSource.class, new NeverMaybe());
          defaultValues.put(Maybe.class, new NeverMaybe());
  
          defaultValues.put(CompletableSource.class, new NeverCompletable());
          defaultValues.put(Completable.class, new NeverCompletable());
  
          defaultValues.put(Action.class, Functions.EMPTY_ACTION);
          defaultValues.put(Runnable.class, Functions.EMPTY_RUNNABLE);
          defaultValues.put(Consumer.class, Functions.emptyConsumer());
          defaultValues.put(LongConsumer.class, Functions.EMPTY_LONG_CONSUMER);
          defaultValues.put(Function.class, Functions.justFunction(1));
          defaultValues.put(Callable.class, Functions.justCallable(1));
          defaultValues.put(Iterable.class, Collections.emptyList());
          defaultValues.put(Object.class, 1);
          defaultValues.put(Class.class, Integer.class);
          Object af = new AllFunctionals();
          for (Class<?> interfaces : AllFunctionals.class.getInterfaces()) {
              defaultValues.put(interfaces, af);
          }
          defaultValues.put(TimeUnit.class, TimeUnit.SECONDS);
          defaultValues.put(Scheduler.class, Schedulers.single());
          defaultValues.put(BackpressureStrategy.class, BackpressureStrategy.MISSING);
          defaultValues.put(BackpressureOverflowStrategy.class, BackpressureOverflowStrategy.ERROR);
          defaultValues.put(Throwable.class, new TestException());
  
          defaultValues.put(Publisher[].class, new Publisher[] { new NeverPublisher(), new NeverPublisher() });
          defaultValues.put(ObservableSource[].class, new ObservableSource[] { new NeverObservable(), new NeverObservable() });
          defaultValues.put(SingleSource[].class, new SingleSource[] { new NeverSingle(), new NeverSingle() });
          defaultValues.put(MaybeSource[].class, new MaybeSource[] { new NeverMaybe(), new NeverMaybe() });
          defaultValues.put(CompletableSource[].class, new CompletableSource[] { new NeverCompletable(), new NeverCompletable() });
  
          defaultValues.put(Object[].class, new Object[] { new Object(), new Object() });
          defaultValues.put(Future.class, new FutureTask<Object>(Functions.EMPTY_RUNNABLE, 1));
  
          defaultValues.put(ParallelFlowable.class, ParallelFlowable.from(Flowable.never()));
          defaultValues.put(Subscriber[].class, new Subscriber[] { new AllFunctionals() });
  
          defaultValues.put(ParallelFailureHandling.class, ParallelFailureHandling.ERROR);
  
          // -----------------------------------------------------------------------------------
  
          defaultInstances = new HashMap<Class<?>, List<Object>>();
  
  //        addDefaultInstance(Flowable.class, Flowable.empty(), "Empty()");
  //        addDefaultInstance(Flowable.class, Flowable.empty().hide(), "Empty().Hide()");
          addDefaultInstance(Flowable.class, Flowable.just(1), "Just(1)");
          addDefaultInstance(Flowable.class, Flowable.just(1).hide(), "Just(1).Hide()");
  //        addDefaultInstance(Flowable.class, Flowable.range(1, 3), "Range(1, 3)");
  //        addDefaultInstance(Flowable.class, Flowable.range(1, 3).hide(), "Range(1, 3).Hide()");
  
  //        addDefaultInstance(Observable.class, Observable.empty(), "Empty()");
  //        addDefaultInstance(Observable.class, Observable.empty().hide(), "Empty().Hide()");
          addDefaultInstance(Observable.class, Observable.just(1), "Just(1)");
          addDefaultInstance(Observable.class, Observable.just(1).hide(), "Just(1).Hide()");
  //        addDefaultInstance(Observable.class, Observable.range(1, 3), "Range(1, 3)");
  //        addDefaultInstance(Observable.class, Observable.range(1, 3).hide(), "Range(1, 3).Hide()");
  
          addDefaultInstance(Completable.class, Completable.complete(), "Complete()");
          addDefaultInstance(Completable.class, Completable.complete().hide(), "Complete().hide()");
  
          addDefaultInstance(Single.class, Single.just(1), "Just(1)");
          addDefaultInstance(Single.class, Single.just(1).hide(), "Just(1).Hide()");
  
          addDefaultInstance(Maybe.class, Maybe.just(1), "Just(1)");
          addDefaultInstance(Maybe.class, Maybe.just(1).hide(), "Just(1).Hide()");
  
          addDefaultInstance(ParallelFlowable.class, Flowable.just(1).parallel(), "Just(1)");
  }
  
      static void addIgnore(ParamIgnore ignore) {
          String key = ignore.toString();
          List<ParamIgnore> list = ignores.get(key);
          if (list == null) {
              list = new ArrayList<ParamIgnore>();
              ignores.put(key, list);
          }
          list.add(ignore);
      }
  
      static void addOverride(ParamOverride ignore) {
          String key = ignore.toString();
          List<ParamOverride> list = overrides.get(key);
          if (list == null) {
              list = new ArrayList<ParamOverride>();
              overrides.put(key, list);
          }
          list.add(ignore);
      }
  
      static void addDefaultInstance(Class<?> clazz, Object o, String tag) {
          List<Object> list = defaultInstances.get(clazz);
          if (list == null) {
              list = new ArrayList<Object>();
              defaultInstances.put(clazz, list);
          }
          list.add(o);
          list.add(tag);
      }
  
      Object defaultPrimitive(Class<?> clazz, ParamOverride override) {
          if (Integer.TYPE == clazz) {
              if (override != null) {
                  return 0;
              }
              return 1;
          }
  
          if (Long.TYPE == clazz) {
              if (override != null) {
                  return 0L;
              }
              return 1L;
          }
  
          if (Boolean.TYPE == clazz) {
              return true;
          }
  
          return null;
      }
  
      void addCheckPrimitive(Class<?> clazz, ParamOverride override, List<Object> values) {
          if (Integer.TYPE == clazz) {
              values.add(-2);
              values.add(override != null && override.mode == ParamMode.ANY);
              values.add(-1);
              values.add(override != null && override.mode == ParamMode.ANY);
              values.add(0);
              values.add(override != null);
              values.add(1);
              values.add(true); // should succeed
              values.add(2);
              values.add(true);
          }
  
          if (Long.TYPE == clazz) {
              values.add(-2L);
              values.add(override != null && override.mode == ParamMode.ANY);
              values.add(-1L);
              values.add(override != null && override.mode == ParamMode.ANY);
              values.add(0L);
              values.add(override != null);
              values.add(1L);
              values.add(true); // should succeed
              values.add(2L);
              values.add(true);
          }
  
          if (Boolean.TYPE == clazz) {
              values.add(false);
              values.add(true);
              values.add(true);
              values.add(true);
          }
      }
  
      void checkClass(Class<?> clazz) {
          List<Throwable> errors = TestCommonHelper.trackPluginErrors();
          try {
              StringBuilder b = new StringBuilder();
              int fail = 0;
  
              outer:
              for (Method m : clazz.getMethods()) {
                  if (m.getDeclaringClass() != clazz) {
                      continue;
                  }
  
                  String key = clazz.getName() + " " + m.getName();
  
                  List<ParamIgnore> ignoreList = ignores.get(key);
                  if (ignoreList != null) {
                      for (ParamIgnore e : ignoreList) {
                          if (Arrays.equals(e.arguments, m.getParameterTypes())) {
                              System.out.println("CheckClass - ignore: " + m);
                              continue outer;
                          }
                      }
                  }
  
                  List<ParamOverride> overrideList = overrides.get(key);
  
                  List<Object> baseObjects = new ArrayList<Object>();
  
                  if ((m.getModifiers() & Modifier.STATIC) != 0) {
                      baseObjects.add(null);
                      baseObjects.add("NULL");
                  } else {
                      List<Object> defaultInstancesList = defaultInstances.get(clazz);
                      if (defaultInstancesList == null) {
                          b.append("\r\nNo default instances for " + clazz);
                          fail++;
                          continue outer;
                      }
                      baseObjects.addAll(defaultInstancesList);
                  }
  
                  for (int ii = 0; ii < baseObjects.size(); ii += 2) {
                      Object baseObject = baseObjects.get(ii);
                      Object tag = baseObjects.get(ii + 1);
                      Class<?>[] params = m.getParameterTypes();
                      int n = params.length;
  
                      for (int i = 0; i < n; i++) {
                          ParamOverride overrideEntry = null;
                          if (overrideList != null) {
                              for (ParamOverride e : overrideList) {
                                  if (e.index == i && Arrays.equals(e.arguments, params)) {
                                      overrideEntry = e;
                                      break;
                                  }
                              }
                          }
  
                          Class<?> entryClass = params[i];
  
                          Object[] callParams = new Object[n];
  
                          for (int j = 0; j < n; j++) {
                              if (j != i) {
                                  if (params[j].isPrimitive()) {
                                      ParamOverride overrideParam = null;
                                      if (overrideList != null) {
                                          for (ParamOverride e : overrideList) {
                                              if (e.index == j && Arrays.equals(e.arguments, params)) {
                                                  overrideParam = e;
                                                  break;
                                              }
                                          }
                                      }
                                      Object def = defaultPrimitive(params[j], overrideParam);
                                      if (def == null) {
                                          b.append("\r\nMissing default non-null value for " + m + " # " + j + " (" + params[j] + ")");
                                          fail++;
                                          continue outer;
                                      }
                                      callParams[j] = def;
                                  } else {
                                      Object def = defaultValues.get(params[j]);
                                      if (def == null) {
                                          b.append("\r\nMissing default non-null value for " + m + " # " + j + " (" + params[j] + ")");
                                          fail++;
                                          continue outer;
                                      }
                                      callParams[j] = def;
                                  }
                              }
                          }
  
                          List<Object> entryValues = new ArrayList<Object>();
  
                          if (entryClass.isPrimitive()) {
                              addCheckPrimitive(params[i], overrideEntry, entryValues);
                          } else {
                              entryValues.add(null);
                              entryValues.add(overrideEntry != null && overrideEntry.mode == ParamMode.ANY);
  
                              Object def = defaultValues.get(params[i]);
                              if (def == null) {
                                  b.append("\r\nMissing default non-null value for " + m + " # " + i + " (" + params[i] + ")");
                                  fail++;
                                  continue outer;
                              }
                              entryValues.add(def);
                              entryValues.add(true);
                          }
  
                          for (int k = 0; k < entryValues.size(); k += 2) {
                              Object[] callParams2 = callParams.clone();
  
                              Object p = entryValues.get(k);
                              callParams2[i] = p;
                              boolean shouldSucceed = (Boolean)entryValues.get(k + 1);
  
                              boolean success = false;
                              Throwable error = null;
                              errors.clear();
                              try {
                                  m.invoke(baseObject, callParams2);
                                  success = true;
                              } catch (Throwable ex) {
                                  // let it fail
                                  error = ex;
                              }
  
                              if (success != shouldSucceed) {
                                  fail++;
                                  if (shouldSucceed) {
                                      b.append("\r\nFailed (should have succeeded): " + m + " # " + i + " = " + p + ", tag = " + tag + ", params = " + Arrays.toString(callParams2));
                                      b.append("\r\n    ").append(error);
                                      if (error.getCause() != null) {
                                          b.append("\r\n    ").append(error.getCause());
                                      }
                                  } else {
                                      b.append("\r\nNo failure (should have failed): " + m + " # " + i + " = " + p + ", tag = " + tag + ", params = " + Arrays.toString(callParams2));
                                  }
                                  continue outer;
                              }
                              if (!errors.isEmpty()) {
                                  fail++;
                                  b.append("\r\nUndeliverable errors:");
                                  for (Throwable err : errors) {
                                      b.append("\r\n    ").append(err);
                                      if (err.getCause() != null) {
                                          b.append("\r\n    ").append(err.getCause());
                                      }
                                  }
                                  continue outer;
                              }
                          }
                      }
                  }
              }
  
              if (fail != 0) {
                  throw new AssertionError("Parameter validation problems: " + fail + b.toString());
              }
          } finally {
              RxJavaCommonPlugins.reset();
          }
      }
  
      @SuppressWarnings("rawtypes")
      static final class AllFunctionals
      implements BiFunction, BiConsumer,
      Predicate, BiPredicate, BooleanSupplier,
      Function3, Function4, Function5, Function6, Function7, Function8, Function9,
      FlowableOnSubscribe, ObservableOnSubscribe, SingleOnSubscribe, MaybeOnSubscribe, CompletableOnSubscribe,
      FlowableTransformer, ObservableTransformer, SingleTransformer, MaybeTransformer, CompletableTransformer,
      Subscriber, RelaxedSubscriber, Observer, SingleObserver, MaybeObserver, CompletableObserver,
      FlowableOperator, ObservableOperator, SingleOperator, MaybeOperator, CompletableOperator,
      Comparator, ParallelTransformer
      {
  
          @Override
          public ParallelFlowable apply(ParallelFlowable upstream) {
              return null;
          }
  
          @Override
          public Object apply(Object t1, Object t2, Object t3, Object t4, Object t5, Object t6, Object t7, Object t8,
                  Object t9) throws Exception {
              return null;
          }
  
          @Override
          public Object apply(Object t1, Object t2, Object t3, Object t4, Object t5, Object t6, Object t7, Object t8)
                  throws Exception {
              return null;
          }
  
          @Override
          public Object apply(Object t1, Object t2, Object t3, Object t4, Object t5, Object t6, Object t7)
                  throws Exception {
              return null;
          }
  
          @Override
          public Object apply(Object t1, Object t2, Object t3, Object t4, Object t5, Object t6) throws Exception {
              return null;
          }
  
          @Override
          public Object apply(Object t1, Object t2, Object t3, Object t4, Object t5) throws Exception {
              return null;
          }
  
          @Override
          public Object apply(Object t1, Object t2, Object t3, Object t4) throws Exception {
              return null;
          }
  
          @Override
          public Object apply(Object t1, Object t2, Object t3) throws Exception {
              return null;
          }
  
          @Override
          public void accept(Object t1, Object t2) throws Exception {
          }
  
          @Override
          public Object apply(Object t1, Object t2) throws Exception {
              return null;
          }
  
          @Override
          public void subscribe(CompletableEmitter e) throws Exception {
          }
  
          @Override
          public void subscribe(MaybeEmitter e) throws Exception {
          }
  
          @Override
          public void subscribe(SingleEmitter e) throws Exception {
          }
  
          @Override
          public void subscribe(ObservableEmitter e) throws Exception {
          }
  
          @Override
          public void subscribe(FlowableEmitter e) throws Exception {
          }
  
          @Override
          public boolean test(Object t1, Object t2) throws Exception {
              return false;
          }
  
          @Override
          public boolean test(Object t) throws Exception {
              return false;
          }
  
          @Override
          public void onSuccess(Object t) {
          }
  
          @Override
          public void onSubscribe(Disposable d) {
          }
  
          @Override
          public void onSubscribe(Subscription s) {
              s.request(Long.MAX_VALUE);
          }
  
          @Override
          public void onNext(Object t) {
          }
  
          @Override
          public void onError(Throwable t) {
          }
  
          @Override
          public void onComplete() {
          }
  
          @Override
          public CompletableSource apply(Completable upstream) {
              return upstream;
          }
  
          @Override
          public MaybeSource apply(Maybe upstream) {
              return upstream;
          }
  
          @Override
          public SingleSource apply(Single upstream) {
              return upstream;
          }
  
          @Override
          public ObservableSource apply(Observable upstream) {
              return upstream;
          }
  
          @Override
          public Publisher apply(Flowable upstream) {
              return upstream;
          }
  
          @Override
          public CompletableObserver apply(CompletableObserver observer) throws Exception {
              return observer;
          }
  
          @Override
          public MaybeObserver apply(MaybeObserver observer) throws Exception {
              return observer;
          }
  
          @Override
          public SingleObserver apply(SingleObserver observer) throws Exception {
              return observer;
          }
  
          @Override
          public Observer apply(Observer observer) throws Exception {
              return observer;
          }
  
          @Override
          public Subscriber apply(Subscriber observer) throws Exception {
              return observer;
          }
  
          @Override
          public boolean getAsBoolean() throws Exception {
              return false;
          }
  
          @Override
          public int compare(Object o1, Object o2) {
              return 0;
          }
      }
  
      enum ParamMode {
          ANY,
          NON_NEGATIVE
      }
  
      static final class ParamIgnore {
          final Class<?> clazz;
          final String name;
          final Class<?>[] arguments;
  
          ParamIgnore(Class<?> clazz, String name, Class<?>... arguments) {
              this.clazz = clazz;
              this.name = name;
              this.arguments = arguments;
          }
  
          @Override
          public String toString() {
              return clazz.getName() + " " + name;
          }
      }
  
      static final class ParamOverride {
          final Class<?> clazz;
          final int index;
          final ParamMode mode;
          final String name;
          final Class<?>[] arguments;
  
          ParamOverride(Class<?> clazz, int index, ParamMode mode, String name, Class<?>... arguments) {
              this.clazz = clazz;
              this.index = index;
              this.mode = mode;
              this.name = name;
              this.arguments = arguments;
  
              try {
                  clazz.getMethod(name, arguments);
              } catch (Exception ex) {
                  throw new AssertionError(ex);
              }
          }
  
          @Override
          public String toString() {
              return clazz.getName() + " " + name;
          }
      }
  
      static final class NeverPublisher extends Flowable<Object> {
  
          @Override
          public void subscribeActual(Subscriber<? super Object> s) {
              // not invoked, the class is a placeholder default value
          }
  
          @Override
          public String toString() {
              return "NeverFlowable";
          }
      }
  
      static final class NeverObservable extends Observable<Object> {
  
          @Override
          public void subscribeActual(Observer<? super Object> s) {
              // not invoked, the class is a placeholder default value
          }
  
          @Override
          public String toString() {
              return "NeverFlowable";
          }
      }
  
      static final class NeverSingle extends Single<Object> {
  
          @Override
          public void subscribeActual(SingleObserver<? super Object> s) {
              // not invoked, the class is a placeholder default value
          }
  
          @Override
          public String toString() {
              return "NeverSingle";
          }
      }
  
      static final class NeverMaybe extends Maybe<Object> {
  
          @Override
          public void subscribeActual(MaybeObserver<? super Object> s) {
              // not invoked, the class is a placeholder default value
          }
  
          @Override
          public String toString() {
              return "NeverMaybe";
          }
      }
      static final class NeverCompletable extends Completable {
  
          @Override
          public void subscribeActual(CompletableObserver s) {
              // not invoked, the class is a placeholder default value
          }
  
          @Override
          public String toString() {
              return "NeverCompletable";
          }
      }
  }
  