From 7d41f623ddabdccdcdb4ea46877cb0c5de67abed Mon Sep 17 00:00:00 2001
From: Adrian Likins <alikins@redhat.com>
Date: Fri, 19 Aug 2016 18:11:24 -0400
Subject: [PATCH] Move py34 mock_open compat to compat/test/mock (#17157)

test/units/plugins/action/test_action.py had code
for handling a bug in python 3.4's mock_open that
causes errors when reading binary data.

Moved to compat/tests/mock.py so other tests can
use it by default.
---
 lib/ansible/compat/tests/mock.py         | 80 +++++++++++++++++++++++
 test/units/plugins/action/test_action.py | 81 ------------------------
 2 files changed, 80 insertions(+), 81 deletions(-)

diff --git a/lib/ansible/compat/tests/mock.py b/lib/ansible/compat/tests/mock.py
index 0614391c4b1..c4bda61ccbc 100644
--- a/lib/ansible/compat/tests/mock.py
+++ b/lib/ansible/compat/tests/mock.py
@@ -22,6 +22,7 @@ __metaclass__ = type
 '''
 Compat module for Python3.x's unittest.mock module
 '''
+import sys
 
 # Python 2.7
 
@@ -36,3 +37,82 @@ except ImportError:
         from mock import *
     except ImportError:
         print('You need the mock library installed on python2.x to run tests')
+
+
+# Prior to 3.4.4, mock_open cannot handle binary read_data
+if sys.version_info >= (3,) and sys.version_info < (3, 4, 4):
+    file_spec = None
+
+    def _iterate_read_data(read_data):
+        # Helper for mock_open:
+        # Retrieve lines from read_data via a generator so that separate calls to
+        # readline, read, and readlines are properly interleaved
+        sep = b'\n' if isinstance(read_data, bytes) else '\n'
+        data_as_list = [l + sep for l in read_data.split(sep)]
+
+        if data_as_list[-1] == sep:
+            # If the last line ended in a newline, the list comprehension will have an
+            # extra entry that's just a newline.  Remove this.
+            data_as_list = data_as_list[:-1]
+        else:
+            # If there wasn't an extra newline by itself, then the file being
+            # emulated doesn't have a newline to end the last line  remove the
+            # newline that our naive format() added
+            data_as_list[-1] = data_as_list[-1][:-1]
+
+        for line in data_as_list:
+            yield line
+
+    def mock_open(mock=None, read_data=''):
+        """
+        A helper function to create a mock to replace the use of `open`. It works
+        for `open` called directly or used as a context manager.
+
+        The `mock` argument is the mock object to configure. If `None` (the
+        default) then a `MagicMock` will be created for you, with the API limited
+        to methods or attributes available on standard file handles.
+
+        `read_data` is a string for the `read` methoddline`, and `readlines` of the
+        file handle to return.  This is an empty string by default.
+        """
+        def _readlines_side_effect(*args, **kwargs):
+            if handle.readlines.return_value is not None:
+                return handle.readlines.return_value
+            return list(_data)
+
+        def _read_side_effect(*args, **kwargs):
+            if handle.read.return_value is not None:
+                return handle.read.return_value
+            return type(read_data)().join(_data)
+
+        def _readline_side_effect():
+            if handle.readline.return_value is not None:
+                while True:
+                    yield handle.readline.return_value
+            for line in _data:
+                yield line
+
+        global file_spec
+        if file_spec is None:
+            import _io
+            file_spec = list(set(dir(_io.TextIOWrapper)).union(set(dir(_io.BytesIO))))
+
+        if mock is None:
+            mock = MagicMock(name='open', spec=open)
+
+        handle = MagicMock(spec=file_spec)
+        handle.__enter__.return_value = handle
+
+        _data = _iterate_read_data(read_data)
+
+        handle.write.return_value = None
+        handle.read.return_value = None
+        handle.readline.return_value = None
+        handle.readlines.return_value = None
+
+        handle.read.side_effect = _read_side_effect
+        handle.readline.side_effect = _readline_side_effect()
+        handle.readlines.side_effect = _readlines_side_effect
+
+        mock.return_value = handle
+        return mock
diff --git a/test/units/plugins/action/test_action.py b/test/units/plugins/action/test_action.py
index 269ffcdbda6..e2481daa1c4 100644
--- a/test/units/plugins/action/test_action.py
+++ b/test/units/plugins/action/test_action.py
@@ -26,8 +26,6 @@ import json
 import pipes
 import os
 
-from sys import version_info
-
 try:
     import builtins
 except ImportError:
@@ -66,85 +64,6 @@ WINDOWS_ARGS = "<<INCLUDE_ANSIBLE_MODULE_JSON_ARGS>>"
 # POWERSHELL_COMMON
 """
 
-# Prior to 3.4.4, mock_open cannot handle binary read_data
-if version_info >= (3,) and version_info < (3, 4, 4):
-    file_spec = None
-
-    def _iterate_read_data(read_data):
-        # Helper for mock_open:
-        # Retrieve lines from read_data via a generator so that separate calls to
-        # readline, read, and readlines are properly interleaved
-        sep = b'\n' if isinstance(read_data, bytes) else '\n'
-        data_as_list = [l + sep for l in read_data.split(sep)]
-
-        if data_as_list[-1] == sep:
-            # If the last line ended in a newline, the list comprehension will have an
-            # extra entry that's just a newline.  Remove this.
-            data_as_list = data_as_list[:-1]
-        else:
-            # If there wasn't an extra newline by itself, then the file being
-            # emulated doesn't have a newline to end the last line  remove the
-            # newline that our naive format() added
-            data_as_list[-1] = data_as_list[-1][:-1]
-
-        for line in data_as_list:
-            yield line
-
-    def mock_open(mock=None, read_data=''):
-        """
-        A helper function to create a mock to replace the use of `open`. It works
-        for `open` called directly or used as a context manager.
-
-        The `mock` argument is the mock object to configure. If `None` (the
-        default) then a `MagicMock` will be created for you, with the API limited
-        to methods or attributes available on standard file handles.
-
-        `read_data` is a string for the `read` methoddline`, and `readlines` of the
-        file handle to return.  This is an empty string by default.
-        """
-        def _readlines_side_effect(*args, **kwargs):
-            if handle.readlines.return_value is not None:
-                return handle.readlines.return_value
-            return list(_data)
-
-        def _read_side_effect(*args, **kwargs):
-            if handle.read.return_value is not None:
-                return handle.read.return_value
-            return type(read_data)().join(_data)
-
-        def _readline_side_effect():
-            if handle.readline.return_value is not None:
-                while True:
-                    yield handle.readline.return_value
-            for line in _data:
-                yield line
-
-
-        global file_spec
-        if file_spec is None:
-            import _io
-            file_spec = list(set(dir(_io.TextIOWrapper)).union(set(dir(_io.BytesIO))))
-
-        if mock is None:
-            mock = MagicMock(name='open', spec=open)
-
-        handle = MagicMock(spec=file_spec)
-        handle.__enter__.return_value = handle
-
-        _data = _iterate_read_data(read_data)
-
-        handle.write.return_value = None
-        handle.read.return_value = None
-        handle.readline.return_value = None
-        handle.readlines.return_value = None
-
-        handle.read.side_effect = _read_side_effect
-        handle.readline.side_effect = _readline_side_effect()
-        handle.readlines.side_effect = _readlines_side_effect
-
-        mock.return_value = handle
-        return mock
-
 
 class DerivedActionBase(ActionBase):
     TRANSFERS_FILES = False