Skip to content

Commit bec8308

Browse files
authored
MAINT Uses pytest-xdist to parallelize tests (#13041)
1 parent b580ad5 commit bec8308

File tree

8 files changed

+58
-39
lines changed

8 files changed

+58
-39
lines changed

Makefile

+5
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,15 @@ test-doc:
3131
ifeq ($(BITS),64)
3232
$(PYTEST) $(shell find doc -name '*.rst' | sort)
3333
endif
34+
test-code-parallel: in
35+
$(PYTEST) -n auto --showlocals -v sklearn --durations=20
3436

3537
test-coverage:
3638
rm -rf coverage .coverage
3739
$(PYTEST) sklearn --showlocals -v --cov=sklearn --cov-report=html:coverage
40+
test-coverage-parallel:
41+
rm -rf coverage .coverage .coverage.*
42+
$(PYTEST) sklearn -n auto --showlocals -v --cov=sklearn --cov-report=html:coverage
3843

3944
test: test-code test-sphinxext test-doc
4045

build_tools/azure/install.cmd

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ IF "%PYTHON_ARCH%"=="64" (
1111
call deactivate
1212
@rem Clean up any left-over from a previous build
1313
conda remove --all -q -y -n %VIRTUALENV%
14-
conda create -n %VIRTUALENV% -q -y python=%PYTHON_VERSION% numpy scipy cython matplotlib pytest wheel pillow joblib
14+
conda create -n %VIRTUALENV% -q -y python=%PYTHON_VERSION% numpy scipy cython matplotlib pytest wheel pillow joblib pytest-xdist
1515

1616
call activate %VIRTUALENV%
1717
) else (

build_tools/azure/install.sh

+4
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ if [[ "$DISTRIB" == "conda" ]]; then
5151
TO_INSTALL="$TO_INSTALL matplotlib=$MATPLOTLIB_VERSION"
5252
fi
5353

54+
if [[ "$PYTHON_VERSION" == "*" ]]; then
55+
TO_INSTALL="$TO_INSTALL pytest-xdist"
56+
fi
57+
5458
make_conda $TO_INSTALL
5559

5660
elif [[ "$DISTRIB" == "ubuntu" ]]; then

build_tools/azure/test_script.cmd

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
@echo on
22

3-
@rem Only 64 bit uses conda
3+
@rem Only 64 bit uses conda and uses a python newer than 3.5
44
IF "%PYTHON_ARCH%"=="64" (
55
call activate %VIRTUALENV%
6+
set PYTEST_ARGS=%PYTEST_ARGS% -n2
67
)
78

89
mkdir %TMP_FOLDER%

build_tools/azure/test_script.sh

+4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ if [[ -n "$CHECK_WARNINGS" ]]; then
3232
TEST_CMD="$TEST_CMD -Werror::DeprecationWarning -Werror::FutureWarning"
3333
fi
3434

35+
if [[ "$PYTHON_VERSION" == "*" ]]; then
36+
TEST_CMD="$TEST_CMD -n2"
37+
fi
38+
3539
mkdir -p $TEST_DIR
3640
cp setup.cfg $TEST_DIR
3741
cd $TEST_DIR

sklearn/metrics/tests/test_common.py

+40-35
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ def test_symmetry():
509509

510510
@pytest.mark.parametrize(
511511
'name',
512-
set(ALL_METRICS) - METRIC_UNDEFINED_BINARY_MULTICLASS)
512+
sorted(set(ALL_METRICS) - METRIC_UNDEFINED_BINARY_MULTICLASS))
513513
def test_sample_order_invariance(name):
514514
random_state = check_random_state(0)
515515
y_true = random_state.randint(0, 2, size=(20, ))
@@ -561,7 +561,7 @@ def test_sample_order_invariance_multilabel_and_multioutput():
561561

562562
@pytest.mark.parametrize(
563563
'name',
564-
set(ALL_METRICS) - METRIC_UNDEFINED_BINARY_MULTICLASS)
564+
sorted(set(ALL_METRICS) - METRIC_UNDEFINED_BINARY_MULTICLASS))
565565
def test_format_invariance_with_1d_vectors(name):
566566
random_state = check_random_state(0)
567567
y1 = random_state.randint(0, 2, size=(20, ))
@@ -636,8 +636,8 @@ def test_format_invariance_with_1d_vectors(name):
636636

637637

638638
@pytest.mark.parametrize(
639-
'name',
640-
set(CLASSIFICATION_METRICS) - METRIC_UNDEFINED_BINARY_MULTICLASS)
639+
'name',
640+
sorted(set(CLASSIFICATION_METRICS) - METRIC_UNDEFINED_BINARY_MULTICLASS))
641641
def test_classification_invariance_string_vs_numbers_labels(name):
642642
# Ensure that classification metrics with string labels are invariant
643643
random_state = check_random_state(0)
@@ -767,22 +767,23 @@ def check_single_sample_multioutput(name):
767767

768768

769769
@pytest.mark.parametrize(
770-
'name',
771-
(set(ALL_METRICS)
772-
# Those metrics are not always defined with one sample
773-
# or in multiclass classification
774-
- METRIC_UNDEFINED_BINARY_MULTICLASS
775-
- set(THRESHOLDED_METRICS)))
770+
'name',
771+
sorted(
772+
set(ALL_METRICS)
773+
# Those metrics are not always defined with one sample
774+
# or in multiclass classification
775+
- METRIC_UNDEFINED_BINARY_MULTICLASS - set(THRESHOLDED_METRICS)))
776776
def test_single_sample(name):
777777
check_single_sample(name)
778778

779779

780-
@pytest.mark.parametrize('name', MULTIOUTPUT_METRICS | MULTILABELS_METRICS)
780+
@pytest.mark.parametrize('name',
781+
sorted(MULTIOUTPUT_METRICS | MULTILABELS_METRICS))
781782
def test_single_sample_multioutput(name):
782783
check_single_sample_multioutput(name)
783784

784785

785-
@pytest.mark.parametrize('name', MULTIOUTPUT_METRICS)
786+
@pytest.mark.parametrize('name', sorted(MULTIOUTPUT_METRICS))
786787
def test_multioutput_number_of_output_differ(name):
787788
y_true = np.array([[1, 0, 0, 1], [0, 1, 1, 1], [1, 1, 0, 1]])
788789
y_pred = np.array([[0, 0], [1, 0], [0, 0]])
@@ -791,7 +792,7 @@ def test_multioutput_number_of_output_differ(name):
791792
assert_raises(ValueError, metric, y_true, y_pred)
792793

793794

794-
@pytest.mark.parametrize('name', MULTIOUTPUT_METRICS)
795+
@pytest.mark.parametrize('name', sorted(MULTIOUTPUT_METRICS))
795796
def test_multioutput_regression_invariance_to_dimension_shuffling(name):
796797
# test invariance to dimension shuffling
797798
random_state = check_random_state(0)
@@ -846,7 +847,7 @@ def test_multilabel_representation_invariance():
846847
"dense and sparse indicator formats." % name)
847848

848849

849-
@pytest.mark.parametrize('name', MULTILABELS_METRICS)
850+
@pytest.mark.parametrize('name', sorted(MULTILABELS_METRICS))
850851
def test_raise_value_error_multilabel_sequences(name):
851852
# make sure the multilabel-sequence format raises ValueError
852853
multilabel_sequences = [
@@ -862,7 +863,7 @@ def test_raise_value_error_multilabel_sequences(name):
862863
assert_raises(ValueError, metric, seq, seq)
863864

864865

865-
@pytest.mark.parametrize('name', METRICS_WITH_NORMALIZE_OPTION)
866+
@pytest.mark.parametrize('name', sorted(METRICS_WITH_NORMALIZE_OPTION))
866867
def test_normalize_option_binary_classification(name):
867868
# Test in the binary case
868869
n_samples = 20
@@ -879,7 +880,7 @@ def test_normalize_option_binary_classification(name):
879880
measure)
880881

881882

882-
@pytest.mark.parametrize('name', METRICS_WITH_NORMALIZE_OPTION)
883+
@pytest.mark.parametrize('name', sorted(METRICS_WITH_NORMALIZE_OPTION))
883884
def test_normalize_option_multiclass_classification(name):
884885
# Test in the multiclass case
885886
random_state = check_random_state(0)
@@ -986,7 +987,7 @@ def check_averaging(name, y_true, y_true_binarize, y_pred, y_pred_binarize,
986987
raise ValueError("Metric is not recorded as having an average option")
987988

988989

989-
@pytest.mark.parametrize('name', METRICS_WITH_AVERAGING)
990+
@pytest.mark.parametrize('name', sorted(METRICS_WITH_AVERAGING))
990991
def test_averaging_multiclass(name):
991992
n_samples, n_classes = 50, 3
992993
random_state = check_random_state(0)
@@ -1003,7 +1004,8 @@ def test_averaging_multiclass(name):
10031004

10041005

10051006
@pytest.mark.parametrize(
1006-
'name', METRICS_WITH_AVERAGING | THRESHOLDED_METRICS_WITH_AVERAGING)
1007+
'name',
1008+
sorted(METRICS_WITH_AVERAGING | THRESHOLDED_METRICS_WITH_AVERAGING))
10071009
def test_averaging_multilabel(name):
10081010
n_samples, n_classes = 40, 5
10091011
_, y = make_multilabel_classification(n_features=1, n_classes=n_classes,
@@ -1019,7 +1021,7 @@ def test_averaging_multilabel(name):
10191021
y_pred, y_pred_binarize, y_score)
10201022

10211023

1022-
@pytest.mark.parametrize('name', METRICS_WITH_AVERAGING)
1024+
@pytest.mark.parametrize('name', sorted(METRICS_WITH_AVERAGING))
10231025
def test_averaging_multilabel_all_zeroes(name):
10241026
y_true = np.zeros((20, 3))
10251027
y_pred = np.zeros((20, 3))
@@ -1044,7 +1046,7 @@ def test_averaging_binary_multilabel_all_zeroes():
10441046
y_pred_binarize, is_multilabel=True)
10451047

10461048

1047-
@pytest.mark.parametrize('name', METRICS_WITH_AVERAGING)
1049+
@pytest.mark.parametrize('name', sorted(METRICS_WITH_AVERAGING))
10481050
def test_averaging_multilabel_all_ones(name):
10491051
y_true = np.ones((20, 3))
10501052
y_pred = np.ones((20, 3))
@@ -1136,9 +1138,10 @@ def check_sample_weight_invariance(name, metric, y1, y2):
11361138

11371139

11381140
@pytest.mark.parametrize(
1139-
'name',
1140-
(set(ALL_METRICS).intersection(set(REGRESSION_METRICS))
1141-
- METRICS_WITHOUT_SAMPLE_WEIGHT))
1141+
'name',
1142+
sorted(
1143+
set(ALL_METRICS).intersection(set(REGRESSION_METRICS)) -
1144+
METRICS_WITHOUT_SAMPLE_WEIGHT))
11421145
def test_regression_sample_weight_invariance(name):
11431146
n_samples = 50
11441147
random_state = check_random_state(0)
@@ -1150,9 +1153,10 @@ def test_regression_sample_weight_invariance(name):
11501153

11511154

11521155
@pytest.mark.parametrize(
1153-
'name',
1154-
(set(ALL_METRICS) - set(REGRESSION_METRICS)
1155-
- METRICS_WITHOUT_SAMPLE_WEIGHT - METRIC_UNDEFINED_BINARY))
1156+
'name',
1157+
sorted(
1158+
set(ALL_METRICS) - set(REGRESSION_METRICS) -
1159+
METRICS_WITHOUT_SAMPLE_WEIGHT - METRIC_UNDEFINED_BINARY))
11561160
def test_binary_sample_weight_invariance(name):
11571161
# binary
11581162
n_samples = 50
@@ -1168,10 +1172,10 @@ def test_binary_sample_weight_invariance(name):
11681172

11691173

11701174
@pytest.mark.parametrize(
1171-
'name',
1172-
(set(ALL_METRICS) - set(REGRESSION_METRICS)
1173-
- METRICS_WITHOUT_SAMPLE_WEIGHT
1174-
- METRIC_UNDEFINED_BINARY_MULTICLASS))
1175+
'name',
1176+
sorted(
1177+
set(ALL_METRICS) - set(REGRESSION_METRICS) -
1178+
METRICS_WITHOUT_SAMPLE_WEIGHT - METRIC_UNDEFINED_BINARY_MULTICLASS))
11751179
def test_multiclass_sample_weight_invariance(name):
11761180
# multiclass
11771181
n_samples = 50
@@ -1187,9 +1191,9 @@ def test_multiclass_sample_weight_invariance(name):
11871191

11881192

11891193
@pytest.mark.parametrize(
1190-
'name',
1191-
(MULTILABELS_METRICS | THRESHOLDED_MULTILABEL_METRICS |
1192-
MULTIOUTPUT_METRICS) - METRICS_WITHOUT_SAMPLE_WEIGHT)
1194+
'name',
1195+
sorted((MULTILABELS_METRICS | THRESHOLDED_MULTILABEL_METRICS
1196+
| MULTIOUTPUT_METRICS) - METRICS_WITHOUT_SAMPLE_WEIGHT))
11931197
def test_multilabel_sample_weight_invariance(name):
11941198
# multilabel indicator
11951199
random_state = check_random_state(0)
@@ -1235,7 +1239,8 @@ def test_no_averaging_labels():
12351239

12361240

12371241
@pytest.mark.parametrize(
1238-
'name', MULTILABELS_METRICS - {"unnormalized_multilabel_confusion_matrix"})
1242+
'name',
1243+
sorted(MULTILABELS_METRICS - {"unnormalized_multilabel_confusion_matrix"}))
12391244
def test_multilabel_label_permutations_invariance(name):
12401245
random_state = check_random_state(0)
12411246
n_samples, n_classes = 20, 4
@@ -1255,7 +1260,7 @@ def test_multilabel_label_permutations_invariance(name):
12551260

12561261

12571262
@pytest.mark.parametrize(
1258-
'name', THRESHOLDED_MULTILABEL_METRICS | MULTIOUTPUT_METRICS)
1263+
'name', sorted(THRESHOLDED_MULTILABEL_METRICS | MULTIOUTPUT_METRICS))
12591264
def test_thresholded_multilabel_multioutput_permutations_invariance(name):
12601265
random_state = check_random_state(0)
12611266
n_samples, n_classes = 20, 4

sklearn/tests/test_random_projection.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
all_sparse_random_matrix = [sparse_random_matrix]
2828
all_dense_random_matrix = [gaussian_random_matrix]
29-
all_random_matrix = set(all_sparse_random_matrix + all_dense_random_matrix)
29+
all_random_matrix = all_sparse_random_matrix + all_dense_random_matrix
3030

3131
all_SparseRandomProjection = [SparseRandomProjection]
3232
all_DenseRandomProjection = [GaussianRandomProjection]

sklearn/tree/tests/test_tree.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1375,7 +1375,7 @@ def test_sparse_input(tree_type, dataset):
13751375

13761376

13771377
@pytest.mark.parametrize("tree_type",
1378-
set(SPARSE_TREES).intersection(REG_TREES))
1378+
sorted(set(SPARSE_TREES).intersection(REG_TREES)))
13791379
@pytest.mark.parametrize("dataset", ["boston", "reg_small"])
13801380
def test_sparse_input_reg_trees(tree_type, dataset):
13811381
# Due to numerical instability of MSE and too strict test, we limit the

0 commit comments

Comments
 (0)