<p>George Joseph has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/testsuite/+/16563">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">testsuite: Allow copying of arbitrary file 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 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/mykeyN.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, 218 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/63/16563/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..c6ef6d4 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>@@ -787,6 +787,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/+/16563">change 16563</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/+/16563"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: testsuite </div>
<div style="display:none"> Gerrit-Branch: 16 </div>
<div style="display:none"> Gerrit-Change-Id: I69bb54fea459e83f65a474d3b74c40b28fe01b4c </div>
<div style="display:none"> Gerrit-Change-Number: 16563 </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>