Merge "Fix bug and migrate test." into sc-dev

This commit is contained in:
Kweku Adams
2021-02-02 22:45:03 +00:00
committed by Android (Google) Code Review
2 changed files with 222 additions and 112 deletions

View File

@@ -791,15 +791,10 @@ class JobConcurrencyManager {
mNumUnspecialized = mConfigMaxTotal;
mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_TOP);
mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_BG);
mNumUnspecialized -= mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP);
mNumUnspecialized -= mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG);
calculateUnspecializedRemaining();
}
private void calculateUnspecializedRemaining() {
mNumUnspecializedRemaining = mNumUnspecialized;
mNumUnspecializedRemaining = mConfigMaxTotal;
for (int i = mNumRunningJobs.size() - 1; i >= 0; --i) {
mNumUnspecializedRemaining -= mNumRunningJobs.valueAt(i);
mNumUnspecializedRemaining -= Math.max(mNumRunningJobs.valueAt(i),
mConfigNumReservedSlots.get(mNumRunningJobs.keyAt(i)));
}
}
@@ -882,24 +877,42 @@ class JobConcurrencyManager {
mNumUnspecialized = mConfigMaxTotal;
final int numTop = mNumRunningJobs.get(WORK_TYPE_TOP)
+ mNumPendingJobs.get(WORK_TYPE_TOP);
final int resTop = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_TOP), numTop);
int resTop = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_TOP), numTop);
mNumActuallyReservedSlots.put(WORK_TYPE_TOP, resTop);
mNumUnspecialized -= resTop;
final int numBg = mNumRunningJobs.get(WORK_TYPE_BG) + mNumPendingJobs.get(WORK_TYPE_BG);
final int resBg = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_BG), numBg);
int resBg = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_BG), numBg);
mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg);
mNumUnspecialized -= resBg;
calculateUnspecializedRemaining();
mNumUnspecializedRemaining = mNumUnspecialized;
// Account for already running jobs after we've assigned the minimum number of slots.
int unspecializedAssigned;
int extraRunning = (mNumRunningJobs.get(WORK_TYPE_TOP) - resTop);
if (extraRunning > 0) {
unspecializedAssigned = Math.max(0,
Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP) - resTop,
extraRunning));
resTop += unspecializedAssigned;
mNumUnspecializedRemaining -= extraRunning;
}
extraRunning = (mNumRunningJobs.get(WORK_TYPE_BG) - resBg);
if (extraRunning > 0) {
unspecializedAssigned = Math.max(0,
Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG) - resBg, extraRunning));
resBg += unspecializedAssigned;
mNumUnspecializedRemaining -= extraRunning;
}
// Assign remaining unspecialized based on ranking.
int unspecializedAssigned = Math.max(0,
Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP),
Math.min(mNumUnspecializedRemaining, numTop - resTop)));
unspecializedAssigned = Math.max(0,
Math.min(mNumUnspecializedRemaining,
Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP), numTop) - resTop));
mNumActuallyReservedSlots.put(WORK_TYPE_TOP, resTop + unspecializedAssigned);
mNumUnspecializedRemaining -= unspecializedAssigned;
unspecializedAssigned = Math.max(0,
Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG),
Math.min(mNumUnspecializedRemaining, numBg - resBg)));
Math.min(mNumUnspecializedRemaining,
Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG), numBg) - resBg));
mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg + unspecializedAssigned);
mNumUnspecializedRemaining -= unspecializedAssigned;
}

View File

@@ -21,9 +21,11 @@ import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE;
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import android.util.Log;
import android.annotation.NonNull;
import android.util.Pair;
import android.util.SparseIntArray;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
@@ -58,106 +60,112 @@ public class WorkCountTrackerTest {
* Represents running and pending jobs.
*/
class Jobs {
public int runningFg;
public int runningBg;
public int pendingFg;
public int pendingBg;
public final SparseIntArray running = new SparseIntArray();
public final SparseIntArray pending = new SparseIntArray();
public void maybeEnqueueJobs(double startRatio, double fgJobRatio) {
while (mRandom.nextDouble() < startRatio) {
if (mRandom.nextDouble() < fgJobRatio) {
pendingFg++;
pending.put(WORK_TYPE_TOP, pending.get(WORK_TYPE_TOP) + 1);
} else {
pendingBg++;
pending.put(WORK_TYPE_BG, pending.get(WORK_TYPE_BG) + 1);
}
}
}
public void maybeFinishJobs(double stopRatio) {
for (int i = runningBg; i > 0; i--) {
for (int i = running.get(WORK_TYPE_BG); i > 0; i--) {
if (mRandom.nextDouble() < stopRatio) {
runningBg--;
running.put(WORK_TYPE_BG, running.get(WORK_TYPE_BG) - 1);
}
}
for (int i = runningFg; i > 0; i--) {
for (int i = running.get(WORK_TYPE_TOP); i > 0; i--) {
if (mRandom.nextDouble() < stopRatio) {
runningFg--;
running.put(WORK_TYPE_TOP, running.get(WORK_TYPE_TOP) - 1);
}
}
}
}
private void startPendingJobs(Jobs jobs, int totalMax, int maxBg, int minBg) {
mWorkCountTracker.setConfig(new JobConcurrencyManager.WorkTypeConfig("critical",
totalMax,
// defaultMin
List.of(Pair.create(WORK_TYPE_TOP, totalMax - maxBg),
Pair.create(WORK_TYPE_BG, minBg)),
// defaultMax
List.of(Pair.create(WORK_TYPE_BG, maxBg))));
private void startPendingJobs(Jobs jobs, int totalMax,
@NonNull List<Pair<Integer, Integer>> minLimits,
@NonNull List<Pair<Integer, Integer>> maxLimits) {
mWorkCountTracker.setConfig(new JobConcurrencyManager.WorkTypeConfig(
"test", totalMax, minLimits, maxLimits));
mWorkCountTracker.resetCounts();
for (int i = 0; i < jobs.runningFg; i++) {
mWorkCountTracker.incrementRunningJobCount(WORK_TYPE_TOP);
}
for (int i = 0; i < jobs.runningBg; i++) {
mWorkCountTracker.incrementRunningJobCount(WORK_TYPE_BG);
}
for (int i = 0; i < jobs.running.size(); ++i) {
final int workType = jobs.running.keyAt(i);
final int count = jobs.running.valueAt(i);
for (int i = 0; i < jobs.pendingFg; i++) {
mWorkCountTracker.incrementPendingJobCount(WORK_TYPE_TOP);
for (int c = 0; c < count; ++c) {
mWorkCountTracker.incrementRunningJobCount(workType);
}
}
for (int i = 0; i < jobs.pendingBg; i++) {
mWorkCountTracker.incrementPendingJobCount(WORK_TYPE_BG);
for (int i = 0; i < jobs.pending.size(); ++i) {
final int workType = jobs.pending.keyAt(i);
final int count = jobs.pending.valueAt(i);
for (int c = 0; c < count; ++c) {
mWorkCountTracker.incrementPendingJobCount(workType);
}
}
mWorkCountTracker.onCountDone();
while ((jobs.pendingFg > 0
while ((jobs.pending.get(WORK_TYPE_TOP) > 0
&& mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE)
|| (jobs.pendingBg > 0
|| (jobs.pending.get(WORK_TYPE_BG) > 0
&& mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE)) {
final boolean isStartingFg = mRandom.nextBoolean();
if (isStartingFg) {
if (jobs.pendingFg > 0
if (jobs.pending.get(WORK_TYPE_TOP) > 0
&& mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE) {
jobs.pendingFg--;
jobs.runningFg++;
jobs.pending.put(WORK_TYPE_TOP, jobs.pending.get(WORK_TYPE_TOP) - 1);
jobs.running.put(WORK_TYPE_TOP, jobs.running.get(WORK_TYPE_TOP) + 1);
mWorkCountTracker.stageJob(WORK_TYPE_TOP);
mWorkCountTracker.onJobStarted(WORK_TYPE_TOP);
}
} else {
if (jobs.pendingBg > 0
if (jobs.pending.get(WORK_TYPE_BG) > 0
&& mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE) {
jobs.pendingBg--;
jobs.runningBg++;
jobs.pending.put(WORK_TYPE_BG, jobs.pending.get(WORK_TYPE_BG) - 1);
jobs.running.put(WORK_TYPE_BG, jobs.running.get(WORK_TYPE_BG) + 1);
mWorkCountTracker.stageJob(WORK_TYPE_BG);
mWorkCountTracker.onJobStarted(WORK_TYPE_BG);
}
}
}
Log.i(TAG, "" + mWorkCountTracker);
}
/**
* Used by the following testRandom* tests.
*/
private void checkRandom(Jobs jobs, int numTests, int totalMax, int maxBg, int minBg,
private void checkRandom(Jobs jobs, int numTests, int totalMax,
@NonNull List<Pair<Integer, Integer>> minLimits,
@NonNull List<Pair<Integer, Integer>> maxLimits,
double startRatio, double fgJobRatio, double stopRatio) {
for (int i = 0; i < numTests; i++) {
jobs.maybeFinishJobs(stopRatio);
jobs.maybeEnqueueJobs(startRatio, fgJobRatio);
startPendingJobs(jobs, totalMax, maxBg, minBg);
startPendingJobs(jobs, totalMax, minLimits, maxLimits);
assertThat(jobs.runningFg).isAtMost(totalMax);
assertThat(jobs.runningBg).isAtMost(totalMax);
assertThat(jobs.runningFg + jobs.runningBg).isAtMost(totalMax);
assertThat(jobs.runningBg).isAtMost(maxBg);
int totalRunning = 0;
for (int r = 0; r < jobs.running.size(); ++r) {
final int numRunning = jobs.running.valueAt(r);
assertWithMessage(
"Work type " + jobs.running.keyAt(r) + " is running too many jobs")
.that(numRunning).isAtMost(totalMax);
totalRunning += numRunning;
}
assertThat(totalRunning).isAtMost(totalMax);
for (Pair<Integer, Integer> maxLimit : maxLimits) {
assertWithMessage("Work type " + maxLimit.first + " is running too many jobs")
.that(jobs.running.get(maxLimit.first)).isAtMost(maxLimit.second);
}
}
}
@@ -170,13 +178,14 @@ public class WorkCountTrackerTest {
final int numTests = 5000;
final int totalMax = 6;
final int maxBg = 4;
final int minBg = 2;
final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double stopRatio = 0.1;
final double fgJobRatio = 0.5;
final double startRatio = 0.1;
checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio , stopRatio);
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
startRatio, fgJobRatio, stopRatio);
}
@Test
@@ -185,13 +194,14 @@ public class WorkCountTrackerTest {
final int numTests = 5000;
final int totalMax = 2;
final int maxBg = 2;
final int minBg = 0;
final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final List<Pair<Integer, Integer>> minLimits = List.of();
final double stopRatio = 0.5;
final double fgJobRatio = 0.5;
final double startRatio = 0.5;
checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
startRatio, fgJobRatio, stopRatio);
}
@Test
@@ -200,13 +210,14 @@ public class WorkCountTrackerTest {
final int numTests = 5000;
final int totalMax = 2;
final int maxBg = 2;
final int minBg = 2;
final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double stopRatio = 0.5;
final double fgJobRatio = 0.5;
final double startRatio = 0.5;
checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
startRatio, fgJobRatio, stopRatio);
}
@Test
@@ -215,13 +226,14 @@ public class WorkCountTrackerTest {
final int numTests = 5000;
final int totalMax = 10;
final int maxBg = 2;
final int minBg = 0;
final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final List<Pair<Integer, Integer>> minLimits = List.of();
final double stopRatio = 0.5;
final double fgJobRatio = 0.5;
final double startRatio = 0.5;
checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
startRatio, fgJobRatio, stopRatio);
}
@Test
@@ -230,13 +242,14 @@ public class WorkCountTrackerTest {
final int numTests = 5000;
final int totalMax = 6;
final int maxBg = 4;
final int minBg = 2;
final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double stopRatio = 0.5;
final double fgJobRatio = 0.1;
final double startRatio = 0.5;
checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
startRatio, fgJobRatio, stopRatio);
}
@Test
@@ -245,13 +258,14 @@ public class WorkCountTrackerTest {
final int numTests = 5000;
final int totalMax = 6;
final int maxBg = 4;
final int minBg = 2;
final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double stopRatio = 0.5;
final double fgJobRatio = 0.9;
final double startRatio = 0.5;
checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
startRatio, fgJobRatio, stopRatio);
}
@Test
@@ -260,13 +274,14 @@ public class WorkCountTrackerTest {
final int numTests = 5000;
final int totalMax = 6;
final int maxBg = 4;
final int minBg = 2;
final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double stopRatio = 0.4;
final double fgJobRatio = 0.1;
final double startRatio = 0.5;
checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
startRatio, fgJobRatio, stopRatio);
}
@Test
@@ -275,53 +290,135 @@ public class WorkCountTrackerTest {
final int numTests = 5000;
final int totalMax = 6;
final int maxBg = 4;
final int minBg = 2;
final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
final double stopRatio = 0.4;
final double fgJobRatio = 0.9;
final double startRatio = 0.5;
checkRandom(jobs, numTests, totalMax, maxBg, minBg, startRatio, fgJobRatio, stopRatio);
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
startRatio, fgJobRatio, stopRatio);
}
/** Used by the following tests */
private void checkSimple(int totalMax, int maxBg, int minBg,
int runningFg, int runningBg, int pendingFg, int pendingBg,
int resultRunningFg, int resultRunningBg, int resultPendingFg, int resultPendingBg) {
private void checkSimple(int totalMax,
@NonNull List<Pair<Integer, Integer>> minLimits,
@NonNull List<Pair<Integer, Integer>> maxLimits,
@NonNull List<Pair<Integer, Integer>> running,
@NonNull List<Pair<Integer, Integer>> pending,
@NonNull List<Pair<Integer, Integer>> resultRunning,
@NonNull List<Pair<Integer, Integer>> resultPending) {
final Jobs jobs = new Jobs();
jobs.runningFg = runningFg;
jobs.runningBg = runningBg;
jobs.pendingFg = pendingFg;
jobs.pendingBg = pendingBg;
for (Pair<Integer, Integer> run : running) {
jobs.running.put(run.first, run.second);
}
for (Pair<Integer, Integer> pend : pending) {
jobs.pending.put(pend.first, pend.second);
}
startPendingJobs(jobs, totalMax, maxBg, minBg);
startPendingJobs(jobs, totalMax, minLimits, maxLimits);
// fail(mWorkerCountTracker.toString());
assertThat(jobs.runningFg).isEqualTo(resultRunningFg);
assertThat(jobs.runningBg).isEqualTo(resultRunningBg);
assertThat(jobs.pendingFg).isEqualTo(resultPendingFg);
assertThat(jobs.pendingBg).isEqualTo(resultPendingBg);
for (Pair<Integer, Integer> run : resultRunning) {
assertWithMessage("Incorrect running result for work type " + run.first)
.that(jobs.running.get(run.first)).isEqualTo(run.second);
}
for (Pair<Integer, Integer> pend : resultPending) {
assertWithMessage("Incorrect pending result for work type " + pend.first)
.that(jobs.pending.get(pend.first)).isEqualTo(pend.second);
}
}
@Test
public void testBasic() {
// Args are:
// First 3: Total-max, bg-max, bg-min.
// Next 2: Running FG / BG
// Next 2: Pending FG / BG
// Next 4: Result running FG / BG, pending FG/BG.
checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 1, 0, /*res run/pen=*/ 1, 0, 0, 0);
checkSimple(6,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
/* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
/* run */ List.of(),
/* pen */ List.of(Pair.create(WORK_TYPE_TOP, 1)),
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 1)),
/* resPen */ List.of());
checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 10, 0, /*res run/pen=*/ 6, 0, 4, 0);
checkSimple(6,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
/* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
/* run */ List.of(),
/* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10)),
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6)),
/* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 4)));
// When there are BG jobs pending, 2 (min-BG) jobs should run.
checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 10, 1, /*res run/pen=*/ 5, 1, 5, 0);
checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 10, 3, /*res run/pen=*/ 4, 2, 6, 1);
checkSimple(6,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
/* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
/* run */ List.of(),
/* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 1)),
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 1)),
/* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 5)));
checkSimple(6,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
/* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
/* run */ List.of(),
/* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)),
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)),
/* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 1)));
checkSimple(8, 6, 2, /*run=*/ 0, 0, /*pen=*/ 0, 49, /*res run/pen=*/ 0, 6, 0, 43);
checkSimple(8,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
/* max */ List.of(Pair.create(WORK_TYPE_BG, 6)),
/* run */ List.of(),
/* pen */ List.of(Pair.create(WORK_TYPE_BG, 49)),
/* resRun */ List.of(Pair.create(WORK_TYPE_BG, 6)),
/* resPen */ List.of(Pair.create(WORK_TYPE_BG, 43)));
checkSimple(6, 4, 2, /*run=*/ 6, 0, /*pen=*/ 10, 3, /*res run/pen=*/ 6, 0, 10, 3);
checkSimple(8,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
/* max */ List.of(Pair.create(WORK_TYPE_BG, 6)),
/* run */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 4)),
/* pen */ List.of(Pair.create(WORK_TYPE_BG, 49)),
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 6)),
/* resPen */ List.of(Pair.create(WORK_TYPE_BG, 47)));
checkSimple(8,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 6)),
/* run */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 4)),
/* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 49)),
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 4)),
/* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 8), Pair.create(WORK_TYPE_BG, 49)));
checkSimple(8,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 6)),
/* run */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 1)),
/* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 49)),
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 2)),
/* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 48)));
checkSimple(8,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 1)),
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 2)),
/* run */ List.of(Pair.create(WORK_TYPE_BG, 6)),
/* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 49)),
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 6)),
/* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 8), Pair.create(WORK_TYPE_BG, 49)));
checkSimple(6,
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
/* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
/* run */ List.of(Pair.create(WORK_TYPE_TOP, 6)),
/* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)),
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6)),
/* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)));
// This could happen if we lower the effective config due to higher memory pressure after
// we've already started running jobs. We shouldn't stop already running jobs, but also
// shouldn't start new ones.
checkSimple(5,
/* min */ List.of(),
/* max */ List.of(Pair.create(WORK_TYPE_BG, 1)),
/* run */ List.of(Pair.create(WORK_TYPE_BG, 6)),
/* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)),
/* resRun */ List.of(Pair.create(WORK_TYPE_BG, 6)),
/* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)));
}
}