sapling/eden/test_support/testcase.py
Adam Simpkins 72f6ada7c2 add a new EdenTestCaseBase class
Summary:
This adds a new `EdenTestCaseBase` class to serve as the base class across a
number of our integration tests and some of our CLI tests.

The main goal of this is to allow eliminating many of the annoying `*Mixin`
classes used in a lot of our integration tests.  These mixin classes are
annoying since they result in complicated multiple inheritance, and it can be
tricky to ensure that the method resolution order behaves the way you want.
The systemd tests in particular use a lot of mixins, which gets complicated.
These mixin classes are also awkward from a Python typing perspective, and the
systemd tests end up resorting to just declaring different APIs in several
places when `typing.TYPE_CHECKING` is True.

The fact that `EdenTestCaseBase` has a `contextlib.ExitStack` member variable
should make it easier for us to eliminate these mixins moving forward: rather
than using mixins that use inheritance and assume a `self.cleanUp()` method
exists, we can transition this code to standalone functions or context
managers, and they can take the `ExitStack` variable as an argument if
necessary.

Reviewed By: wez

Differential Revision: D21084097

fbshipit-source-id: 77ee457b7debe6f584f630e3e30f79fe634a2026
2020-04-22 15:02:41 -07:00

69 lines
2.4 KiB
Python

#!/usr/bin/env python3
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2.
import contextlib
import unittest
from pathlib import Path
from typing import Optional, Union
from . import environment_variable as env_module
from .temporary_directory import TempFileManager
class EdenTestCaseBase(unittest.TestCase):
"""Base class for many EdenFS test cases.
This class provides a number of convenience functions.
"""
exit_stack: contextlib.ExitStack
temp_mgr: TempFileManager
def setUp(self) -> None:
super().setUp()
self.exit_stack = contextlib.ExitStack()
self.addCleanup(self.exit_stack.close)
self.temp_mgr = self.exit_stack.enter_context(
TempFileManager(self._get_tmp_prefix())
)
def _get_tmp_prefix(self) -> str:
"""Get a prefix to use for the test's temporary directory name. """
# Attempt to include a potion of the test name in the temporary directory
# prefix, but limit it to 20 characters. If the path is too long EdenFS will
# fail to start since its Unix socket path won't fit in sockaddr_un, which has a
# 108 byte maximum path length.
method_name = self._testMethodName
for strip_prefix in ("test_", "test"):
if method_name.startswith(strip_prefix):
method_name = method_name[len(strip_prefix) :]
break
return f"eden_test.{method_name[:20]}."
def setenv(self, name: str, value: Optional[str]) -> None:
self.exit_stack.enter_context(env_module.setenv_scope(name, value))
def unsetenv(self, name: str) -> None:
self.exit_stack.enter_context(env_module.unsetenv_scope(name))
@property
def tmp_dir(self) -> Path:
return self.temp_mgr.top_level_tmp_dir()
def make_temp_dir(self, prefix: Optional[str] = None) -> Path:
"""Make a directory with a uniquely-generated name under the top-level test-case
subdirectory.
"""
return self.temp_mgr.make_temp_dir(prefix=prefix)
def make_test_dir(self, name: Union[Path, str], parents: bool = True) -> Path:
"""Make a directory with a specific name under the top-level test-case
subdirectory.
"""
dir_path = self.tmp_dir / name
dir_path.mkdir(parents=parents)
return dir_path