test/TestRunner.py
f8eab8ed
 
 # tests are fairly 'live' (but safe to run)
 # setup authorized_keys for logged in user such
 # that the user can log in as themselves before running tests
 
 import unittest
 import getpass
 import ansible.runner
 import os
 import shutil
fae3a718
 import time
90bf67cf
 import tempfile
f8eab8ed
 
6f09b41e
 from nose.plugins.skip import SkipTest
 
f9b70822
 
6f09b41e
 def get_binary(name):
     for directory in os.environ["PATH"].split(os.pathsep):
         path = os.path.join(directory, name)
         if os.path.isfile(path) and os.access(path, os.X_OK):
             return path
     return None
 
f9b70822
 
f8eab8ed
 class TestRunner(unittest.TestCase):
 
83b9a43e
     def setUp(self):
         self.user = getpass.getuser()
         self.runner = ansible.runner.Runner(
611d56dc
             basedir='test/',
83b9a43e
             module_name='ping',
             module_path='library/',
             module_args='',
             remote_user=self.user,
             remote_pass=None,
             host_list='test/ansible_hosts',
             timeout=5,
             forks=1,
             background=0,
             pattern='all',
9985995a
             transport='local',
83b9a43e
         )
         self.cwd = os.getcwd()
         self.test_dir = os.path.join(self.cwd, 'test')
         self.stage_dir = self._prepare_stage_dir()
 
     def _prepare_stage_dir(self):
         stage_path = os.path.join(self.test_dir, 'test_data')
         if os.path.exists(stage_path):
             shutil.rmtree(stage_path, ignore_errors=False)
             assert not os.path.exists(stage_path)
         os.makedirs(stage_path)
         assert os.path.exists(stage_path)
         return stage_path
 
     def _get_test_file(self, filename):
         # get a file inside the test input directory
         filename = os.path.join(self.test_dir, filename)
         assert os.path.exists(filename)
         return filename
 
     def _get_stage_file(self, filename):
         # get a file inside the test output directory
         filename = os.path.join(self.stage_dir, filename)
         return filename
 
515fd9e9
     def _run(self, module_name, module_args, background=0, check_mode=False):
83b9a43e
         ''' run a module and get the localhost results '''
         self.runner.module_name = module_name
         args = ' '.join(module_args)
         self.runner.module_args = args
         self.runner.background = background
515fd9e9
         self.runner.check = check_mode
83b9a43e
         results = self.runner.run()
         # when using nosetests this will only show up on failure
         # which is pretty useful
83d1028a
         assert "localhost" in results['contacted']
         return results['contacted']['localhost']
83b9a43e
 
611d56dc
     def test_action_plugins(self):
         result = self._run("uncategorized_plugin", [])
         assert result.get("msg") == "uncategorized"
         result = self._run("categorized_plugin", [])
         assert result.get("msg") == "categorized"
 
83b9a43e
     def test_ping(self):
         result = self._run('ping', [])
         assert "ping" in result
 
     def test_facter(self):
         if not get_binary("facter"):
             raise SkipTest
         result = self._run('facter', [])
         assert "hostname" in result
 
     # temporarily disbabled since it occasionally hangs
     # ohai's fault, setup module doesn't actually run this
     # to get ohai's "facts" anyway
     #
     #def test_ohai(self):
     #    if not get_binary("facter"):
     #            raise SkipTest
     #    result = self._run('ohai',[])
     #    assert "hostname" in result
 
     def test_copy(self):
         # test copy module, change trigger, etc
         input_ = self._get_test_file('sample.j2')
         output = self._get_stage_file('sample.out')
         assert not os.path.exists(output)
         result = self._run('copy', [
             "src=%s" % input_,
             "dest=%s" % output,
         ])
         assert os.path.exists(output)
         data_in = file(input_).read()
         data_out = file(output).read()
         assert data_in == data_out
         assert 'failed' not in result
f9b70822
         assert result['changed'] is True
83b9a43e
         assert 'md5sum' in result
         result = self._run('copy', [
             "src=%s" % input_,
             "dest=%s" % output,
         ])
f9b70822
         assert result['changed'] is False
515fd9e9
         with open(output, "a") as output_stream:
             output_stream.write("output file now differs from input")
         result = self._run('copy',
                            ["src=%s" % input_, "dest=%s" % output, "force=no"],
                            check_mode=True)
         assert result['changed'] is False
83b9a43e
 
     def test_command(self):
         # test command module, change trigger, etc
f9b70822
         result = self._run('command', ["/bin/echo", "hi"])
83b9a43e
         assert "failed" not in result
         assert "msg" not in result
         assert result['rc'] == 0
         assert result['stdout'] == 'hi'
         assert result['stderr'] == ''
 
f9b70822
         result = self._run('command', ["false"])
83b9a43e
         assert result['rc'] == 1
         assert 'failed' not in result
 
f9b70822
         result = self._run('command', ["/usr/bin/this_does_not_exist", "splat"])
83b9a43e
         assert 'msg' in result
         assert 'failed' in result
 
f9b70822
         result = self._run('shell', ["/bin/echo", "$HOME"])
83b9a43e
         assert 'failed' not in result
         assert result['rc'] == 0
 
f9b70822
         result = self._run('command', ["creates='/tmp/ansible command test'", "chdir=/tmp", "touch", "'ansible command test'"])
6477bdc6
         assert 'changed' in result
         assert result['rc'] == 0
 
f9b70822
         result = self._run('command', ["creates='/tmp/ansible command test'", "false"])
6477bdc6
         assert 'skipped' in result
 
f9b70822
         result = self._run('shell', ["removes=/tmp/ansible\\ command\\ test", "chdir=/tmp", "rm -f 'ansible command test'; echo $?"])
6477bdc6
         assert 'changed' in result
         assert result['rc'] == 0
         assert result['stdout'] == '0'
 
f9b70822
         result = self._run('shell', ["removes=/tmp/ansible\\ command\\ test", "false"])
6477bdc6
         assert 'skipped' in result
 
eb49ee38
     def test_git(self):
f9b70822
         self._run('file', ['path=/tmp/gitdemo', 'state=absent'])
         self._run('file', ['path=/tmp/gd', 'state=absent'])
f41d4ac3
         self._run('file', ['path=/tmp/gdbare', 'state=absent'])
         self._run('file', ['path=/tmp/gdreference', 'state=absent'])
         self._run('file', ['path=/tmp/gdreftest', 'state=absent'])
f9b70822
         self._run('command', ['git init gitdemo', 'chdir=/tmp'])
         self._run('command', ['touch a', 'chdir=/tmp/gitdemo'])
         self._run('command', ['git add *', 'chdir=/tmp/gitdemo'])
f41d4ac3
         self._run('command', ['git commit -m "test commit 1"', 'chdir=/tmp/gitdemo'])
f9b70822
         self._run('command', ['touch b', 'chdir=/tmp/gitdemo'])
         self._run('command', ['git add *', 'chdir=/tmp/gitdemo'])
         self._run('command', ['git commit -m "test commit 2"', 'chdir=/tmp/gitdemo'])
         result = self._run('git', ["repo=\"file:///tmp/gitdemo\"", "dest=/tmp/gd"])
eb49ee38
         assert result['changed']
         # test the force option not set
f9b70822
         self._run('file', ['path=/tmp/gd/a', 'state=absent'])
         result = self._run('git', ["repo=\"file:///tmp/gitdemo\"", "dest=/tmp/gd", "force=no"])
eb49ee38
         assert result['failed']
         # test the force option when set
f9b70822
         result = self._run('git', ["repo=\"file:///tmp/gitdemo\"", "dest=/tmp/gd", "force=yes"])
eb49ee38
         assert result['changed']
f41d4ac3
         # test the bare option
         result = self._run('git', ["repo=\"file:///tmp/gitdemo\"", "dest=/tmp/gdbare", "bare=yes", "remote=test"])
         assert result['changed']
4391cbfd
         # test a no-op fetch, add origin for el6 versions of git
         self._run('command', ['git remote add origin file:///tmp/gitdemo', 'chdir=/tmp/gdbare'])
f41d4ac3
         result = self._run('git', ["repo=\"file:///tmp/gitdemo\"", "dest=/tmp/gdbare", "bare=yes"])
         assert not result['changed']
         # test whether fetch is working for bare repos
         self._run('command', ['touch c', 'chdir=/tmp/gitdemo'])
         self._run('command', ['git add *', 'chdir=/tmp/gitdemo'])
         self._run('command', ['git commit -m "test commit 3"', 'chdir=/tmp/gitdemo'])
         result = self._run('git', ["repo=\"file:///tmp/gitdemo\"", "dest=/tmp/gdbare", "bare=yes"])
         assert result['changed']
         # test reference repos
         result = self._run('git', ["repo=\"file:///tmp/gdbare\"", "dest=/tmp/gdreference", "bare=yes"])
         assert result['changed']
         result = self._run('git', ["repo=\"file:///tmp/gitdemo\"", "dest=/tmp/gdreftest", "reference=/tmp/gdreference/"])
         assert result['changed']
         assert os.path.isfile('/tmp/gdreftest/a')
         result = self._run('command', ['ls', 'chdir=/tmp/gdreference/objects/pack'])
         assert result['stdout'] != ''
         result = self._run('command', ['ls', 'chdir=/tmp/gdreftest/.git/objects/pack'])
         assert result['stdout'] == ''
f9b70822
 
eefe66f1
     def test_file(self):
         filedemo = tempfile.mkstemp()[1]
         assert self._run('file', ['dest=' + filedemo, 'state=directory'])['failed']
         assert os.path.isfile(filedemo)
 
         assert self._run('file', ['dest=' + filedemo, 'src=/dev/null', 'state=link'])['failed']
         assert os.path.isfile(filedemo)
 
cbc12f0d
         res = self._run('file', ['dest=' + filedemo, 'mode=604', 'state=file'])
         assert res['changed']
eefe66f1
         assert os.path.isfile(filedemo) and os.stat(filedemo).st_mode == 0100604
 
         assert self._run('file', ['dest=' + filedemo, 'state=absent'])['changed']
         assert not os.path.exists(filedemo)
         assert not self._run('file', ['dest=' + filedemo, 'state=absent'])['changed']
 
         filedemo = tempfile.mkdtemp()
         assert self._run('file', ['dest=' + filedemo, 'state=file'])['failed']
         assert os.path.isdir(filedemo)
 
c5d2f6b0
         # this used to fail but will now make a 'null' symlink in the directory pointing to dev/null.
         # I feel this is ok but don't want to enforce it with a test.
         #result = self._run('file', ['dest=' + filedemo, 'src=/dev/null', 'state=link'])
         #assert result['failed']
         #assert os.path.isdir(filedemo)
eefe66f1
 
         assert self._run('file', ['dest=' + filedemo, 'mode=701', 'state=directory'])['changed']
         assert os.path.isdir(filedemo) and os.stat(filedemo).st_mode == 040701
 
         assert self._run('file', ['dest=' + filedemo, 'state=absent'])['changed']
         assert not os.path.exists(filedemo)
         assert not self._run('file', ['dest=' + filedemo, 'state=absent'])['changed']
 
         tmp_dir = tempfile.mkdtemp()
         filedemo = os.path.join(tmp_dir, 'link')
         os.symlink('/dev/zero', filedemo)
         assert self._run('file', ['dest=' + filedemo, 'state=file'])['failed']
         assert os.path.islink(filedemo)
 
         assert self._run('file', ['dest=' + filedemo, 'state=directory'])['failed']
         assert os.path.islink(filedemo)
 
         assert self._run('file', ['dest=' + filedemo, 'src=/dev/null', 'state=link'])['changed']
         assert os.path.islink(filedemo) and os.path.realpath(filedemo) == '/dev/null'
 
         assert self._run('file', ['dest=' + filedemo, 'state=absent'])['changed']
         assert not os.path.exists(filedemo)
         assert not self._run('file', ['dest=' + filedemo, 'state=absent'])['changed']
190ce16b
 
         # Make sure that we can deal safely with bad symlinks
         os.symlink('/tmp/non_existent_target', filedemo)
         assert self._run('file', ['dest=' + tmp_dir, 'state=directory recurse=yes mode=701'])['changed']
         assert not self._run('file', ['dest=' + tmp_dir, 'state=directory', 'recurse=yes', 'owner=' + str(os.getuid())])['changed']
         assert os.path.islink(filedemo)
         assert self._run('file', ['dest=' + filedemo, 'state=absent'])['changed']
         assert not os.path.exists(filedemo)
eefe66f1
         os.rmdir(tmp_dir)
 
83b9a43e
     def test_large_output(self):
ecc0b077
         large_path = "/usr/share/dict/words"
         if not os.path.exists(large_path):
             large_path = "/usr/share/dict/cracklib-small"
             if not os.path.exists(large_path):
                 raise SkipTest
83b9a43e
         # Ensure reading a large amount of output from a command doesn't hang.
f9b70822
         result = self._run('command', ["/bin/cat", large_path])
83b9a43e
         assert "failed" not in result
         assert "msg" not in result
         assert result['rc'] == 0
         assert len(result['stdout']) > 100000
         assert result['stderr'] == ''
 
     def test_async(self):
         # test async launch and job status
         # of any particular module
f9b70822
         result = self._run('command', [get_binary("sleep"), "3"], background=20)
83b9a43e
         assert 'ansible_job_id' in result
         assert 'started' in result
         jid = result['ansible_job_id']
         # no real chance of this op taking a while, but whatever
         time.sleep(5)
         # CLI will abstract this (when polling), but this is how it works internally
f9b70822
         result = self._run('async_status', ["jid=%s" % jid])
83b9a43e
         # TODO: would be nice to have tests for supervisory process
         # killing job after X seconds
         assert 'finished' in result
         assert 'failed' not in result
         assert 'rc' in result
         assert 'stdout' in result
         assert result['ansible_job_id'] == jid
 
     def test_fetch(self):
         input_ = self._get_test_file('sample.j2')
83d1028a
         output = os.path.join(self.stage_dir, 'localhost', input_)
f9b70822
         self._run('fetch', ["src=%s" % input_, "dest=%s" % self.stage_dir])
83b9a43e
         assert os.path.exists(output)
         assert open(input_).read() == open(output).read()
 
9cf182c2
     def test_assemble(self):
         input = self._get_test_file('assemble.d')
         output = self._get_stage_file('sample.out')
         result = self._run('assemble', [
             "src=%s" % input,
             "dest=%s" % output,
         ])
         assert os.path.exists(output)
         out = file(output).read()
         assert out.find("first") != -1
         assert out.find("second") != -1
         assert out.find("third") != -1
f9b70822
         assert result['changed'] is True
9cf182c2
         assert 'md5sum' in result
         assert 'failed' not in result
         result = self._run('assemble', [
             "src=%s" % input,
             "dest=%s" % output,
         ])
f9b70822
         assert result['changed'] is False
ad637343
 
943829c9
     def test_lineinfile(self):
7116c83c
         # Unit tests for the lineinfile module, without backref features.
cff8cdd4
         sampleroot = 'rocannon'
         sample_origin = self._get_test_file(sampleroot + '.txt')
         sample = self._get_stage_file(sampleroot + '.out' + '.txt')
f9b70822
         shutil.copy(sample_origin, sample)
943829c9
         # The order of the test cases is important
 
         # defaults to insertafter at the end of the file
         testline = 'First: Line added by default at the end of the file.'
         testcase = ('lineinfile', [
                     "dest=%s" % sample,
                     "regexp='^First: '",
                     "line='%s'" % testline
f9b70822
                     ])
943829c9
         result = self._run(*testcase)
f9b70822
         assert result['changed']
943829c9
         assert result['msg'] == 'line added'
f9b70822
         artifact = [x.strip() for x in open(sample)]
943829c9
         assert artifact[-1] == testline
         assert artifact.count(testline) == 1
 
         # run a second time, verify only one line has been added
         result = self._run(*testcase)
f9b70822
         assert not result['changed']
943829c9
         assert result['msg'] == ''
f9b70822
         artifact = [x.strip() for x in open(sample)]
943829c9
         assert artifact.count(testline) == 1
 
         # insertafter with EOF
         testline = 'Second: Line added with insertafter=EOF'
         testcase = ('lineinfile', [
                     "dest=%s" % sample,
                     "insertafter=EOF",
                     "regexp='^Second: '",
                     "line='%s'" % testline
f9b70822
                     ])
943829c9
         result = self._run(*testcase)
f9b70822
         assert result['changed']
943829c9
         assert result['msg'] == 'line added'
f9b70822
         artifact = [x.strip() for x in open(sample)]
943829c9
         assert artifact[-1] == testline
         assert artifact.count(testline) == 1
 
         # with invalid insertafter regex
f9b70822
         # If the regexp doesn't match and the insertafter doesn't match,
         # do nothing.
943829c9
         testline = 'Third: Line added with an invalid insertafter regex'
         testcase = ('lineinfile', [
                     "dest=%s" % sample,
                     "insertafter='^abcdefgh'",
                     "regexp='^Third: '",
                     "line='%s'" % testline
f9b70822
                     ])
943829c9
         result = self._run(*testcase)
f9b70822
         assert not result['changed']
943829c9
 
         # with an insertafter regex
f9b70822
         # The regexp doesn't match, but the insertafter is specified and does,
         # so insert after insertafter.
943829c9
         testline = 'Fourth: Line added with a valid insertafter regex'
         testcase = ('lineinfile', [
                     "dest=%s" % sample,
                     "insertafter='^receive messages to '",
                     "regexp='^Fourth: '",
                     "line='%s'" % testline
f9b70822
                     ])
943829c9
         result = self._run(*testcase)
f9b70822
         assert result['changed']
943829c9
         assert result['msg'] == 'line added'
f9b70822
         artifact = [x.strip() for x in open(sample)]
943829c9
         assert artifact.count(testline) == 1
         idx = artifact.index('receive messages to and from a corresponding device over any distance')
         assert artifact[idx + 1] == testline
 
         # replacement of a line from a regex
         # we replace the line, so we need to get its idx before the run
f9b70822
         artifact = [x.strip() for x in open(sample)]
943829c9
         target_line = 'combination of microphone, speaker, keyboard and display. It can send and'
         idx = artifact.index(target_line)
 
         testline = 'Fith: replacement of a line: combination of microphone'
         testcase = ('lineinfile', [
                     "dest=%s" % sample,
                     "regexp='combination of microphone'",
                     "line='%s'" % testline
f9b70822
                     ])
943829c9
         result = self._run(*testcase)
f9b70822
         assert result['changed']
943829c9
         assert result['msg'] == 'line replaced'
f9b70822
         artifact = [x.strip() for x in open(sample)]
943829c9
         assert artifact.count(testline) == 1
         assert artifact.index(testline) == idx
         assert target_line not in artifact
 
         # removal of a line
         # we replace the line, so we need to get its idx before the run
f9b70822
         artifact = [x.strip() for x in open(sample)]
943829c9
         target_line = 'receive messages to and from a corresponding device over any distance'
         idx = artifact.index(target_line)
 
         testcase = ('lineinfile', [
                     "dest=%s" % sample,
                     "regexp='^receive messages to and from '",
                     "state=absent"
f9b70822
                     ])
943829c9
         result = self._run(*testcase)
f9b70822
         assert result['changed']
         artifact = [x.strip() for x in open(sample)]
943829c9
         assert target_line not in artifact
 
         # with both insertafter and insertbefore (should fail)
         testline = 'Seventh: this line should not be there'
         testcase = ('lineinfile', [
                     "dest=%s" % sample,
                     "insertafter='BOF'",
                     "insertbefore='BOF'",
                     "regexp='^communication. '",
                     "line='%s'" % testline
f9b70822
                     ])
943829c9
         result = self._run(*testcase)
f9b70822
         assert result['failed']
943829c9
 
         # insertbefore with BOF
         testline = 'Eighth: insertbefore BOF'
         testcase = ('lineinfile', [
                     "dest=%s" % sample,
                     "insertbefore=BOF",
                     "regexp='^Eighth: '",
                     "line='%s'" % testline
f9b70822
                     ])
943829c9
         result = self._run(*testcase)
f9b70822
         assert result['changed']
943829c9
         assert result['msg'] == 'line added'
f9b70822
         artifact = [x.strip() for x in open(sample)]
943829c9
         assert artifact.count(testline) == 1
         assert artifact[0] == testline
 
         # insertbefore with regex
         testline = 'Ninth: insertbefore with a regex'
         testcase = ('lineinfile', [
                     "dest=%s" % sample,
                     "insertbefore='^communication. Typically '",
                     "regexp='^Ninth: '",
                     "line='%s'" % testline
f9b70822
                     ])
943829c9
         result = self._run(*testcase)
f9b70822
         assert result['changed']
943829c9
         assert result['msg'] == 'line added'
f9b70822
         artifact = [x.strip() for x in open(sample)]
943829c9
         assert artifact.count(testline) == 1
         idx = artifact.index('communication. Typically it is depicted as a lunch-box sized object with some')
         assert artifact[idx - 1] == testline
 
cc0c908c
         # Testing validate
         testline = 'Tenth: Testing with validate'
         testcase = ('lineinfile', [
                         "dest=%s" % sample,
                         "regexp='^Tenth: '",
                         "line='%s'" % testline,
                         "validate='grep -q Tenth %s'",
                     ])
         result = self._run(*testcase)
         assert result['changed'], "File wasn't changed when it should have been"
         assert result['msg'] == 'line added', "msg was incorrect"
         artifact = [ x.strip() for x in open(sample) ]
         assert artifact[-1] == testline
 
 
         # Testing validate
         testline = '#11: Testing with validate'
         testcase = ('lineinfile', [
                         "dest=%s" % sample,
                         "regexp='^#11: '",
                         "line='%s'" % testline,
                         "validate='grep -q #12# %s'",
                     ])
         result = self._run(*testcase)
         assert result['failed']
 
943829c9
         # cleanup
         os.unlink(sample)
 
f9b70822
     def test_lineinfile_backrefs(self):
7116c83c
         # Unit tests for the lineinfile module, with backref features.
f9b70822
         sampleroot = 'rocannon'
         sample_origin = self._get_test_file(sampleroot + '.txt')
         origin_lines = [line.strip() for line in open(sample_origin)]
         sample = self._get_stage_file(sampleroot + '.out' + '.txt')
         shutil.copy(sample_origin, sample)
         # The order of the test cases is important
 
         # The regexp doesn't match, so the line will not be added anywhere.
75ceb805
         testline = r'\1: Line added by default at the end of the file.'
f9b70822
         testcase = ('lineinfile', [
                     "dest=%s" % sample,
                     "regexp='^(First): '",
                     "line='%s'" % testline,
                     "backrefs=yes",
                     ])
         result = self._run(*testcase)
         assert not result['changed']
         assert result['msg'] == ''
         artifact = [x.strip() for x in open(sample)]
         assert artifact == origin_lines
 
         # insertafter with EOF
         # The regexp doesn't match, so the line will not be added anywhere.
75ceb805
         testline = r'\1: Line added with insertafter=EOF'
f9b70822
         testcase = ('lineinfile', [
                     "dest=%s" % sample,
                     "insertafter=EOF",
                     "regexp='^(Second): '",
                     "line='%s'" % testline,
                     "backrefs=yes",
                     ])
         result = self._run(*testcase)
         assert not result['changed']
         assert result['msg'] == ''
         artifact = [x.strip() for x in open(sample)]
         assert artifact == origin_lines
 
         # with invalid insertafter regex
         # The regexp doesn't match, so do nothing.
75ceb805
         testline = r'\1: Line added with an invalid insertafter regex'
f9b70822
         testcase = ('lineinfile', [
                     "dest=%s" % sample,
                     "insertafter='^abcdefgh'",
                     "regexp='^(Third): '",
                     "line='%s'" % testline,
                     "backrefs=yes",
                     ])
         result = self._run(*testcase)
         assert not result['changed']
         assert artifact == origin_lines
 
         # with an insertafter regex
         # The regexp doesn't match, so do nothing.
75ceb805
         testline = r'\1: Line added with a valid insertafter regex'
f9b70822
         testcase = ('lineinfile', [
                     "dest=%s" % sample,
                     "insertafter='^receive messages to '",
                     "regexp='^(Fourth): '",
                     "line='%s'" % testline,
                     "backrefs=yes",
                     ])
         result = self._run(*testcase)
         assert not result['changed']
         assert result['msg'] == ''
         assert artifact == origin_lines
 
         # replacement of a line from a regex
         # we replace the line, so we need to get its idx before the run
         artifact = [x.strip() for x in open(sample)]
         target_line = 'combination of microphone, speaker, keyboard and display. It can send and'
         idx = artifact.index(target_line)
943829c9
 
75ceb805
         testline = r'\1 of megaphone'
ca581840
         testline_after = 'combination of megaphone'
f9b70822
         testcase = ('lineinfile', [
                     "dest=%s" % sample,
                     "regexp='(combination) of microphone'",
                     "line='%s'" % testline,
                     "backrefs=yes",
                     ])
         result = self._run(*testcase)
         assert result['changed']
         assert result['msg'] == 'line replaced'
         artifact = [x.strip() for x in open(sample)]
         assert artifact.count(testline_after) == 1
         assert artifact.index(testline_after) == idx
         assert target_line not in artifact
 
ca581840
         # Go again, should be unchanged now.
75ceb805
         testline = r'\1 of megaphone'
ca581840
         testline_after = 'combination of megaphone'
         testcase = ('lineinfile', [
                     "dest=%s" % sample,
                     "regexp='(combination) of megaphone'",
                     "line='%s'" % testline,
                     "backrefs=yes",
                     ])
         result = self._run(*testcase)
         assert not result['changed']
         assert result['msg'] == ''
 
         # Try a numeric, named capture group example.
         f = open(sample, 'a+')
         f.write("1 + 1 = 3" + os.linesep)
         f.close()
75ceb805
         testline = r"2 + \g<num> = 3"
ca581840
         testline_after = "2 + 1 = 3"
         testcase = ('lineinfile', [
                     "dest=%s" % sample,
75ceb805
                     r"regexp='1 \+ (?P<num>\d) = 3'",
ca581840
                     "line='%s'" % testline,
                     "backrefs=yes",
                     ])
         result = self._run(*testcase)
         artifact = [x.strip() for x in open(sample)]
         assert result['changed']
         assert result['msg'] == 'line replaced'
         artifact = [x.strip() for x in open(sample)]
         assert '1 + 1 = 3' not in artifact
         assert testline_after == artifact[-1]
 
f9b70822
         # with both insertafter and insertbefore (should fail)
         testline = 'Seventh: this line should not be there'
         testcase = ('lineinfile', [
                     "dest=%s" % sample,
                     "insertafter='BOF'",
                     "insertbefore='BOF'",
                     "regexp='^communication. '",
                     "line='%s'" % testline
                     ])
         result = self._run(*testcase)
         assert result['failed']
 
         os.unlink(sample)