<p>George Joseph has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/testsuite/+/16587">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">testsuite: Allow copying of arbitrary files to asterisk instances<br><br>If you have a key or certificate file that has to go in<br>/var/lib/asterisk/keys, there was no way for a test to get<br>that file into that directory. Various work-arounds ensued.<br><br>* Added a replaceable parameter "<<instanceid>>" to config<br> file processing which would be replaced with the Asterisk<br> instance's id. I.E. 'ast1' would be '1'.<br><br>* Added automatic installation of files in a test's "files"<br> directory to its Asterisk instance's well-known directories.<br> To use, create directories named after entries in the<br> asterisk.conf "directories" category and place your files there.<br> It works much like the test's "configs" directory.<br><br> For example:<br> mytest/<br> configs/<br> ast1/<br> someconfig.conf<br> ast2/<br> someconfig.conf<br> files/<br> common/<br> astvarlibdir/<br> keys/<br> ca.crt<br> ast1/<br> astvarlibdir/<br> keys/<br> mykey1.pem<br> ast2/<br> astvarlibdir/<br> keys/<br> mykey2.pem<br> test-config.yaml<br><br> Since 'astvarlibdir' is usually defined in asterisk.conf as<br> '/var/lib/asterisk', this would copy 'ca.crt' to both Asterisk<br> instance's '/var/lib/asterisk/keys' directory, 'mykey1.pem' to<br> the first Asterisk instance's '/var/lib/asterisk/keys' directory<br> and 'mykey2.pem' to the second instance's '/var/lib/asterisk/keys'<br> directory.<br><br> Each of the instance's "someconfig.conf" could reference those<br> files like so:<br><br> cacert = <<astvarlibdir>>/keys/ca.crt.pem<br> keyfile = <<astvarlibdir>>/keys/mykey<<instanceid>>.pem<br><br> If you have files that can be shared among tests, you can define<br> 'base-files-path' in each test's 'test-object-config' section which<br> would point to a directory structured like the 'files' directory<br> above.<br><br> For example<br> mytests/<br> myfiles/<br> common/<br> astvarlibdir/<br> keys/<br> ca.crt<br> ast1/<br> astvarlibdir/<br> keys/<br> mykey1.pem<br> ast2/<br> astvarlibdir/<br> keys/<br> mykey2.pem<br><br> mytest1/<br> ...<br> mytest2/<br> ...<br><br> Then in both test's test-config.yaml...<br> test-object-config:<br> base-files-path: 'tests/mytests/myfiles'<br><br> base-files-path is processed before the automatic files<br> processing of each test so individual tests can add its own files<br> or even override a common file if it chooses.<br><br>ASTERISK-29675<br><br>Change-Id: I69bb54fea459e83f65a474d3b74c40b28fe01b4c<br>---<br>M README.txt<br>M lib/python/asterisk/asterisk.py<br>M lib/python/asterisk/test_case.py<br>A lib/python/polyfill.py<br>M sample-yaml/test-config.yaml.sample<br>5 files changed, 221 insertions(+), 2 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/testsuite refs/changes/87/16587/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/README.txt b/README.txt</span><br><span>index 459fbed..93888be 100644</span><br><span>--- a/README.txt</span><br><span>+++ b/README.txt</span><br><span>@@ -335,6 +335,41 @@</span><br><span> Since each Asterisk instance required difference SIP settings, each 'ast%d'</span><br><span> folder will have a different sip.conf file.</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+You can also copy arbitrary files like sound, key and certificate files into</span><br><span style="color: hsl(120, 100%, 40%);">+any of the entries in the asterisk.conf "directories" category using its</span><br><span style="color: hsl(120, 100%, 40%);">+entry name name. All intermediate directories will be created if they</span><br><span style="color: hsl(120, 100%, 40%);">+don't already exist.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+For example, to have a certificate and keys copied to an Asterisk instance's</span><br><span style="color: hsl(120, 100%, 40%);">+'/var/lib/asterisk/keys' directory, you'd place it in...</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ basic-call/</span><br><span style="color: hsl(120, 100%, 40%);">+ files/</span><br><span style="color: hsl(120, 100%, 40%);">+ common/</span><br><span style="color: hsl(120, 100%, 40%);">+ astvarlibdir/</span><br><span style="color: hsl(120, 100%, 40%);">+ keys/</span><br><span style="color: hsl(120, 100%, 40%);">+ ca.crt</span><br><span style="color: hsl(120, 100%, 40%);">+ ast1/</span><br><span style="color: hsl(120, 100%, 40%);">+ astvarlibdir/</span><br><span style="color: hsl(120, 100%, 40%);">+ keys/</span><br><span style="color: hsl(120, 100%, 40%);">+ instance1-key.pem</span><br><span style="color: hsl(120, 100%, 40%);">+ ...</span><br><span style="color: hsl(120, 100%, 40%);">+ ast2/</span><br><span style="color: hsl(120, 100%, 40%);">+ astvarlibdir/</span><br><span style="color: hsl(120, 100%, 40%);">+ keys/</span><br><span style="color: hsl(120, 100%, 40%);">+ instance2-key.pem</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Since 'astvarlibdir' is defined in asterisk.conf as '/var/lib/asterisk',</span><br><span style="color: hsl(120, 100%, 40%);">+this would copy 'ca.crt' to both instance's '/var/lib/asterisk/keys/' directory,</span><br><span style="color: hsl(120, 100%, 40%);">+'instance1-key.pem' to instance 1's '/var/lib/asterisk/keys/' directory and</span><br><span style="color: hsl(120, 100%, 40%);">+'instance2-key.pem' to instance 2's '/var/lib/asterisk/keys/' directory.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+If you have files that can be shared among multiple tests, you can create a</span><br><span style="color: hsl(120, 100%, 40%);">+directory following the same structure as above in some parent directory and</span><br><span style="color: hsl(120, 100%, 40%);">+direct each test to include it with the 'base-files-path' parameter in</span><br><span style="color: hsl(120, 100%, 40%);">+its test-config.yaml file. See sample-yaml/test-config.yaml.sample for</span><br><span style="color: hsl(120, 100%, 40%);">+more info.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> d) Test Execution</span><br><span> </span><br><span> The "run-test" executable will be run by a top level application in the</span><br><span>diff --git a/lib/python/asterisk/asterisk.py b/lib/python/asterisk/asterisk.py</span><br><span>index 977dc6e..f1371db 100644</span><br><span>--- a/lib/python/asterisk/asterisk.py</span><br><span>+++ b/lib/python/asterisk/asterisk.py</span><br><span>@@ -16,6 +16,7 @@</span><br><span> import shutil</span><br><span> import logging</span><br><span> import fileinput</span><br><span style="color: hsl(120, 100%, 40%);">+import polyfill</span><br><span> </span><br><span> from . import test_suite_utils</span><br><span> </span><br><span>@@ -43,7 +44,6 @@</span><br><span> </span><br><span> LOGGER = logging.getLogger(__name__)</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> class AsteriskRemoteProtocol(protocol.Protocol):</span><br><span> """Class that acts as a remote protocol to Asterisk"""</span><br><span> </span><br><span>@@ -367,6 +367,7 @@</span><br><span> self.original_astmoddir = ""</span><br><span> self.remote_config = remote_config</span><br><span> self.memcheck_delay_stop = 0</span><br><span style="color: hsl(120, 100%, 40%);">+ self.instance_id = 0</span><br><span> if test_config is not None and 'memcheck-delay-stop' in test_config:</span><br><span> self.memcheck_delay_stop = test_config['memcheck-delay-stop'] or 0</span><br><span> </span><br><span>@@ -419,6 +420,7 @@</span><br><span> while True:</span><br><span> if not os.path.isdir("%s/ast%d" % (self.base, i)):</span><br><span> self.base = "%s/ast%d" % (self.base, i)</span><br><span style="color: hsl(120, 100%, 40%);">+ self.instance_id = i;</span><br><span> break</span><br><span> i += 1</span><br><span> </span><br><span>@@ -673,6 +675,7 @@</span><br><span> for key in self.directories.keys():</span><br><span> value = value.replace("<<%s>>" % key,</span><br><span> "%s%s" % (self.base, self.directories[key]))</span><br><span style="color: hsl(120, 100%, 40%);">+ value = value.replace("<<instanceid>>", self.instance_id)</span><br><span> return value</span><br><span> </span><br><span> # Quick little function for doing search and replace in a file used below.</span><br><span>@@ -787,6 +790,61 @@</span><br><span> except IOError:</span><br><span> LOGGER.warn("The destination is not writable '%s'" % target_path)</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ def install_files(self, source_path):</span><br><span style="color: hsl(120, 100%, 40%);">+ """Installs all files located in a directory to this</span><br><span style="color: hsl(120, 100%, 40%);">+ instance of Asterisk.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ Keyword Arguments:</span><br><span style="color: hsl(120, 100%, 40%);">+ source_path This argument must be the path to the directory</span><br><span style="color: hsl(120, 100%, 40%);">+ containing the files to be installed into standard</span><br><span style="color: hsl(120, 100%, 40%);">+ locations.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ Example: tests/my-cool-test/files/ast1</span><br><span style="color: hsl(120, 100%, 40%);">+ This directory must contain at least one sub-directory</span><br><span style="color: hsl(120, 100%, 40%);">+ matching the name of an entry from the asterisk.conf</span><br><span style="color: hsl(120, 100%, 40%);">+ "directories" category. For example "astvarlibdir".</span><br><span style="color: hsl(120, 100%, 40%);">+ Each entry in those directories will be copied to the</span><br><span style="color: hsl(120, 100%, 40%);">+ runtime equivalent for the test.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ Example:</span><br><span style="color: hsl(120, 100%, 40%);">+ Given the following filesystem layout:</span><br><span style="color: hsl(120, 100%, 40%);">+ tests/my-cool-test/files/ast1</span><br><span style="color: hsl(120, 100%, 40%);">+ - astvarlibdir/</span><br><span style="color: hsl(120, 100%, 40%);">+ - sounds/</span><br><span style="color: hsl(120, 100%, 40%);">+ - recordings/</span><br><span style="color: hsl(120, 100%, 40%);">+ - myrecording.ulaw</span><br><span style="color: hsl(120, 100%, 40%);">+ - astkeydir/keys/</span><br><span style="color: hsl(120, 100%, 40%);">+ - mykey.pem</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ...and given the following entries in asterisk.conf:</span><br><span style="color: hsl(120, 100%, 40%);">+ astvarlibdir => /var/lib/asterisk</span><br><span style="color: hsl(120, 100%, 40%);">+ astkeydir => /var/lib/asterisk</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ The myrecording.ulaw and mykey.pem files would be copied to</span><br><span style="color: hsl(120, 100%, 40%);">+ <testroot>/var/lib/asterisk/sounds/recordings/ and</span><br><span style="color: hsl(120, 100%, 40%);">+ <testroot>/var/lib/asterisk/keys/ respectively.</span><br><span style="color: hsl(120, 100%, 40%);">+ """</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ self._make_directory_structure()</span><br><span style="color: hsl(120, 100%, 40%);">+ if not os.path.exists(source_path):</span><br><span style="color: hsl(120, 100%, 40%);">+ return</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for fname in os.listdir(source_path):</span><br><span style="color: hsl(120, 100%, 40%);">+ source = "%s/%s" % (source_path, fname)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if os.path.isdir(source):</span><br><span style="color: hsl(120, 100%, 40%);">+ # If the directory name isn't one of the</span><br><span style="color: hsl(120, 100%, 40%);">+ # well known directories, just skip it.</span><br><span style="color: hsl(120, 100%, 40%);">+ if not self.directories[fname]:</span><br><span style="color: hsl(120, 100%, 40%);">+ continue</span><br><span style="color: hsl(120, 100%, 40%);">+ # join() doesn't work if one of the paths is absolute</span><br><span style="color: hsl(120, 100%, 40%);">+ # so we have to skip the leading '/' in the directory</span><br><span style="color: hsl(120, 100%, 40%);">+ # entry. I.E /var/lib/asterisk becomes var/lib/asterisk</span><br><span style="color: hsl(120, 100%, 40%);">+ direntry = self.directories[fname]</span><br><span style="color: hsl(120, 100%, 40%);">+ dirpath = direntry[1:] if direntry[0] == '/' else direntry</span><br><span style="color: hsl(120, 100%, 40%);">+ dest = os.path.join(self.base, dirpath)</span><br><span style="color: hsl(120, 100%, 40%);">+ polyfill.copytree(source, dest, dirs_exist_ok=True)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def _overwrite_file(self, filename, values):</span><br><span> """Overwrite a particular config file</span><br><span> </span><br><span>diff --git a/lib/python/asterisk/test_case.py b/lib/python/asterisk/test_case.py</span><br><span>index 1c7edfa..c4cde27 100644</span><br><span>--- a/lib/python/asterisk/test_case.py</span><br><span>+++ b/lib/python/asterisk/test_case.py</span><br><span>@@ -122,6 +122,7 @@</span><br><span> self.ami = []</span><br><span> self.fastagi = []</span><br><span> self.base_config_path = None</span><br><span style="color: hsl(120, 100%, 40%);">+ self.base_files_path = None</span><br><span> self.reactor_timeout = 30</span><br><span> self.passed = None</span><br><span> self.fail_tokens = []</span><br><span>@@ -149,6 +150,8 @@</span><br><span> if test_config:</span><br><span> if 'config-path' in test_config:</span><br><span> self.base_config_path = test_config['config-path']</span><br><span style="color: hsl(120, 100%, 40%);">+ if 'base-files-path' in test_config:</span><br><span style="color: hsl(120, 100%, 40%);">+ self.base_files_path = test_config['base-files-path']</span><br><span> if 'reactor-timeout' in test_config:</span><br><span> self.reactor_timeout = test_config['reactor-timeout']</span><br><span> if 'memcheck-delay-stop' in test_config:</span><br><span>@@ -248,7 +251,8 @@</span><br><span> for i in range(count)]</span><br><span> return asterisks</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- def create_asterisk(self, count=1, base_configs_path=None, test_config=None):</span><br><span style="color: hsl(120, 100%, 40%);">+ def create_asterisk(self, count=1, base_configs_path=None, test_config=None,</span><br><span style="color: hsl(120, 100%, 40%);">+ base_files_path=None):</span><br><span> """Create n instances of Asterisk</span><br><span> </span><br><span> Note: if the instances of Asterisk being created are remote, the</span><br><span>@@ -264,6 +268,11 @@</span><br><span> configuration can be overwritten by individual tests,</span><br><span> however.</span><br><span> test_config Test Configuration</span><br><span style="color: hsl(120, 100%, 40%);">+ base_files_path Provides common files for Asterisk instances</span><br><span style="color: hsl(120, 100%, 40%);">+ to use. This is useful for certain test types that use</span><br><span style="color: hsl(120, 100%, 40%);">+ the same files, like keys, all the time. This</span><br><span style="color: hsl(120, 100%, 40%);">+ configuration can be overwritten by individual tests,</span><br><span style="color: hsl(120, 100%, 40%);">+ however.</span><br><span> """</span><br><span> for i, ast_config in enumerate(self.get_asterisk_hosts(count)):</span><br><span> local_num = ast_config.get('num')</span><br><span>@@ -300,6 +309,19 @@</span><br><span> (self.test_name, local_num),</span><br><span> self.test_config.get_deps())</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ # If a base files directory exists for this Asterisk instance</span><br><span style="color: hsl(120, 100%, 40%);">+ # has been provided, install it first</span><br><span style="color: hsl(120, 100%, 40%);">+ if base_files_path is None:</span><br><span style="color: hsl(120, 100%, 40%);">+ base_files_path = self.base_files_path</span><br><span style="color: hsl(120, 100%, 40%);">+ if base_files_path:</span><br><span style="color: hsl(120, 100%, 40%);">+ self.ast[i].install_files("%s/common" % (base_files_path))</span><br><span style="color: hsl(120, 100%, 40%);">+ self.ast[i].install_files("%s/ast%d" %</span><br><span style="color: hsl(120, 100%, 40%);">+ (base_files_path, local_num))</span><br><span style="color: hsl(120, 100%, 40%);">+ # Copy test specific config files</span><br><span style="color: hsl(120, 100%, 40%);">+ self.ast[i].install_files("%s/files/common" % (self.test_name))</span><br><span style="color: hsl(120, 100%, 40%);">+ self.ast[i].install_files("%s/files/ast%d" %</span><br><span style="color: hsl(120, 100%, 40%);">+ (self.test_name, local_num))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> def create_ami_factory(self, count=1, username="user", secret="mysecret",</span><br><span> port=5038):</span><br><span> """Create n instances of AMI. Each AMI instance will attempt to connect</span><br><span>diff --git a/lib/python/polyfill.py b/lib/python/polyfill.py</span><br><span>new file mode 100644</span><br><span>index 0000000..bd95d5f</span><br><span>--- /dev/null</span><br><span>+++ b/lib/python/polyfill.py</span><br><span>@@ -0,0 +1,85 @@</span><br><span style="color: hsl(120, 100%, 40%);">+import sys</span><br><span style="color: hsl(120, 100%, 40%);">+import os</span><br><span style="color: hsl(120, 100%, 40%);">+import shutil</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+"""</span><br><span style="color: hsl(120, 100%, 40%);">+ These are polyfills for a few file/directory functions</span><br><span style="color: hsl(120, 100%, 40%);">+ that don't show up until Python 3.9.</span><br><span style="color: hsl(120, 100%, 40%);">+"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def makedirs(name, mode=0o777, exist_ok=False):</span><br><span style="color: hsl(120, 100%, 40%);">+ head, tail = os.path.split(name)</span><br><span style="color: hsl(120, 100%, 40%);">+ if not tail:</span><br><span style="color: hsl(120, 100%, 40%);">+ head, tail = os.path.split(head)</span><br><span style="color: hsl(120, 100%, 40%);">+ if head and tail and not os.path.exists(head):</span><br><span style="color: hsl(120, 100%, 40%);">+ try:</span><br><span style="color: hsl(120, 100%, 40%);">+ makedirs(head, exist_ok=exist_ok)</span><br><span style="color: hsl(120, 100%, 40%);">+ except FileExistsError:</span><br><span style="color: hsl(120, 100%, 40%);">+ # Defeats race condition when another thread created the path</span><br><span style="color: hsl(120, 100%, 40%);">+ pass</span><br><span style="color: hsl(120, 100%, 40%);">+ cdir = os.curdir</span><br><span style="color: hsl(120, 100%, 40%);">+ if isinstance(tail, bytes):</span><br><span style="color: hsl(120, 100%, 40%);">+ cdir = bytes(os.curdir, 'ASCII')</span><br><span style="color: hsl(120, 100%, 40%);">+ if tail == cdir: # xxx/newdir/. exists if xxx/newdir exists</span><br><span style="color: hsl(120, 100%, 40%);">+ return</span><br><span style="color: hsl(120, 100%, 40%);">+ try:</span><br><span style="color: hsl(120, 100%, 40%);">+ os.mkdir(name, mode)</span><br><span style="color: hsl(120, 100%, 40%);">+ except OSError:</span><br><span style="color: hsl(120, 100%, 40%);">+ if not exist_ok or not os.path.isdir(name):</span><br><span style="color: hsl(120, 100%, 40%);">+ raise</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def copytree(src, dst, symlinks=False, ignore=None, copy_function=shutil.copy2,</span><br><span style="color: hsl(120, 100%, 40%);">+ ignore_dangling_symlinks=False, dirs_exist_ok=False):</span><br><span style="color: hsl(120, 100%, 40%);">+ names = os.listdir(src)</span><br><span style="color: hsl(120, 100%, 40%);">+ if ignore is not None:</span><br><span style="color: hsl(120, 100%, 40%);">+ ignored_names = ignore(src, names)</span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span style="color: hsl(120, 100%, 40%);">+ ignored_names = set()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ makedirs(dst, exist_ok=dirs_exist_ok)</span><br><span style="color: hsl(120, 100%, 40%);">+ errors = []</span><br><span style="color: hsl(120, 100%, 40%);">+ for name in names:</span><br><span style="color: hsl(120, 100%, 40%);">+ if name in ignored_names:</span><br><span style="color: hsl(120, 100%, 40%);">+ continue</span><br><span style="color: hsl(120, 100%, 40%);">+ srcname = os.path.join(src, name)</span><br><span style="color: hsl(120, 100%, 40%);">+ dstname = os.path.join(dst, name)</span><br><span style="color: hsl(120, 100%, 40%);">+ try:</span><br><span style="color: hsl(120, 100%, 40%);">+ if os.path.islink(srcname):</span><br><span style="color: hsl(120, 100%, 40%);">+ linkto = os.readlink(srcname)</span><br><span style="color: hsl(120, 100%, 40%);">+ if symlinks:</span><br><span style="color: hsl(120, 100%, 40%);">+ # We can't just leave it to `copy_function` because legacy</span><br><span style="color: hsl(120, 100%, 40%);">+ # code with a custom `copy_function` may rely on copytree</span><br><span style="color: hsl(120, 100%, 40%);">+ # doing the right thing.</span><br><span style="color: hsl(120, 100%, 40%);">+ os.symlink(linkto, dstname)</span><br><span style="color: hsl(120, 100%, 40%);">+ shutil.copystat(srcname, dstname, follow_symlinks=not symlinks)</span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span style="color: hsl(120, 100%, 40%);">+ # ignore dangling symlink if the flag is on</span><br><span style="color: hsl(120, 100%, 40%);">+ if not os.path.exists(linkto) and ignore_dangling_symlinks:</span><br><span style="color: hsl(120, 100%, 40%);">+ continue</span><br><span style="color: hsl(120, 100%, 40%);">+ # otherwise let the copy occurs. copy2 will raise an error</span><br><span style="color: hsl(120, 100%, 40%);">+ if os.path.isdir(srcname):</span><br><span style="color: hsl(120, 100%, 40%);">+ copytree(srcname, dstname, symlinks, ignore,</span><br><span style="color: hsl(120, 100%, 40%);">+ copy_function, dirs_exist_ok=dirs_exist_ok)</span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span style="color: hsl(120, 100%, 40%);">+ copy_function(srcname, dstname)</span><br><span style="color: hsl(120, 100%, 40%);">+ elif os.path.isdir(srcname):</span><br><span style="color: hsl(120, 100%, 40%);">+ copytree(srcname, dstname, symlinks, ignore, copy_function, dirs_exist_ok=dirs_exist_ok)</span><br><span style="color: hsl(120, 100%, 40%);">+ else:</span><br><span style="color: hsl(120, 100%, 40%);">+ # Will raise a SpecialFileError for unsupported file types</span><br><span style="color: hsl(120, 100%, 40%);">+ copy_function(srcname, dstname)</span><br><span style="color: hsl(120, 100%, 40%);">+ # catch the Error from the recursive copytree so that we can</span><br><span style="color: hsl(120, 100%, 40%);">+ # continue with other files</span><br><span style="color: hsl(120, 100%, 40%);">+ except shutil.Error as err:</span><br><span style="color: hsl(120, 100%, 40%);">+ errors.extend(err.args[0])</span><br><span style="color: hsl(120, 100%, 40%);">+ except OSError as why:</span><br><span style="color: hsl(120, 100%, 40%);">+ errors.append((srcname, dstname, str(why)))</span><br><span style="color: hsl(120, 100%, 40%);">+ try:</span><br><span style="color: hsl(120, 100%, 40%);">+ shutil.copystat(src, dst)</span><br><span style="color: hsl(120, 100%, 40%);">+ except OSError as why:</span><br><span style="color: hsl(120, 100%, 40%);">+ # Copying file access times may fail on Windows</span><br><span style="color: hsl(120, 100%, 40%);">+ if getattr(why, 'winerror', None) is None:</span><br><span style="color: hsl(120, 100%, 40%);">+ errors.append((src, dst, str(why)))</span><br><span style="color: hsl(120, 100%, 40%);">+ if errors:</span><br><span style="color: hsl(120, 100%, 40%);">+ raise shutil.Error(errors)</span><br><span style="color: hsl(120, 100%, 40%);">+ return dst</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>diff --git a/sample-yaml/test-config.yaml.sample b/sample-yaml/test-config.yaml.sample</span><br><span>index 773b98f..37a2de9 100644</span><br><span>--- a/sample-yaml/test-config.yaml.sample</span><br><span>+++ b/sample-yaml/test-config.yaml.sample</span><br><span>@@ -204,6 +204,25 @@</span><br><span> # large sets of tests that cover the same functionality, and allows them</span><br><span> # to share config files.</span><br><span> config-path: 'tests/foo'</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ # A path to a base directory containing abritrary files to be copied</span><br><span style="color: hsl(120, 100%, 40%);">+ # into the Asterisk well-known directories for Asterisk instances</span><br><span style="color: hsl(120, 100%, 40%);">+ # in this test.</span><br><span style="color: hsl(120, 100%, 40%);">+ # The directory should be relative to the testsuite top-level directory.</span><br><span style="color: hsl(120, 100%, 40%);">+ # For example... If set to 'tests/mytests/myfiles' then files in</span><br><span style="color: hsl(120, 100%, 40%);">+ # 'tests/mytest/myfiles/common/astvarlibdir/keys' would be copied to all</span><br><span style="color: hsl(120, 100%, 40%);">+ # of this test's Asterisk instances's '/var/lib/asterisk/keys' directories.</span><br><span style="color: hsl(120, 100%, 40%);">+ # Files in 'tests/mytest/myfiles/ast1/astvarlibdir/keys' would be copied</span><br><span style="color: hsl(120, 100%, 40%);">+ # into the first Asterisk instance's '/var/lib/asterisk/keys' directory.</span><br><span style="color: hsl(120, 100%, 40%);">+ # Files in 'tests/mytest/myfiles/ast2/astvarlibdir/keys' would be copied</span><br><span style="color: hsl(120, 100%, 40%);">+ # into the second Asterisk instance's '/var/lib/asterisk/keys' directory.</span><br><span style="color: hsl(120, 100%, 40%);">+ # etc.</span><br><span style="color: hsl(120, 100%, 40%);">+ # This is useful for tests that can share files like keys and certificates.</span><br><span style="color: hsl(120, 100%, 40%);">+ # NOTE: Files in individual '<testdir>/files' directories are automatically</span><br><span style="color: hsl(120, 100%, 40%);">+ # copied so you don't need this parameter to share files among instances</span><br><span style="color: hsl(120, 100%, 40%);">+ # in the same test.</span><br><span style="color: hsl(120, 100%, 40%);">+ base-files-directory: 'tests/mytests/myfiles'</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> reactor-timeout: 30</span><br><span> spawn-after-hangup: True</span><br><span> # Indicate that the test is going to be restarting Asterisk so</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/testsuite/+/16587">change 16587</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/testsuite/+/16587"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: testsuite </div>
<div style="display:none"> Gerrit-Branch: 19 </div>
<div style="display:none"> Gerrit-Change-Id: I69bb54fea459e83f65a474d3b74c40b28fe01b4c </div>
<div style="display:none"> Gerrit-Change-Number: 16587 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>