private List<String> loadBalanceSuites(List<ForkedJvmInfo> jvmInfo,
      TestsCollection testsCollection, List<SuiteBalancer> balancers) {
    
    // Order test suites identically for balancers.
    // and split into replicated and non-replicated suites.
    Map<Boolean,List<String>> partitioned = sortAndSplitReplicated(testsCollection.testClasses);

    Collection<String> replicated = partitioned.get(true);
    Collection<String> suites     = partitioned.get(false);

    final List<SuiteBalancer> balancersWithFallback = new ArrayList<>(balancers);
    balancersWithFallback.add(new RoundRobinBalancer());

    // Go through all the balancers, the first one to assign a suite wins.
    final List<String> remaining = new ArrayList<>(suites);
    Collections.sort(remaining);

    final Map<Integer,List<Assignment>> perJvmAssignments = new HashMap<>();
    for (ForkedJvmInfo si : jvmInfo) {
      perJvmAssignments.put(si.id, new ArrayList<Assignment>());
    }
    final int jvmCount = jvmInfo.size();
    for (SuiteBalancer balancer : balancersWithFallback) {
      balancer.setOwner(this);
      final List<Assignment> assignments =
          balancer.assign(
              Collections.unmodifiableCollection(remaining), jvmCount, masterSeed());

      for (Assignment e : assignments) {
        if (e == null) {
          throw new RuntimeException("Balancer must return non-null assignments.");
        }
        if (!remaining.remove(e.suiteName)) {
          throw new RuntimeException("Balancer must return suite name as a key: " + e.suiteName);
        }

        log(String.format(Locale.ROOT,
            "Assignment hint: J%-2d (cost %5d) %s (by %s)",
            e.slaveId,
            e.estimatedCost,
            e.suiteName,
            balancer.getClass().getSimpleName()), Project.MSG_VERBOSE);

        perJvmAssignments.get(e.slaveId).add(e);
      }
    }

    if (remaining.size() != 0) {
      throw new RuntimeException("Not all suites assigned?: " + remaining);
    }

    if (shuffleOnSlave) {
      // Shuffle suites on slaves so that the result is always the same wrt master seed
      // (sort first, then shuffle with a constant seed).
      for (List<Assignment> assignments : perJvmAssignments.values()) {
        Collections.sort(assignments);
        Collections.shuffle(assignments, new Random(this.masterSeed()));
      }
    }

    // Take a fraction of suites scheduled as last on each slave and move them to a common
    // job-stealing queue.
    List<SuiteHint> stealingQueueWithHints = new ArrayList<>();
    for (ForkedJvmInfo si : jvmInfo) {
      final List<Assignment> assignments = perJvmAssignments.get(si.id);
      int moveToCommon = (int) (assignments.size() * dynamicAssignmentRatio);

      if (moveToCommon > 0) {
        final List<Assignment> movedToCommon = 
            assignments.subList(assignments.size() - moveToCommon, assignments.size());
        for (Assignment a : movedToCommon) {
          stealingQueueWithHints.add(new SuiteHint(a.suiteName, a.estimatedCost));
        }
        movedToCommon.clear();
      }

      final ArrayList<String> slaveSuites = (si.testSuites = new ArrayList<>());
      for (Assignment a : assignments) {
        slaveSuites.add(a.suiteName);
      }
    }

    // Sort stealing queue according to descending cost.
    Collections.sort(stealingQueueWithHints, SuiteHint.DESCENDING_BY_WEIGHT);

    // Append all replicated suites to each forked JVM, AFTER we process the stealing queue
    // to enforce all replicated suites run on each bound JVM.
    if (!replicated.isEmpty()) {
      for (ForkedJvmInfo si : jvmInfo) {
        for (String suite : replicated) {
            si.testSuites.add(suite);
        }
        if (shuffleOnSlave) {
          // Shuffle suites on slaves so that the result is always the same wrt master seed
          // (sort first, then shuffle with a constant seed).
          Collections.shuffle(si.testSuites, new Random(this.masterSeed()));
        }
      }
    }

    // Dump scheduling information.
    for (ForkedJvmInfo si : jvmInfo) {
      log("Forked JVM J" + si.id + " assignments (after shuffle):", Project.MSG_VERBOSE);
      for (String suiteName : si.testSuites) {
        log("  " + suiteName, Project.MSG_VERBOSE);
      }
    }

    log("Stealing queue:", Project.MSG_VERBOSE);
    for (SuiteHint suiteHint : stealingQueueWithHints) {
      log("  " + suiteHint.suiteName + " " + suiteHint.cost, Project.MSG_VERBOSE);
    }

    List<String> stealingQueue = new ArrayList<>(stealingQueueWithHints.size());
    for (SuiteHint suiteHint : stealingQueueWithHints) {
      stealingQueue.add(suiteHint.suiteName);
    }
    return stealingQueue;
  }
private List < String > loadBalanceSuites ( List < ForkedJvmInfo > jvmInfo , TestsCollection testsCollection , List < SuiteBalancer > balancers ) { Map < Boolean , List < String > > partitioned = sortAndSplitReplicated ( testsCollection . testClasses ) ; Collection < String > replicated = partitioned . get ( true ) ; Collection < String > suites = partitioned . get ( false ) ; final List < SuiteBalancer > balancersWithFallback = new ArrayList < > ( balancers ) ; balancersWithFallback . add ( new RoundRobinBalancer ( ) ) ; final List < String > remaining = new ArrayList < > ( suites ) ; Collections . sort ( remaining ) ; final Map < Integer , List < Assignment > > perJvmAssignments = new HashMap < > ( ) ; for ( ForkedJvmInfo si : jvmInfo ) { perJvmAssignments . put ( si . id , new ArrayList < Assignment > ( ) ) ; } final int jvmCount = jvmInfo . size ( ) ; for ( SuiteBalancer balancer : balancersWithFallback ) { balancer . setOwner ( this ) ; final List < Assignment > assignments = balancer . assign ( Collections . unmodifiableCollection ( remaining ) , jvmCount , masterSeed ( ) ) ; for ( Assignment e : assignments ) { if ( e == null ) { throw new RuntimeException ( "Balancer must return non-null assignments." ) ; } if ( ! remaining . remove ( e . suiteName ) ) { throw new RuntimeException ( "Balancer must return suite name as a key: " + e . suiteName ) ; } log ( String . format ( Locale . ROOT , "Assignment hint: J%-2d (cost %5d) %s (by %s)" , e . slaveId , e . estimatedCost , e . suiteName , balancer . getClass ( ) . getSimpleName ( ) ) , Project . MSG_VERBOSE ) ; perJvmAssignments . get ( e . slaveId ) . add ( e ) ; } } if ( remaining . size ( ) != 0 ) { throw new RuntimeException ( "Not all suites assigned?: " + remaining ) ; } if ( shuffleOnSlave ) { for ( List < Assignment > assignments : perJvmAssignments . values ( ) ) { Collections . sort ( assignments ) ; Collections . shuffle ( assignments , new Random ( this . masterSeed ( ) ) ) ; } } List < SuiteHint > stealingQueueWithHints = new ArrayList < > ( ) ; for ( ForkedJvmInfo si : jvmInfo ) { final List < Assignment > assignments = perJvmAssignments . get ( si . id ) ; int moveToCommon = ( int ) ( assignments . size ( ) * dynamicAssignmentRatio ) ; if ( moveToCommon > 0 ) { final List < Assignment > movedToCommon = assignments . subList ( assignments . size ( ) - moveToCommon , assignments . size ( ) ) ; for ( Assignment a : movedToCommon ) { stealingQueueWithHints . add ( new SuiteHint ( a . suiteName , a . estimatedCost ) ) ; } movedToCommon . clear ( ) ; } final ArrayList < String > slaveSuites = ( si . testSuites = new ArrayList < > ( ) ) ; for ( Assignment a : assignments ) { slaveSuites . add ( a . suiteName ) ; } } Collections . sort ( stealingQueueWithHints , SuiteHint . DESCENDING_BY_WEIGHT ) ; if ( ! replicated . isEmpty ( ) ) { for ( ForkedJvmInfo si : jvmInfo ) { for ( String suite : replicated ) { si . testSuites . add ( suite ) ; } if ( shuffleOnSlave ) { Collections . shuffle ( si . testSuites , new Random ( this . masterSeed ( ) ) ) ; } } } for ( ForkedJvmInfo si : jvmInfo ) { log ( "Forked JVM J" + si . id + " assignments (after shuffle):" , Project . MSG_VERBOSE ) ; for ( String suiteName : si . testSuites ) { log ( "  " + suiteName , Project . MSG_VERBOSE ) ; } } log ( "Stealing queue:" , Project . MSG_VERBOSE ) ; for ( SuiteHint suiteHint : stealingQueueWithHints ) { log ( "  " + suiteHint . suiteName + " " + suiteHint . cost , Project . MSG_VERBOSE ) ; } List < String > stealingQueue = new ArrayList < > ( stealingQueueWithHints . size ( ) ) ; for ( SuiteHint suiteHint : stealingQueueWithHints ) { stealingQueue . add ( suiteHint . suiteName ) ; } return stealingQueue ; }
Perform load balancing of the set of suites. Sets {@link ForkedJvmInfo#testSuites}
to suites preassigned to a given slave and returns a pool of suites
that should be load-balanced dynamically based on job stealing.