Skip to content
This repository has been archived by the owner on Nov 17, 2023. It is now read-only.

Change LC_NUMERIC for CentOS CI jobs to verify locale invariance #18097

Merged
merged 11 commits into from
May 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions ci/docker/Dockerfile.build.centos7
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,18 @@ RUN pip3 install --no-cache-dir --upgrade pip && \
protobuf==3.5.2 \
tabulate==0.7.5

# Fix the en_DK.UTF-8 locale to test locale invariance
RUN localedef -i en_DK -f UTF-8 en_DK.UTF-8

ARG USER_ID=0
COPY install/docker_filepermissions.sh /work/
RUN /work/docker_filepermissions.sh

ENV PYTHONPATH=./python/
# Verify that MXNet works correctly when the C locale is set to a locale that uses a comma as the
# decimal separator. Please see #16134 for an example of a bug caused by incorrect handling of
# number serialization and deserialization.
ENV LC_NUMERIC=en_DK.UTF-8
WORKDIR /work/mxnet

COPY runtime_functions.sh /work/
Expand Down
4 changes: 2 additions & 2 deletions ci/docker/runtime_functions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1678,7 +1678,7 @@ build_scala_docs() {

pushd $scala_path

scala_doc_sources=`find . -type f -name "*.scala" | egrep "./core|./infer" | egrep -v "/javaapi" | egrep -v "Suite" | egrep -v "/mxnetexamples"`
scala_doc_sources=`find . -type f -name "*.scala" | egrep "./core|./infer" | egrep -v "/javaapi" | egrep -v "Suite" | egrep -v "CancelTestUtil" | egrep -v "/mxnetexamples"`
jar_native=`find native -name "*.jar" | grep "target/lib/" | tr "\\n" ":" `
jar_macros=`find macros -name "*.jar" | tr "\\n" ":" `
jar_core=`find core -name "*.jar" | tr "\\n" ":" `
Expand Down Expand Up @@ -1753,7 +1753,7 @@ build_java_docs() {

pushd $java_path

java_doc_sources=`find . -type f -name "*.scala" | egrep "./core|./infer" | egrep "/javaapi" | egrep -v "Suite" | egrep -v "/mxnetexamples"`
java_doc_sources=`find . -type f -name "*.scala" | egrep "./core|./infer" | egrep "/javaapi" | egrep -v "Suite" | egrep -v "CancelTestUtil" | egrep -v "/mxnetexamples"`
jar_native=`find native -name "*.jar" | grep "target/lib/" | tr "\\n" ":" `
jar_macros=`find macros -name "*.jar" | tr "\\n" ":" `
jar_core=`find core -name "*.jar" | tr "\\n" ":" `
Expand Down
5 changes: 5 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ def pytest_configure():
logging.warning('*** test-level seed set: all "@with_seed()" '
'tests run deterministically ***')

# Load the user's locale settings to verify that MXNet works correctly when the C locale is set
# to anything other than the default value. Please see #16134 for an example of a bug caused by
# incorrect handling of C locales.
import locale
locale.setlocale(locale.LC_ALL, "")

@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.mxnet

import java.text.DecimalFormatSymbols
import java.util.Locale

import org.scalatest.Assertions

object CancelTestUtil {
/**
* Cancel the test if the system's locale uses a decimal separator other than '.'. Please see
* #18097 for more information.
*/
def assumeStandardDecimalSeparator(): Unit = {
val lcNumeric = System.getenv("LC_NUMERIC");

val decimalFormatSymbols = if (lcNumeric != null) {
val localeName = lcNumeric.stripSuffix(".UTF-8".stripSuffix(".utf-8"))
val locale = Locale.forLanguageTag(localeName)
DecimalFormatSymbols.getInstance(locale)
} else {
DecimalFormatSymbols.getInstance()
}

val isStandardDecimalPoint = (decimalFormatSymbols.getDecimalSeparator == '.') &&
(lcNumeric != null && lcNumeric.toLowerCase != "en_dk.utf-8") // Java doesn't seem to respect
// the decimal separator
// set in en_DK.UTF8, which is
// used in CentOS CI jobs.
if (!isStandardDecimalPoint) {
Assertions.cancel("Some operators " +
"break when the decimal separator is set to anything other than \".\". These operators " +
"should be rewritten to utilize the new FFI. Please see #18097 for more information.")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ class ModuleSuite extends FunSuite with BeforeAndAfterAll {
}

test ("module reshape") {
CancelTestUtil.assumeStandardDecimalSeparator()

var sym = Symbol.Variable("data")
sym = Symbol.FullyConnected("fc")()(Map("data" -> sym, "num_hidden" -> 20))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ class OperatorSuite extends FunSuite with BeforeAndAfterAll
}

test("scalar op") {
CancelTestUtil.assumeStandardDecimalSeparator()

val data = Symbol.Variable("data")
val shape = Shape(3, 4)
val dataTmp = NDArray.ones(shape) * 5
Expand Down Expand Up @@ -256,6 +258,8 @@ class OperatorSuite extends FunSuite with BeforeAndAfterAll
}

test("symbol pow") {
CancelTestUtil.assumeStandardDecimalSeparator()

val shape = Shape(1, 1)

val data = Symbol.Variable("data")
Expand All @@ -277,6 +281,8 @@ class OperatorSuite extends FunSuite with BeforeAndAfterAll
}

test("pow fn") {
CancelTestUtil.assumeStandardDecimalSeparator()

val shape = Shape(3, 4)
val exp = Symbol.Variable("exp")
import SymbolConversions._
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class ConvSuite extends FunSuite with BeforeAndAfterAll {
private var tu = new TestUtil

test("train mnist") {
CancelTestUtil.assumeStandardDecimalSeparator()
// symbol net
val batchSize = 100

Expand Down
8 changes: 8 additions & 0 deletions tests/python/unittest/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@
from contextlib import contextmanager
import pytest
from tempfile import TemporaryDirectory
import locale

xfail_when_nonstandard_decimal_separator = pytest.mark.xfail(
locale.localeconv()["decimal_point"] != ".",
reason="Some operators break when the decimal separator is set to anything other than \".\". "
"These operators should be rewritten to utilize the new FFI. Please see #18097 for more "
"information."
)

def assertRaises(expected_exception, func, *args, **kwargs):
try:
Expand Down
3 changes: 2 additions & 1 deletion tests/python/unittest/test_autograd.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from mxnet.ndarray import zeros_like
from mxnet.autograd import *
from mxnet.test_utils import *
from common import setup_module, with_seed, teardown_module
from common import setup_module, with_seed, teardown_module, xfail_when_nonstandard_decimal_separator
from mxnet.test_utils import EnvManager


Expand Down Expand Up @@ -107,6 +107,7 @@ def autograd_assert(*args, **kwargs):
for a, b in zip(grad_vals, grad_res):
assert same(a.asnumpy(), b.asnumpy())

@xfail_when_nonstandard_decimal_separator
@with_seed()
def test_unary_func():
def check_unary_func(x):
Expand Down
3 changes: 2 additions & 1 deletion tests/python/unittest/test_contrib_autograd.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import mxnet.ndarray as nd
from mxnet.contrib.autograd import *
from mxnet.test_utils import *
from common import setup_module, with_seed, teardown_module
from common import setup_module, with_seed, teardown_module, xfail_when_nonstandard_decimal_separator

def autograd_assert(*args, **kwargs):
func = kwargs["func"]
Expand All @@ -34,6 +34,7 @@ def autograd_assert(*args, **kwargs):
for a, b in zip(grad_vals, grad_res):
assert same(a.asnumpy(), b.asnumpy())

@xfail_when_nonstandard_decimal_separator
@with_seed()
def test_unary_func():
x = nd.uniform(shape=(4, 5))
Expand Down
4 changes: 3 additions & 1 deletion tests/python/unittest/test_contrib_operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
import itertools
from numpy.testing import assert_allclose, assert_array_equal
from mxnet.test_utils import *
from common import with_seed, assert_raises_cudnn_not_satisfied
from common import with_seed, assert_raises_cudnn_not_satisfied, \
xfail_when_nonstandard_decimal_separator
import unittest

def test_box_nms_op():
Expand Down Expand Up @@ -285,6 +286,7 @@ def test_multibox_target_op():
assert_array_equal(loc_mask.asnumpy(), expected_loc_mask)
assert_array_equal(cls_target.asnumpy(), expected_cls_target)

@xfail_when_nonstandard_decimal_separator
def test_gradient_multiplier_op():
# We use the quadratic function in combination with gradient multiplier
def f(x, a, b, c):
Expand Down
4 changes: 3 additions & 1 deletion tests/python/unittest/test_contrib_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@

curr_path = os.path.dirname(os.path.abspath(os.path.expanduser(__file__)))
sys.path.insert(0, os.path.join(curr_path, '../unittest'))
from common import with_seed
from common import with_seed, xfail_when_nonstandard_decimal_separator


@xfail_when_nonstandard_decimal_separator
def test_group_adagrad():
mx.random.seed(0)
opt1 = mx.optimizer.contrib.GroupAdaGrad
Expand Down Expand Up @@ -61,6 +62,7 @@ def test_group_adagrad():
g_stype='row_sparse')


@xfail_when_nonstandard_decimal_separator
@with_seed()
@pytest.mark.serial
def test_adamw():
Expand Down
4 changes: 3 additions & 1 deletion tests/python/unittest/test_contrib_stes_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
# specific language governing permissions and limitations
# under the License.

from common import with_seed
from common import with_seed, xfail_when_nonstandard_decimal_separator
import mxnet as mx
from mxnet import nd, autograd, gluon
from mxnet.test_utils import default_context
Expand Down Expand Up @@ -98,6 +98,7 @@ def check_ste(net_type_str, w_init, hybridize, in_data, ctx=None):
str(net.w.grad()) + " but expected " + \
str(net.expected_grads(in_data, w_init))

@xfail_when_nonstandard_decimal_separator
@with_seed()
def test_contrib_round_ste():
# Test with random data
Expand All @@ -119,6 +120,7 @@ def test_contrib_round_ste():
check_ste(net_type_str="RoundSTENET", w_init=w_init, hybridize=False, in_data=in_data)


@xfail_when_nonstandard_decimal_separator
@with_seed()
def test_contrib_sign_ste():
in_data = nd.uniform(-10, 10, shape=30) # 10 and 30 are arbitrary numbers
Expand Down
4 changes: 3 additions & 1 deletion tests/python/unittest/test_gluon.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from mxnet.test_utils import use_np
import mxnet.numpy as _mx_np
from common import (setup_module, with_seed, assertRaises, teardown_module,
assert_raises_cudnn_not_satisfied)
assert_raises_cudnn_not_satisfied, xfail_when_nonstandard_decimal_separator)
import numpy as np
from numpy.testing import assert_array_equal
import pytest
Expand Down Expand Up @@ -754,6 +754,7 @@ def test_batchnorm():
check_layer_forward(layer, (2, 10, 10, 10))


@xfail_when_nonstandard_decimal_separator
@with_seed()
def test_sync_batchnorm():
def _check_batchnorm_result(input, num_devices=1, cuda=False):
Expand Down Expand Up @@ -1377,6 +1378,7 @@ def test_inline():
assert len_1 == len_2 + 2


@xfail_when_nonstandard_decimal_separator
@with_seed()
def test_activations():
point_to_validate = mx.nd.array([-0.1, 0.1] * 3)
Expand Down
5 changes: 4 additions & 1 deletion tests/python/unittest/test_gluon_data_vision.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
from mxnet.gluon.data.vision import transforms
from mxnet import image
from mxnet.test_utils import *
from common import assertRaises, setup_module, with_seed, teardown_module
from common import assertRaises, setup_module, with_seed, teardown_module, \
xfail_when_nonstandard_decimal_separator

import numpy as np

Expand Down Expand Up @@ -318,6 +319,7 @@ def test_random_rotation():
assert_almost_equal(data, transformer(data))


@xfail_when_nonstandard_decimal_separator
@with_seed()
def test_rotate():
transformer = transforms.Rotate(10.)
Expand Down Expand Up @@ -389,6 +391,7 @@ def test_random_transforms():
num_apply += 1
assert_almost_equal(num_apply/float(iteration), 0.5, 0.1)

@xfail_when_nonstandard_decimal_separator
@with_seed()
def test_random_gray():
from mxnet.gluon.data.vision import transforms
Expand Down
4 changes: 3 additions & 1 deletion tests/python/unittest/test_gluon_trainer.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from mxnet import gluon
from mxnet.gluon import nn
from mxnet.test_utils import assert_almost_equal
from common import setup_module, with_seed, assertRaises
from common import setup_module, with_seed, assertRaises, xfail_when_nonstandard_decimal_separator
from copy import deepcopy
import pytest

Expand Down Expand Up @@ -217,6 +217,7 @@ def check_init(ctxes):
check_init([mx.cpu(1), mx.cpu(2)])
check_init([mx.cpu(1)])

@xfail_when_nonstandard_decimal_separator
@with_seed()
def test_trainer_reset_kv():
def check_trainer_reset_kv(kv):
Expand Down Expand Up @@ -250,6 +251,7 @@ def check_trainer_reset_kv(kv):
for kv in kvs:
check_trainer_reset_kv(kv)

@xfail_when_nonstandard_decimal_separator
@with_seed()
def test_trainer_sparse_kv():
def check_trainer_sparse_kv(kv, stype, grad_stype, update_on_kv, expected):
Expand Down
6 changes: 4 additions & 2 deletions tests/python/unittest/test_higher_order_grad.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from operator import mul
import random

from common import with_seed
from common import with_seed, xfail_when_nonstandard_decimal_separator
import mxnet
from mxnet import nd, autograd, gluon
from mxnet.test_utils import (
Expand Down Expand Up @@ -276,6 +276,7 @@ def grad_grad_op(x):
check_nth_order_unary(array, log, [grad_op, grad_grad_op], [1, 2])


@xfail_when_nonstandard_decimal_separator
@with_seed()
def test_log2():
def log2(x):
Expand All @@ -289,7 +290,7 @@ def grad_grad_op(x):
array = random_arrays(shape)
check_second_order_unary(array, log2, grad_grad_op)


@xfail_when_nonstandard_decimal_separator
@with_seed()
def test_log10():
def log10(x):
Expand Down Expand Up @@ -415,6 +416,7 @@ def grad_grad_op(x):
check_nth_order_unary(array, sigmoid, grad_grad_op, 2)


@xfail_when_nonstandard_decimal_separator
@with_seed()
def test_sqrt():
def sqrt(x):
Expand Down
3 changes: 2 additions & 1 deletion tests/python/unittest/test_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import numpy as np
import scipy.ndimage
from mxnet.test_utils import *
from common import assertRaises, with_seed
from common import assertRaises, with_seed, xfail_when_nonstandard_decimal_separator
import shutil
import tempfile
import unittest
Expand Down Expand Up @@ -366,6 +366,7 @@ def test_random_size_crop(self):
assert ratio[0] - epsilon <= float(new_w)/new_h <= ratio[1] + epsilon, \
'ration of new width and height out of the bound{}/{}={}'.format(new_w, new_h, float(new_w)/new_h)

@xfail_when_nonstandard_decimal_separator
@with_seed()
def test_imrotate(self):
# test correctness
Expand Down
Loading