Merge branch '2018.3' into 48204_mysql_user_password_exception_part_deux

This commit is contained in:
Mike Place 2018-10-09 10:01:45 -06:00 committed by GitHub
commit 92c06800e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 152 additions and 43 deletions

View file

@ -48,6 +48,9 @@ pipeline {
description: 'The docs job has failed',
status: 'FAILURE',
context: "jenkins/pr/docs"
slackSend channel: "#jenkins-prod-pr",
color: '#FF0000',
message: "FAILED: PR-Job: '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
}
}
}

View file

@ -72,6 +72,9 @@ timeout(time: 6, unit: 'HOURS') {
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has failed",
status: 'FAILURE',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
slackSend channel: "#jenkins-prod-pr",
color: '#FF0000',
message: "FAILED: PR-Job: '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
}
}
}

View file

@ -72,6 +72,9 @@ timeout(time: 6, unit: 'HOURS') {
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has failed",
status: 'FAILURE',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
slackSend channel: "#jenkins-prod-pr",
color: '#FF0000',
message: "FAILED: PR-Job: '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
}
}
}

View file

@ -72,6 +72,9 @@ timeout(time: 6, unit: 'HOURS') {
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has failed",
status: 'FAILURE',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
slackSend channel: "#jenkins-prod-pr",
color: '#FF0000',
message: "FAILED: PR-Job: '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
}
}
}

View file

@ -72,6 +72,9 @@ timeout(time: 6, unit: 'HOURS') {
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has failed",
status: 'FAILURE',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
slackSend channel: "#jenkins-prod-pr",
color: '#FF0000',
message: "FAILED: PR-Job: '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
}
}
}

View file

@ -72,6 +72,9 @@ timeout(time: 6, unit: 'HOURS') {
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has failed",
status: 'FAILURE',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
slackSend channel: "#jenkins-prod-pr",
color: '#FF0000',
message: "FAILED: PR-Job: '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
}
}
}

View file

@ -72,6 +72,9 @@ timeout(time: 6, unit: 'HOURS') {
description: "The ${TEST_SUITE}-${TEST_PLATFORM} job has failed",
status: 'FAILURE',
context: "jenkins/pr/${TEST_SUITE}-${TEST_PLATFORM}"
slackSend channel: "#jenkins-prod-pr",
color: '#FF0000',
message: "FAILED: PR-Job: '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
}
}
}

View file

@ -94,6 +94,9 @@ pipeline {
description: 'The lint job has failed',
status: 'FAILURE',
context: "jenkins/pr/lint"
slackSend channel: "#jenkins-prod-pr",
color: '#FF0000',
message: "FAILED: PR-Job: '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})"
}
}
}

View file

@ -267,14 +267,12 @@ Write-Output " - $script_name :: Copying DLLs . . ."
Write-Output " ----------------------------------------------------------------"
# Architecture Specific DLL's
ForEach($key in $ini[$bitDLLs].Keys) {
If ($arrInstalled -notcontains $key) {
Write-Output " - $key . . ."
$file = "$($ini[$bitDLLs][$key])"
$url = "$($ini['Settings']['SaltRepo'])/$bitFolder/$file"
$file = "$($ini['Settings']['DownloadDir'])\$bitFolder\$file"
DownloadFileWithProgress $url $file
Copy-Item $file -destination $($ini['Settings']['Python2Dir'])
}
Write-Output " - $key . . ."
$file = "$($ini[$bitDLLs][$key])"
$url = "$($ini['Settings']['SaltRepo'])/$bitFolder/$file"
$file = "$($ini['Settings']['DownloadDir'])\$bitFolder\$file"
DownloadFileWithProgress $url $file
Copy-Item $file -destination $($ini['Settings']['Python2Dir'])
}
#------------------------------------------------------------------------------

View file

@ -276,14 +276,12 @@ Write-Output " - $script_name :: Copying DLLs . . ."
Write-Output " ----------------------------------------------------------------"
# Architecture Specific DLL's
ForEach($key in $ini[$bitDLLs].Keys) {
If ($arrInstalled -notcontains $key) {
Write-Output " - $key . . ."
$file = "$($ini[$bitDLLs][$key])"
$url = "$($ini['Settings']['SaltRepo'])/$bitFolder/$file"
$file = "$($ini['Settings']['DownloadDir'])\$bitFolder\$file"
DownloadFileWithProgress $url $file
Copy-Item $file -destination $($ini['Settings']['Python3Dir'])
}
Write-Output " - $key . . ."
$file = "$($ini[$bitDLLs][$key])"
$url = "$($ini['Settings']['SaltRepo'])/$bitFolder/$file"
$file = "$($ini['Settings']['DownloadDir'])\$bitFolder\$file"
DownloadFileWithProgress $url $file
Copy-Item $file -destination $($ini['Settings']['Python3Dir'])
}
#------------------------------------------------------------------------------

View file

@ -46,6 +46,7 @@ import salt.utils.files
import salt.utils.network
import salt.utils.path
import salt.utils.platform
import salt.utils.pkg.rpm
from salt.ext import six
from salt.ext.six.moves import range
@ -1896,8 +1897,8 @@ def os_data():
# architecture.
if grains.get('os_family') == 'Debian':
osarch = __salt__['cmd.run']('dpkg --print-architecture').strip()
elif grains.get('os_family') == 'RedHat':
osarch = __salt__['cmd.run']('rpm --eval %{_host_cpu}').strip()
elif grains.get('os_family') in ['RedHat', 'Suse']:
osarch = salt.utils.pkg.rpm.get_osarch()
elif grains.get('os_family') in ('NILinuxRT', 'Poky'):
archinfo = {}
for line in __salt__['cmd.run']('opkg print-architecture').splitlines():

View file

@ -321,7 +321,9 @@ class Maintenance(salt.utils.process.SignalHandlingMultiprocessingProcess):
'''
Fire presence events if enabled
'''
if self.presence_events:
# On the first run it may need more time for the EventPublisher
# to come up and be ready. Set the timeout to account for this.
if self.presence_events and self.event.connect_pull(timeout=3):
present = self.ckminions.connected_ids()
new = present.difference(old_present)
lost = old_present.difference(present)
@ -331,9 +333,7 @@ class Maintenance(salt.utils.process.SignalHandlingMultiprocessingProcess):
'lost': list(lost)}
self.event.fire_event(data, tagify('change', 'presence'))
data = {'present': list(present)}
# On the first run it may need more time for the EventPublisher
# to come up and be ready. Set the timeout to account for this.
self.event.fire_event(data, tagify('present', 'presence'), timeout=3)
self.event.fire_event(data, tagify('present', 'presence'))
old_present.clear()
old_present.update(present)

View file

@ -23,7 +23,11 @@ requisite to a pkg.installed state for the package which provides pip
from __future__ import absolute_import, print_function, unicode_literals
import re
import logging
import pkg_resources
try:
import pkg_resources
HAS_PKG_RESOURCES = True
except ImportError:
HAS_PKG_RESOURCES = False
# Import salt libs
import salt.utils.versions
@ -71,6 +75,8 @@ def __virtual__():
'''
Only load if the pip module is available in __salt__
'''
if HAS_PKG_RESOURCES is False:
return False, 'The pkg_resources python library is not installed'
if 'pip.list' in __salt__:
return __virtualname__
return False

View file

@ -9,6 +9,7 @@ import collections
import datetime
import logging
import subprocess
import salt.utils.stringutils
# Import 3rd-party libs
from salt.ext import six
@ -47,7 +48,7 @@ def get_osarch():
close_fds=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE).communicate()[0]
return ret or 'unknown'
return salt.utils.stringutils.to_str(ret).strip() or 'unknown'
def check_32(arch, osarch=None):

View file

@ -13,6 +13,7 @@ import textwrap
from tests.support.case import ModuleCase
from tests.support.helpers import flaky
from tests.support.paths import TMP_PILLAR_TREE
from tests.support.unit import skipIf
# Import Salt Libs
import salt.utils.files
@ -169,6 +170,7 @@ class SaltUtilSyncModuleTest(ModuleCase):
self.assertEqual(ret, expected_return)
@skipIf(True, 'Pillar refresh test is flaky. Skipping for now.')
class SaltUtilSyncPillarTest(ModuleCase):
'''
Testcase for the saltutil sync pillar module

View file

@ -14,13 +14,16 @@ import os
import pipes
import shutil
import tempfile
import logging
# Import Salt Testing libs
from tests.support.case import ShellCase
from tests.support.paths import TMP
from tests.support.mixins import ShellCaseCommonTestsMixin
from tests.support.unit import skipIf
# Import salt libs
import salt.utils.platform
import salt.utils.files
import salt.utils.yaml
@ -28,6 +31,9 @@ import salt.utils.yaml
from salt.ext import six
log = logging.getLogger(__name__)
class CopyTest(ShellCase, ShellCaseCommonTestsMixin):
_call_binary_ = 'salt-cp'
@ -54,19 +60,24 @@ class CopyTest(ShellCase, ShellCaseCommonTestsMixin):
with salt.utils.files.fopen(testfile, 'r') as fh_:
testfile_contents = fh_.read()
def quote(arg):
if salt.utils.platform.is_windows():
return arg
return pipes.quote(arg)
for idx, minion in enumerate(minions):
if 'localhost' in minion:
continue
ret = self.run_salt(
'--out yaml {0} file.directory_exists {1}'.format(
pipes.quote(minion), TMP
quote(minion), TMP
)
)
data = salt.utils.yaml.safe_load('\n'.join(ret))
if data[minion] is False:
ret = self.run_salt(
'--out yaml {0} file.makedirs {1}'.format(
pipes.quote(minion),
quote(minion),
TMP
)
)
@ -79,19 +90,23 @@ class CopyTest(ShellCase, ShellCaseCommonTestsMixin):
)
ret = self.run_cp('--out pprint {0} {1} {2}'.format(
pipes.quote(minion),
pipes.quote(testfile),
pipes.quote(minion_testfile)
quote(minion),
quote(testfile),
quote(minion_testfile),
))
data = salt.utils.yaml.safe_load('\n'.join(ret))
for part in six.itervalues(data):
self.assertTrue(part[minion_testfile])
if salt.utils.platform.is_windows():
key = minion_testfile.replace('\\', '\\\\')
else:
key = minion_testfile
self.assertTrue(part[key])
ret = self.run_salt(
'--out yaml {0} file.file_exists {1}'.format(
pipes.quote(minion),
pipes.quote(minion_testfile)
quote(minion),
quote(minion_testfile)
)
)
data = salt.utils.yaml.safe_load('\n'.join(ret))
@ -99,22 +114,23 @@ class CopyTest(ShellCase, ShellCaseCommonTestsMixin):
ret = self.run_salt(
'--out yaml {0} file.contains {1} {2}'.format(
pipes.quote(minion),
pipes.quote(minion_testfile),
pipes.quote(testfile_contents)
quote(minion),
quote(minion_testfile),
quote(testfile_contents)
)
)
data = salt.utils.yaml.safe_load('\n'.join(ret))
self.assertTrue(data[minion])
ret = self.run_salt(
'--out yaml {0} file.remove {1}'.format(
pipes.quote(minion),
pipes.quote(minion_testfile)
quote(minion),
quote(minion_testfile)
)
)
data = salt.utils.yaml.safe_load('\n'.join(ret))
self.assertTrue(data[minion])
@skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS')
def test_issue_7754(self):
config_dir = os.path.join(TMP, 'issue-7754')

View file

@ -8,8 +8,10 @@ import textwrap
# Import Salt Testing libs
from tests.support.case import ModuleCase
from tests.support.paths import FILES
from tests.support.unit import skipIf
# Import Salt Libs
import salt.utils.platform
import salt.utils.files
@ -25,6 +27,7 @@ class EnabledTest(ModuleCase):
"export SALTY_VARIABLE='saltines' && echo $SALTY_VARIABLE ; "
"echo duh &> /dev/null")
@skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS')
def test_shell_default_enabled(self):
'''
ensure that python_shell defaults to True for cmd.run
@ -33,6 +36,7 @@ class EnabledTest(ModuleCase):
ret = self.run_function('cmd.run', [self.cmd])
self.assertEqual(ret.strip(), enabled_ret)
@skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS')
def test_shell_disabled(self):
'''
test shell disabled output for cmd.run
@ -42,6 +46,7 @@ class EnabledTest(ModuleCase):
ret = self.run_function('cmd.run', [self.cmd], python_shell=False)
self.assertEqual(ret, disabled_ret)
@skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS')
def test_template_shell(self):
'''
Test cmd.shell works correctly when using a template.
@ -72,6 +77,7 @@ class EnabledTest(ModuleCase):
finally:
os.remove(state_file)
@skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS')
def test_template_default_disabled(self):
'''
test shell disabled output for templates (python_shell=False is the default

View file

@ -256,6 +256,9 @@ class KeyTest(ShellCase, ShellCaseCommonTestsMixin):
for fname in key_names:
self.assertTrue(os.path.isfile(os.path.join(tempdir, fname)))
finally:
for dirname, dirs, files in os.walk(tempdir):
for filename in files:
os.chmod(os.path.join(dirname, filename), 0o700)
shutil.rmtree(tempdir)
def test_keys_generation_keysize_minmax(self):

View file

@ -63,7 +63,7 @@ class MatchTest(ShellCase, ShellCaseCommonTestsMixin):
def test_compound_pcre_grain_and_grain(self):
match = 'P@test_grain:^cheese$ and * and G@test_grain:cheese'
data = self.run_salt('-t 1 -C \'{0}\' test.ping'.format(match))
data = self.run_salt('-t 1 -C "{0}" test.ping'.format(match))
assert minion_in_returns('minion', data) is True
assert minion_in_returns('sub_minion', data) is False
@ -74,22 +74,22 @@ class MatchTest(ShellCase, ShellCaseCommonTestsMixin):
assert minion_in_returns('minion', data) is False
def test_compound_not_sub_minion(self):
data = self.run_salt("-C 'not sub_minion' test.ping")
data = self.run_salt('-C "not sub_minion" test.ping')
assert minion_in_returns('minion', data) is True
assert minion_in_returns('sub_minion', data) is False
def test_compound_all_and_not_grains(self):
data = self.run_salt("-C '* and ( not G@test_grain:cheese )' test.ping")
data = self.run_salt('-C "* and ( not G@test_grain:cheese )" test.ping')
assert minion_in_returns('minion', data) is False
assert minion_in_returns('sub_minion', data) is True
def test_compound_grain_regex(self):
data = self.run_salt("-C 'G%@planets%merc*' test.ping")
data = self.run_salt('-C "G%@planets%merc*" test.ping')
assert minion_in_returns('minion', data) is True
assert minion_in_returns('sub_minion', data) is False
def test_coumpound_pcre_grain_regex(self):
data = self.run_salt("-C 'P%@planets%^(mercury|saturn)$' test.ping")
data = self.run_salt('-C "P%@planets%^(mercury|saturn)$" test.ping')
assert minion_in_returns('minion', data) is True
assert minion_in_returns('sub_minion', data) is True
@ -300,7 +300,7 @@ class MatchTest(ShellCase, ShellCaseCommonTestsMixin):
self.assertIn('minion', data.replace('sub_minion', 'stub'))
def test_ipcidr(self):
subnets_data = self.run_salt('--out yaml \'*\' network.subnets')
subnets_data = self.run_salt('--out yaml "*" network.subnets')
yaml_data = salt.utils.yaml.safe_load('\n'.join(subnets_data))
# We're just after the first defined subnet from 'minion'
@ -357,7 +357,11 @@ class MatchTest(ShellCase, ShellCaseCommonTestsMixin):
data = self.run_salt('-d minion salt ldap.search "filter=ou=People"', catch_stderr=True)
self.assertIn('You can only get documentation for one method at one time', '\n'.join(data[1]))
@skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS')
def test_issue_7754(self):
'''
Skip on Windows because Syslog is not installed
'''
old_cwd = os.getcwd()
config_dir = os.path.join(TMP, 'issue-7754')
if not os.path.isdir(config_dir):

View file

@ -31,6 +31,7 @@ from salt.ext import six
# Import salt libs
import salt.utils.files
import salt.utils.yaml
import salt.utils.platform
log = logging.getLogger(__name__)
@ -271,9 +272,12 @@ class MinionTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMix
for minion in minions:
minion.shutdown()
@skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS')
def test_exit_status_unknown_user(self):
'''
Ensure correct exit status when the minion is configured to run as an unknown user.
Skipped on windows because daemonization not supported
'''
minion = testprogram.TestDaemonSaltMinion(
@ -302,6 +306,7 @@ class MinionTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMix
minion.shutdown()
# pylint: disable=invalid-name
# @skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS')
def test_exit_status_unknown_argument(self):
'''
Ensure correct exit status when an unknown argument is passed to salt-minion.
@ -331,9 +336,12 @@ class MinionTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMix
# cause timeout exeptions and respective traceback
minion.shutdown()
@skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS')
def test_exit_status_correct_usage(self):
'''
Ensure correct exit status when salt-minion starts correctly.
Skipped on windows because daemonization not supported
'''
minion = testprogram.TestDaemonSaltMinion(

View file

@ -10,13 +10,18 @@
from __future__ import absolute_import, print_function, unicode_literals
import logging
from tests.support.unit import skipIf
# Import salt tests libs
import tests.integration.utils
from tests.integration.utils import testprogram
import salt.utils.platform
log = logging.getLogger(__name__)
@skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS')
class ProxyTest(testprogram.TestProgramCase):
'''
Various integration tests for the salt-proxy executable.
@ -25,6 +30,8 @@ class ProxyTest(testprogram.TestProgramCase):
def test_exit_status_no_proxyid(self):
'''
Ensure correct exit status when --proxyid argument is missing.
Skip on Windows because daemonization not supported
'''
proxy = testprogram.TestDaemonSaltProxy(
@ -61,6 +68,8 @@ class ProxyTest(testprogram.TestProgramCase):
def test_exit_status_unknown_user(self):
'''
Ensure correct exit status when the proxy is configured to run as an unknown user.
Skip on Windows because daemonization not supported
'''
proxy = testprogram.TestDaemonSaltProxy(
@ -92,6 +101,8 @@ class ProxyTest(testprogram.TestProgramCase):
def test_exit_status_unknown_argument(self):
'''
Ensure correct exit status when an unknown argument is passed to salt-proxy.
Skip on Windows because daemonization not supported
'''
proxy = testprogram.TestDaemonSaltProxy(
@ -120,6 +131,8 @@ class ProxyTest(testprogram.TestProgramCase):
def test_exit_status_correct_usage(self):
'''
Ensure correct exit status when salt-proxy starts correctly.
Skip on Windows because daemonization not supported
'''
proxy = testprogram.TestDaemonSaltProxy(

View file

@ -15,12 +15,14 @@ from tests.support.case import ShellCase
from tests.support.paths import TMP
from tests.support.mixins import ShellCaseCommonTestsMixin
from tests.support.helpers import skip_if_not_root
from tests.support.unit import skipIf
# Import Salt libs
import salt.utils.files
import salt.utils.platform
import salt.utils.yaml
USERA = 'saltdev'
USERA_PWD = 'saltdev'
HASHED_USERA_PWD = '$6$SALTsalt$ZZFD90fKFWq8AGmmX0L3uBtS9fXL62SrTk5zcnQ6EkD6zoiM3kB88G1Zvs0xm/gZ7WXJRs5nsTBybUvGSqZkT.'
@ -90,7 +92,11 @@ class RunTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMixin)
data = self.run_run('-d virt.list foo', catch_stderr=True)
self.assertIn('You can only get documentation for one method at one time', '\n'.join(data[1]))
@skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS')
def test_issue_7754(self):
'''
Skip on windows because syslog not available
'''
old_cwd = os.getcwd()
config_dir = os.path.join(TMP, 'issue-7754')
if not os.path.isdir(config_dir):

View file

@ -18,11 +18,13 @@ import logging
from tests.support.case import ShellCase
from tests.support.paths import TMP
from tests.support.mixins import ShellCaseCommonTestsMixin
from tests.support.unit import skipIf
from tests.integration.utils import testprogram
# Import salt libs
import salt.utils.files
import salt.utils.yaml
import salt.utils.platform
log = logging.getLogger(__name__)
@ -80,9 +82,12 @@ class SyndicTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMix
if os.path.isdir(config_dir):
shutil.rmtree(config_dir)
@skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS')
def test_exit_status_unknown_user(self):
'''
Ensure correct exit status when the syndic is configured to run as an unknown user.
Skipped on windows because daemonization not supported
'''
syndic = testprogram.TestDaemonSaltSyndic(
@ -110,9 +115,12 @@ class SyndicTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMix
syndic.shutdown()
# pylint: disable=invalid-name
@skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS')
def test_exit_status_unknown_argument(self):
'''
Ensure correct exit status when an unknown argument is passed to salt-syndic.
Skipped on windows because daemonization not supported
'''
syndic = testprogram.TestDaemonSaltSyndic(
@ -138,9 +146,12 @@ class SyndicTest(ShellCase, testprogram.TestProgramCase, ShellCaseCommonTestsMix
# cause timeout exeptions and respective traceback
syndic.shutdown()
@skipIf(salt.utils.platform.is_windows(), 'Skip on Windows OS')
def test_exit_status_correct_usage(self):
'''
Ensure correct exit status when salt-syndic starts correctly.
Skipped on windows because daemonization not supported
'''
syndic = testprogram.TestDaemonSaltSyndic(

View file

@ -77,6 +77,18 @@ integration.shell.test_arguments
integration.shell.test_auth
integration.shell.test_call
integration.shell.test_cloud
integration.shell.test_cp
integration.shell.test_enabled
integration.shell.test_key
integration.shell.test_master
integration.shell.test_master_tops
integration.shell.test_matcher
integration.shell.test_minion
integration.shell.test_proxy
integration.shell.test_runner
integration.shell.test_saltcli
integration.shell.test_spm
integration.shell.test_syndic
integration.spm.test_build
integration.spm.test_files
integration.spm.test_info