blob: 05223c3f43966aa311ed781a51b5dad114eb80fb [file] [log] [blame]
#!/usr/bin/env python3
# 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.
"""Tests for gen_requirements, found in python/."""
import collections
import contextlib
import os
import sys
import tvm
import tvm.testing
import pytest
# Insert the parent dir to python/tvm into the import path, so that gen_requirements may be
# imported.
sys.path.insert(0, os.path.dirname(tvm.__file__))
try:
import gen_requirements
finally:
sys.path.pop(0)
@contextlib.contextmanager
def patch(obj, **kw):
old = {}
for prop_name, new in kw.items():
old[prop_name] = getattr(obj, prop_name)
setattr(obj, prop_name, new)
yield
for prop_name, value in old.items():
setattr(obj, prop_name, value)
PROBLEM_REQUIREMENTS = [
("extras-pre-core", ("", ["foo", 123])), # entry before core
(456, ("", ["foo", "bar"])), # invalid extras name, deps should not be processed
("core", ("", ["foo"])), # ordinary core entry.
("wrong-description-type", (None, ["foo"])), # wrong description type
("bad-value", None), # value field is not a 2-tuple
("bad-value-2", ("", ["foo"], 34)), # value field is not a 2-tuple
("invalid", ("", ["qux"])), # duplicate invalid entry, all items valid.
("extras-foo", ("", ["bar", "baz"])), # ordinary extras entry.
("invalid", ("", ["baz", None, 123])), # valid extra name, invalid deps.
("unsorted", ("", ["qux", "bar", "foo"])), # deps out of order
("versioned_dep", ("", ["baz==1.2", "foo==^2.0", "buz<3", "bar>4"])),
("duplicate_dep", ("", ["buz", "buz", "foo"])), # duplicate listed dependency
("dev", ("", ["baz", "qux"])), # ordinary dev entry.
("extras-post-dev", ("", ["bar", "buzz"])), # entry after dev
]
def test_validate_requirements():
with patch(gen_requirements, REQUIREMENTS_BY_PIECE=None):
assert gen_requirements.validate_requirements_by_piece() == [
"must be list or tuple, see None"
]
with patch(gen_requirements, REQUIREMENTS_BY_PIECE=PROBLEM_REQUIREMENTS):
problems = gen_requirements.validate_requirements_by_piece()
assert problems == [
'piece extras-pre-core: must list after "core" (core must be first)',
"piece extras-pre-core: deps should be a list of strings, got ['foo', 123]",
"piece 456: must be str",
"piece wrong-description-type: description should be a string, got None",
(
'piece bad-value: should be formatted like ("bad-value", ("<requirements.txt '
'comment>", ["dep1", "dep2", ...])). got: None'
),
(
'piece bad-value-2: should be formatted like ("bad-value-2", '
'("<requirements.txt comment>", ["dep1", "dep2", ...])). got: (\'\', '
"['foo'], 34)"
),
"piece invalid: listed twice",
"piece invalid: deps should be a list of strings, got ['baz', None, 123]",
"piece unsorted: deps must be sorted. Correct order:\n ['bar', 'foo', 'qux']",
"piece versioned_dep: deps must be sorted. Correct order:\n ['bar>4', 'baz==1.2', 'buz<3', 'foo==^2.0']",
"piece versioned_dep: dependency baz==1.2 should not specify a version. Add it to CONSTRAINTS instead.",
"piece versioned_dep: dependency foo==^2.0 should not specify a version. Add it to CONSTRAINTS instead.",
"piece versioned_dep: dependency buz<3 should not specify a version. Add it to CONSTRAINTS instead.",
"piece versioned_dep: dependency bar>4 should not specify a version. Add it to CONSTRAINTS instead.",
"piece duplicate_dep: dependency buz listed twice",
'piece extras-post-dev: must list before "dev" (dev must be last)',
'pieces other than "core" and "dev" must appear in alphabetical order: '
"['bad-value', 'bad-value-2', 'duplicate_dep', 'extras-foo', 'extras-post-dev', "
"'extras-pre-core', 'invalid', 'invalid', 'unsorted', 'versioned_dep', "
"'wrong-description-type']",
]
TEST_REQUIREMENTS_BY_PIECE = (
("core", ("core tvm requirements", ("bar", "foo", "non-constrained"))),
("extra-one", ("requirements for one feature", ("baz", "qux"))),
("extra-two", ("requirements for two feature", ("buz", "qux", "semver-minor", "semver-patch"))),
("dev", ("requirements for dev", ("buz", "oof", "rab"))),
)
def test_validate_constraints():
with patch(
gen_requirements,
REQUIREMENTS_BY_PIECE=TEST_REQUIREMENTS_BY_PIECE,
CONSTRAINTS=(
("unlisted", "~=3"),
("double-specified", "<2"),
(
"double-specified",
"==3",
),
("bad-constraint", "1.2.0"),
("bad-semver-constraint", "i don't match the regex :P"),
("alpha-semver-constraint", "^foo.bar.23"),
),
):
problems = gen_requirements.validate_constraints()
assert problems == [
"unlisted: not specified in REQUIREMENTS_BY_PIECE",
"double-specified: not specified in REQUIREMENTS_BY_PIECE",
"double-specified: specified twice",
"double-specified: not specified in REQUIREMENTS_BY_PIECE",
"bad-constraint: not specified in REQUIREMENTS_BY_PIECE",
'bad-constraint: constraint "1.2.0" does not look like a valid constraint',
"bad-semver-constraint: not specified in REQUIREMENTS_BY_PIECE",
'bad-semver-constraint: constraint "i don\'t match the regex :P" does not look like a valid constraint',
"alpha-semver-constraint: not specified in REQUIREMENTS_BY_PIECE",
"alpha-semver-constraint: invalid semver constraint ^foo.bar.23",
"CONSTRAINTS entries should be in this sorted order: ['alpha-semver-constraint', 'bad-constraint', 'bad-semver-constraint', 'double-specified', 'double-specified', 'unlisted']",
]
TEST_CONSTRAINTS = (
("bar", "==1.0"),
("baz", ">2.3"),
("buz", "^1.3.0"),
("non-constrained", None), # Support a comment.
("oof", "==0.3.4"),
("qux", "~=1.2.4"),
("semver-minor", "^0.2.2-patch2.post3+buildmeta"), # Ensure prerelease and buildmeta preserved.
("semver-patch", "^0.0.2+bm"), # Ensure postrelease preserved.
)
def test_join_requirements():
with patch(
gen_requirements,
REQUIREMENTS_BY_PIECE=TEST_REQUIREMENTS_BY_PIECE,
CONSTRAINTS=TEST_CONSTRAINTS,
):
requirements = gen_requirements.join_requirements()
assert requirements == collections.OrderedDict(
[
("core", ("core tvm requirements", ["bar==1.0", "foo", "non-constrained"])),
("extra-one", ("requirements for one feature", ["baz>2.3", "qux~=1.2.4"])),
(
"extra-two",
(
"requirements for two feature",
[
"buz>=1.3.0,<2.0.0",
"qux~=1.2.4",
"semver-minor>=0.2.2-patch2.post3+buildmeta,<0.3.0",
"semver-patch>=0.0.2+bm,<0.0.3",
],
),
),
("dev", ("requirements for dev", ["buz>=1.3.0,<2.0.0", "oof==0.3.4", "rab"])),
(
"all-prod",
(
"Combined dependencies for all TVM pieces, excluding dev",
[
"bar==1.0",
"baz>2.3",
"buz>=1.3.0,<2.0.0",
"foo",
"non-constrained",
"qux~=1.2.4",
"semver-minor>=0.2.2-patch2.post3+buildmeta,<0.3.0",
"semver-patch>=0.0.2+bm,<0.0.3",
],
),
),
]
)
def test_semver():
problems = []
assert gen_requirements.parse_semver("C", "^1.2.0", problems) == (["1", "2", "0"], 0, 1)
assert problems == []
assert gen_requirements.parse_semver("C", "^0.2.0", problems) == (["0", "2", "0"], 1, 2)
assert problems == []
assert gen_requirements.parse_semver("C", "^0.0.0", problems) == (["0", "0", "0"], 0, 0)
assert problems == []
assert gen_requirements.parse_semver("C", "^0.a.0", problems) == ([], 0, 0)
assert problems == ["C: invalid semver constraint ^0.a.0"]
if __name__ == "__main__":
tvm.testing.main()