• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2009 Google Inc. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#            http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Test that FakeFilesystem calls work identically to a real filesystem."""
16# pylint: disable-all
17
18import os
19import shutil
20import sys
21import tempfile
22import time
23import unittest
24
25from pyfakefs import fake_filesystem
26
27
28def sep(path):
29    """Converts slashes in the path to the architecture's path seperator."""
30    if isinstance(path, str):
31        return path.replace('/', os.sep)
32    return path
33
34
35def _get_errno(raised_error):
36    if raised_error is not None:
37        try:
38            return raised_error.errno
39        except AttributeError:
40            pass
41
42
43class TestCase(unittest.TestCase):
44    is_windows = sys.platform.startswith('win')
45    _FAKE_FS_BASE = sep('/fakefs')
46
47
48class FakeFilesystemVsRealTest(TestCase):
49    def _paths(self, path):
50        """For a given path, return paths in the real and fake filesystems."""
51        if not path:
52            return None, None
53        return (os.path.join(self.real_base, path),
54                os.path.join(self.fake_base, path))
55
56    def _create_test_file(self, file_type, path, contents=None):
57        """Create a dir, file, or link in both the real fs and the fake."""
58        path = sep(path)
59        self._created_files.append([file_type, path, contents])
60        real_path, fake_path = self._paths(path)
61        if file_type == 'd':
62            os.mkdir(real_path)
63            self.fake_os.mkdir(fake_path)
64        if file_type == 'f':
65            fh = open(real_path, 'w')
66            fh.write(contents or '')
67            fh.close()
68            fh = self.fake_open(fake_path, 'w')
69            fh.write(contents or '')
70            fh.close()
71        # b for binary file
72        if file_type == 'b':
73            fh = open(real_path, 'wb')
74            fh.write(contents or '')
75            fh.close()
76            fh = self.fake_open(fake_path, 'wb')
77            fh.write(contents or '')
78            fh.close()
79        # l for symlink, h for hard link
80        if file_type in ('l', 'h'):
81            real_target, fake_target = (contents, contents)
82            # If it begins with '/', make it relative to the base. You can't go
83            # creating files in / for the real file system.
84            if contents.startswith(os.sep):
85                real_target, fake_target = self._paths(contents[1:])
86            if file_type == 'l':
87                os.symlink(real_target, real_path)
88                self.fake_os.symlink(fake_target, fake_path)
89            elif file_type == 'h':
90                os.link(real_target, real_path)
91                self.fake_os.link(fake_target, fake_path)
92
93    def setUp(self):
94        # Base paths in the real and test file systems. We keep them different
95        # so that missing features in the fake don't fall through to the base
96        # operations and magically succeed.
97        tsname = 'fakefs.%s' % time.time()
98        self.cwd = os.getcwd()
99        # Fully expand the base_path - required on OS X.
100        self.real_base = os.path.realpath(
101            os.path.join(tempfile.gettempdir(), tsname))
102        os.chdir(tempfile.gettempdir())
103        if os.path.isdir(self.real_base):
104            shutil.rmtree(self.real_base)
105        os.mkdir(self.real_base)
106        self.fake_base = self._FAKE_FS_BASE
107
108        # Make sure we can write to the physical testing temp directory.
109        self.assertTrue(os.access(self.real_base, os.W_OK))
110
111        self.fake_filesystem = fake_filesystem.FakeFilesystem()
112        self.fake_filesystem.create_dir(self.fake_base)
113        self.fake_os = fake_filesystem.FakeOsModule(self.fake_filesystem)
114        self.fake_open = fake_filesystem.FakeFileOpen(self.fake_filesystem)
115        self._created_files = []
116
117        os.chdir(self.real_base)
118        self.fake_os.chdir(self.fake_base)
119
120    def tearDown(self):
121        # We have to remove all the files from the real FS. Doing the same for
122        # the fake FS is optional, but doing it is an extra sanity check.
123        os.chdir(tempfile.gettempdir())
124        try:
125            rev_files = self._created_files[:]
126            rev_files.reverse()
127            for info in rev_files:
128                real_path, fake_path = self._paths(info[1])
129                if info[0] == 'd':
130                    try:
131                        os.rmdir(real_path)
132                    except OSError as e:
133                        if 'Directory not empty' in e:
134                            self.fail('Real path %s not empty: %s : %s' % (
135                                real_path, e, os.listdir(real_path)))
136                        else:
137                            raise
138                    self.fake_os.rmdir(fake_path)
139                if info[0] == 'f' or info[0] == 'l':
140                    os.remove(real_path)
141                    self.fake_os.remove(fake_path)
142        finally:
143            shutil.rmtree(self.real_base)
144            os.chdir(self.cwd)
145
146    def _compare_behaviors(self, method_name, path, real, fake,
147                           method_returns_path=False):
148        """Invoke an os method in both real and fake contexts and compare
149        results.
150
151        Invoke a real filesystem method with a path to a real file and invoke
152        a fake filesystem method with a path to a fake file and compare the
153        results. We expect some calls to throw Exceptions, so we catch those
154        and compare them.
155
156        Args:
157            method_name: Name of method being tested, for use in
158                error messages.
159            path: potential path to a file in the real and fake file systems,
160                passing an empty tuple indicates that no arguments to pass
161                to method.
162            real: built-in system library or method from the built-in system
163                library which takes a path as an arg and returns some value.
164            fake: fake_filsystem object or method from a fake_filesystem class
165                which takes a path as an arg and returns some value.
166            method_returns_path: True if the method returns a path, and thus we
167                must compensate for expected difference between real and fake.
168
169        Returns:
170            A description of the difference in behavior, or None.
171        """
172        # pylint: disable=C6403
173
174        def _error_class(exc):
175            if exc:
176                if hasattr(exc, 'errno'):
177                    return '{}({})'.format(exc.__class__.__name__, exc.errno)
178                return exc.__class__.__name__
179            return 'None'
180
181        real_err, real_value = self._get_real_value(method_name, path, real)
182        fake_err, fake_value = self._get_fake_value(method_name, path, fake)
183
184        method_call = '%s' % method_name
185        method_call += '()' if path == () else '(%s)' % path
186        # We only compare on the error class because the acutal error contents
187        # is almost always different because of the file paths.
188        if _error_class(real_err) != _error_class(fake_err):
189            if real_err is None:
190                return '%s: real version returned %s, fake raised %s' % (
191                    method_call, real_value, _error_class(fake_err))
192            if fake_err is None:
193                return '%s: real version raised %s, fake returned %s' % (
194                    method_call, _error_class(real_err), fake_value)
195            return '%s: real version raised %s, fake raised %s' % (
196                method_call, _error_class(real_err), _error_class(fake_err))
197        real_errno = _get_errno(real_err)
198        fake_errno = _get_errno(fake_err)
199        if real_errno != fake_errno:
200            return '%s(%s): both raised %s, real errno %s, fake errno %s' % (
201                method_name, path, _error_class(real_err),
202                real_errno, fake_errno)
203        # If the method is supposed to return a full path AND both values
204        # begin with the expected full path, then trim it off.
205        if method_returns_path:
206            if (real_value and fake_value
207                    and real_value.startswith(self.real_base)
208                    and fake_value.startswith(self.fake_base)):
209                real_value = real_value[len(self.real_base):]
210                fake_value = fake_value[len(self.fake_base):]
211        if real_value != fake_value:
212            return '%s: real return %s, fake returned %s' % (
213                method_call, real_value, fake_value)
214        return None
215
216    @staticmethod
217    def _get_fake_value(method_name, path, fake):
218        fake_value = None
219        fake_err = None
220        try:
221            fake_method = fake
222            if not callable(fake):
223                fake_method = getattr(fake, method_name)
224            args = [] if path == () else [path]
225            fake_value = str(fake_method(*args))
226        except Exception as e:  # pylint: disable-msg=W0703
227            fake_err = e
228        return fake_err, fake_value
229
230    @staticmethod
231    def _get_real_value(method_name, path, real):
232        real_value = None
233        real_err = None
234        # Catching Exception below gives a lint warning, but it's what we need.
235        try:
236            args = [] if path == () else [path]
237            real_method = real
238            if not callable(real):
239                real_method = getattr(real, method_name)
240            real_value = str(real_method(*args))
241        except Exception as e:  # pylint: disable-msg=W0703
242            real_err = e
243        return real_err, real_value
244
245    def assertOsMethodBehaviorMatches(self, method_name, path,
246                                      method_returns_path=False):
247        """Invoke an os method in both real and fake contexts and compare.
248
249        For a given method name (from the os module) and a path, compare the
250        behavior of the system provided module against the fake_filesystem
251        module.
252        We expect results and/or Exceptions raised to be identical.
253
254        Args:
255            method_name: Name of method being tested.
256            path: potential path to a file in the real and fake file systems.
257            method_returns_path: True if the method returns a path, and thus we
258                must compensate for expected difference between real and fake.
259
260        Returns:
261            A description of the difference in behavior, or None.
262        """
263        path = sep(path)
264        return self._compare_behaviors(method_name, path, os, self.fake_os,
265                                       method_returns_path)
266
267    def diff_open_method_behavior(self, method_name, path, mode, data,
268                                  method_returns_data=True):
269        """Invoke an open method in both real and fkae contexts and compare.
270
271        Args:
272            method_name: Name of method being tested.
273            path: potential path to a file in the real and fake file systems.
274            mode: how to open the file.
275            data: any data to pass to the method.
276            method_returns_data: True if a method returns some sort of data.
277
278        For a given method name (from builtin open) and a path, compare the
279        behavior of the system provided module against the fake_filesystem
280        module.
281        We expect results and/or Exceptions raised to be identical.
282
283        Returns:
284            A description of the difference in behavior, or None.
285        """
286        with open(path, mode) as real_fh:
287            with self.fake_open(path, mode) as fake_fh:
288                return self._compare_behaviors(
289                    method_name, data, real_fh, fake_fh, method_returns_data)
290
291    def diff_os_path_method_behavior(self, method_name, path,
292                                     method_returns_path=False):
293        """Invoke an os.path method in both real and fake contexts and compare.
294
295        For a given method name (from the os.path module) and a path, compare
296        the behavior of the system provided module against the
297        fake_filesytem module.
298        We expect results and/or Exceptions raised to be identical.
299
300        Args:
301            method_name: Name of method being tested.
302            path: potential path to a file in the real and fake file systems.
303            method_returns_path: True if the method returns a path, and thus we
304                must compensate for expected difference between real and fake.
305
306        Returns:
307            A description of the difference in behavior, or None.
308        """
309        return self._compare_behaviors(method_name, path, os.path,
310                                       self.fake_os.path,
311                                       method_returns_path)
312
313    def assertOsPathMethodBehaviorMatches(self, method_name, path,
314                                          method_returns_path=False):
315        """Assert that an os.path behaves the same in both real and
316        fake contexts.
317
318        Wraps DiffOsPathMethodBehavior, raising AssertionError if any
319        differences are reported.
320
321        Args:
322            method_name: Name of method being tested.
323            path: potential path to a file in the real and fake file systems.
324            method_returns_path: True if the method returns a path, and thus we
325                must compensate for expected difference between real and fake.
326
327        Raises:
328            AssertionError if there is any difference in behavior.
329        """
330        path = sep(path)
331        diff = self.diff_os_path_method_behavior(
332            method_name, path, method_returns_path)
333        if diff:
334            self.fail(diff)
335
336    def assertAllOsBehaviorsMatch(self, path):
337        path = sep(path)
338        os_method_names = [] if self.is_windows else ['readlink']
339        os_method_names_no_args = ['getcwd']
340        os_path_method_names = ['isabs',
341                                'isdir',
342                                'isfile',
343                                'exists'
344                                ]
345        if not self.is_windows:
346            os_path_method_names.append('islink')
347            os_path_method_names.append('lexists')
348        wrapped_methods = [
349            ['access', self._access_real, self._access_fake],
350            ['stat.size', self._stat_size_real, self._stat_size_fake],
351            ['lstat.size', self._lstat_size_real, self._lstat_size_fake]
352        ]
353
354        differences = []
355        for method_name in os_method_names:
356            diff = self.assertOsMethodBehaviorMatches(method_name, path)
357            if diff:
358                differences.append(diff)
359        for method_name in os_method_names_no_args:
360            diff = self.assertOsMethodBehaviorMatches(method_name, (),
361                                                      method_returns_path=True)
362            if diff:
363                differences.append(diff)
364        for method_name in os_path_method_names:
365            diff = self.diff_os_path_method_behavior(method_name, path)
366            if diff:
367                differences.append(diff)
368        for m in wrapped_methods:
369            diff = self._compare_behaviors(m[0], path, m[1], m[2])
370            if diff:
371                differences.append(diff)
372        if differences:
373            self.fail('Behaviors do not match for %s:\n    %s' %
374                      (path, '\n    '.join(differences)))
375
376    def assertFileHandleBehaviorsMatch(self, path, mode, data):
377        path = sep(path)
378        write_method_names = ['write', 'writelines']
379        read_method_names = ['read', 'readlines']
380        other_method_names = ['truncate', 'flush', 'close']
381        differences = []
382        for method_name in write_method_names:
383            diff = self.diff_open_method_behavior(
384                method_name, path, mode, data)
385            if diff:
386                differences.append(diff)
387        for method_name in read_method_names + other_method_names:
388            diff = self.diff_open_method_behavior(method_name, path, mode, ())
389            if diff:
390                differences.append(diff)
391        if differences:
392            self.fail('Behaviors do not match for %s:\n    %s' %
393                      (path, '\n    '.join(differences)))
394
395    # Helpers for checks which are not straight method calls.
396    @staticmethod
397    def _access_real(path):
398        return os.access(path, os.R_OK)
399
400    def _access_fake(self, path):
401        return self.fake_os.access(path, os.R_OK)
402
403    def _stat_size_real(self, path):
404        real_path, unused_fake_path = self._paths(path)
405        # fake_filesystem.py does not implement stat().st_size for directories
406        if os.path.isdir(real_path):
407            return None
408        return os.stat(real_path).st_size
409
410    def _stat_size_fake(self, path):
411        unused_real_path, fake_path = self._paths(path)
412        # fake_filesystem.py does not implement stat().st_size for directories
413        if self.fake_os.path.isdir(fake_path):
414            return None
415        return self.fake_os.stat(fake_path).st_size
416
417    def _lstat_size_real(self, path):
418        real_path, unused_fake_path = self._paths(path)
419        if os.path.isdir(real_path):
420            return None
421        size = os.lstat(real_path).st_size
422        # Account for the difference in the lengths of the absolute paths.
423        if os.path.islink(real_path):
424            if os.readlink(real_path).startswith(os.sep):
425                size -= len(self.real_base)
426        return size
427
428    def _lstat_size_fake(self, path):
429        unused_real_path, fake_path = self._paths(path)
430        # size = 0
431        if self.fake_os.path.isdir(fake_path):
432            return None
433        size = self.fake_os.lstat(fake_path).st_size
434        # Account for the difference in the lengths of the absolute paths.
435        if self.fake_os.path.islink(fake_path):
436            if self.fake_os.readlink(fake_path).startswith(os.sep):
437                size -= len(self.fake_base)
438        return size
439
440    def test_isabs(self):
441        # We do not have to create any files for isabs.
442        self.assertOsPathMethodBehaviorMatches('isabs', None)
443        self.assertOsPathMethodBehaviorMatches('isabs', '')
444        self.assertOsPathMethodBehaviorMatches('isabs', '/')
445        self.assertOsPathMethodBehaviorMatches('isabs', '/a')
446        self.assertOsPathMethodBehaviorMatches('isabs', 'a')
447
448    def test_none_path(self):
449        self.assertAllOsBehaviorsMatch(None)
450
451    def test_empty_path(self):
452        self.assertAllOsBehaviorsMatch('')
453
454    def test_root_path(self):
455        self.assertAllOsBehaviorsMatch('/')
456
457    def test_non_existant_file(self):
458        self.assertAllOsBehaviorsMatch('foo')
459
460    def test_empty_file(self):
461        self._create_test_file('f', 'aFile')
462        self.assertAllOsBehaviorsMatch('aFile')
463
464    def test_file_with_contents(self):
465        self._create_test_file('f', 'aFile', 'some contents')
466        self.assertAllOsBehaviorsMatch('aFile')
467
468    def test_file_with_binary_contents(self):
469        self._create_test_file('b', 'aFile', b'some contents')
470        self.assertAllOsBehaviorsMatch('aFile')
471
472    @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows')
473    def test_sym_link_to_empty_file(self):
474        self._create_test_file('f', 'aFile')
475        self._create_test_file('l', 'link_to_empty', 'aFile')
476        self.assertAllOsBehaviorsMatch('link_to_empty')
477
478    @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows')
479    def test_hard_link_to_empty_file(self):
480        self._create_test_file('f', 'aFile')
481        self._create_test_file('h', 'link_to_empty', 'aFile')
482        self.assertAllOsBehaviorsMatch('link_to_empty')
483
484    @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows')
485    def test_sym_link_to_real_file(self):
486        self._create_test_file('f', 'aFile', 'some contents')
487        self._create_test_file('l', 'link_to_file', 'aFile')
488        self.assertAllOsBehaviorsMatch('link_to_file')
489
490    @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows')
491    def test_hard_link_to_real_file(self):
492        self._create_test_file('f', 'aFile', 'some contents')
493        self._create_test_file('h', 'link_to_file', 'aFile')
494        self.assertAllOsBehaviorsMatch('link_to_file')
495
496    @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows')
497    def test_broken_sym_link(self):
498        self._create_test_file('l', 'broken_link', 'broken')
499        self._create_test_file('l', 'loop', '/a/loop')
500        self.assertAllOsBehaviorsMatch('broken_link')
501
502    def test_file_in_a_folder(self):
503        self._create_test_file('d', 'a')
504        self._create_test_file('d', 'a/b')
505        self._create_test_file('f', 'a/b/file', 'contents')
506        self.assertAllOsBehaviorsMatch('a/b/file')
507
508    @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows')
509    def test_absolute_sym_link_to_folder(self):
510        self._create_test_file('d', 'a')
511        self._create_test_file('d', 'a/b')
512        self._create_test_file('f', 'a/b/file', 'contents')
513        self._create_test_file('l', 'a/link', '/a/b')
514        self.assertAllOsBehaviorsMatch('a/link/file')
515
516    @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows')
517    def test_link_to_folder_after_chdir(self):
518        self._create_test_file('d', 'a')
519        self._create_test_file('d', 'a/b')
520        self._create_test_file('f', 'a/b/file', 'contents')
521        self._create_test_file('l', 'a/link', '/a/b')
522
523        real_dir, fake_dir = self._paths('a/b')
524        os.chdir(real_dir)
525        self.fake_os.chdir(fake_dir)
526        self.assertAllOsBehaviorsMatch('file')
527
528    @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows')
529    def test_relative_sym_link_to_folder(self):
530        self._create_test_file('d', 'a')
531        self._create_test_file('d', 'a/b')
532        self._create_test_file('f', 'a/b/file', 'contents')
533        self._create_test_file('l', 'a/link', 'b')
534        self.assertAllOsBehaviorsMatch('a/link/file')
535
536    @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows')
537    def test_sym_link_to_parent(self):
538        # Soft links on HFS+ / OS X behave differently.
539        if os.uname()[0] != 'Darwin':
540            self._create_test_file('d', 'a')
541            self._create_test_file('d', 'a/b')
542            self._create_test_file('l', 'a/b/c', '..')
543            self.assertAllOsBehaviorsMatch('a/b/c')
544
545    @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows')
546    def test_path_through_sym_link_to_parent(self):
547        self._create_test_file('d', 'a')
548        self._create_test_file('f', 'a/target', 'contents')
549        self._create_test_file('d', 'a/b')
550        self._create_test_file('l', 'a/b/c', '..')
551        self.assertAllOsBehaviorsMatch('a/b/c/target')
552
553    @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows')
554    def test_sym_link_to_sibling_directory(self):
555        self._create_test_file('d', 'a')
556        self._create_test_file('d', 'a/b')
557        self._create_test_file('d', 'a/sibling_of_b')
558        self._create_test_file('f', 'a/sibling_of_b/target', 'contents')
559        self._create_test_file('l', 'a/b/c', '../sibling_of_b')
560        self.assertAllOsBehaviorsMatch('a/b/c/target')
561
562    @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows')
563    def test_sym_link_to_sibling_directory_non_existant_file(self):
564        self._create_test_file('d', 'a')
565        self._create_test_file('d', 'a/b')
566        self._create_test_file('d', 'a/sibling_of_b')
567        self._create_test_file('f', 'a/sibling_of_b/target', 'contents')
568        self._create_test_file('l', 'a/b/c', '../sibling_of_b')
569        self.assertAllOsBehaviorsMatch('a/b/c/file_does_not_exist')
570
571    @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows')
572    def test_broken_sym_link_to_sibling_directory(self):
573        self._create_test_file('d', 'a')
574        self._create_test_file('d', 'a/b')
575        self._create_test_file('d', 'a/sibling_of_b')
576        self._create_test_file('f', 'a/sibling_of_b/target', 'contents')
577        self._create_test_file('l', 'a/b/c', '../broken_sibling_of_b')
578        self.assertAllOsBehaviorsMatch('a/b/c/target')
579
580    def test_relative_path(self):
581        self._create_test_file('d', 'a')
582        self._create_test_file('d', 'a/b')
583        self._create_test_file('d', 'a/sibling_of_b')
584        self._create_test_file('f', 'a/sibling_of_b/target', 'contents')
585        self.assertAllOsBehaviorsMatch('a/b/../sibling_of_b/target')
586
587    def test_broken_relative_path(self):
588        self._create_test_file('d', 'a')
589        self._create_test_file('d', 'a/b')
590        self._create_test_file('d', 'a/sibling_of_b')
591        self._create_test_file('f', 'a/sibling_of_b/target', 'contents')
592        self.assertAllOsBehaviorsMatch('a/b/../broken/target')
593
594    def test_bad_relative_path(self):
595        self._create_test_file('d', 'a')
596        self._create_test_file('f', 'a/target', 'contents')
597        self._create_test_file('d', 'a/b')
598        self._create_test_file('d', 'a/sibling_of_b')
599        self._create_test_file('f', 'a/sibling_of_b/target', 'contents')
600        self.assertAllOsBehaviorsMatch('a/b/../broken/../target')
601
602    def test_getmtime_nonexistant_path(self):
603        self.assertOsPathMethodBehaviorMatches('getmtime', 'no/such/path')
604
605    def test_builtin_open_modes(self):
606        self._create_test_file('f', 'read', 'some contents')
607        self._create_test_file('f', 'write', 'some contents')
608        self._create_test_file('f', 'append', 'some contents')
609        self.assertFileHandleBehaviorsMatch('read', 'r', 'other contents')
610        self.assertFileHandleBehaviorsMatch('write', 'w', 'other contents')
611        self.assertFileHandleBehaviorsMatch('append', 'a', 'other contents')
612        self._create_test_file('f', 'readplus', 'some contents')
613        self._create_test_file('f', 'writeplus', 'some contents')
614        self.assertFileHandleBehaviorsMatch(
615            'readplus', 'r+', 'other contents')
616        self.assertFileHandleBehaviorsMatch(
617            'writeplus', 'w+', 'other contents')
618        self._create_test_file('b', 'binaryread', b'some contents')
619        self._create_test_file('b', 'binarywrite', b'some contents')
620        self._create_test_file('b', 'binaryappend', b'some contents')
621        self.assertFileHandleBehaviorsMatch(
622            'binaryread', 'rb', b'other contents')
623        self.assertFileHandleBehaviorsMatch(
624            'binarywrite', 'wb', b'other contents')
625        self.assertFileHandleBehaviorsMatch(
626            'binaryappend', 'ab', b'other contents')
627        self.assertFileHandleBehaviorsMatch('read', 'rb', 'other contents')
628        self.assertFileHandleBehaviorsMatch('write', 'wb', 'other contents')
629        self.assertFileHandleBehaviorsMatch('append', 'ab', 'other contents')
630
631
632def main(_):
633    unittest.main()
634
635
636if __name__ == '__main__':
637    unittest.main()
638