Esempio n. 1
0
    def setUp(self):
        """
        启动测试执行的初始化
        """
        # 初始化日志类
        DebugTool.set_debug(False)
        try:
            # 删除临时日志
            FileTool.remove_files(path=os.path.join(
                _TEST_DATA_DIR, 'temp/http_service_log/log'),
                                  regex_str='test_case*')
        except:
            pass

        self.logger = simple_log.Logger(
            conf_file_name=os.path.join(
                _TEST_DATA_DIR, 'tcp_ip_service/test_http_service.json'),
            logger_name=simple_log.EnumLoggerName.ConsoleAndFile,
            config_type=simple_log.EnumLoggerConfigType.JSON_FILE,
            logfile_path=os.path.join(
                _TEST_DATA_DIR, 'temp/http_service_log/log/test_case.log'),
            is_create_logfile_by_day=True)
        self.logger.setLevelWithHandler(simple_log.DEBUG)

        # 启动服务
        self.server = HttpService(
            logger=self.logger,
            server_status_info_fun=self.server_status_info_fun,
            self_tag='UnitTest',
            log_level=simple_log.INFO,
            server_http_deal_fun=self.server_http_deal_fun)
        _server_opts = HttpService.generate_server_opts()
        _server_opts.ip = "127.0.0.1"
        _server_opts.port = 9513
        self.server.start_server(server_opts=_server_opts)
Esempio n. 2
0
def test_debugtools_1():
    # 测试DebugTools  -  跨模块的打印 - 增加日志类的干扰
    _logger = simple_log.Logger(
        conf_file_name=_TEMP_DIR + '/test_debugtools_1.json',
        logger_name=simple_log.EnumLoggerName.ConsoleAndFile,
        config_type=simple_log.EnumLoggerConfigType.JSON_FILE,
        logfile_path=_TEMP_DIR + '/log/test_debugtools_1.log')
    _logger.log(simple_log.DEBUG,
                'test_debugtools_1:write_log:DEBUG:1:界面应显示本日志,文件不应显示本日志')
    _logger.log(simple_log.INFO,
                'test_debugtools_1:write_log:INFO:2:界面应显示本日志,文件应显示本日志')
    del _logger

    DebugTool.set_debug(True)
    DebugTool.debug_print("自己本模块的打印")
    test_debugtools()
    return
Esempio n. 3
0
    def setUpClass(cls):
        """
        启动测试类执行的初始化,只执行一次
        """
        # 初始化日志类
        DebugTool.set_debug(False)
        try:
            # 删除临时日志
            FileTool.remove_files(path=_TEMP_DIR + '/log/',
                                  regex_str='test_case*')
        except:
            pass

        cls.logger = simple_log.Logger(
            conf_file_name=_TEMP_DIR +
            '/../../simple_grpc/test_simple_grpc.json',
            logger_name=simple_log.EnumLoggerName.ConsoleAndFile,
            config_type=simple_log.EnumLoggerConfigType.JSON_FILE,
            logfile_path=_TEMP_DIR + '/log/test_case_asyn.log',
            is_create_logfile_by_day=True,
        )
        cls.logger.setLevelWithHandler(simple_log.DEBUG)

        # 设置json转换对象的参数映射

        # 日志处理函数
        def _asyn_logging_fun(levelno, topic_name, msg):
            print('haha:%s, %s, %s' % (str(levelno), topic_name, msg))

        # 异步日志
        cls._asyn_logger = CallChainTool.create_call_chain_logger(
            logger=cls.logger,
            asyn_logging=True,
            asyn_logging_fun=_asyn_logging_fun,
            asyn_deal_msg_fun=SimpleGRpcTools.api_call_chain_asyn_deal_msg_fun)

        # 服务端处理类,可以多个服务公用
        cls.servicer_simple_call = SimpleGRpcServicer(logger=cls._asyn_logger)
        cls.servicer_simple_call.add_service(EnumCallMode.Simple,
                                             'service_simple_call_para',
                                             service_simple_call_para)
        cls.servicer_simple_call.add_service(
            EnumCallMode.Simple, 'service_simple_call_no_para_no_return',
            service_simple_call_no_para_no_return)
        cls.servicer_simple_call.add_service(EnumCallMode.Simple,
                                             'service_simple_call_return',
                                             service_simple_call_return)
        cls.servicer_simple_call.add_service(EnumCallMode.Simple,
                                             'service_simple_throw_excepiton',
                                             service_simple_throw_excepiton)
        cls.servicer_simple_call.add_service(EnumCallMode.Simple,
                                             'service_simple_overtime',
                                             service_simple_overtime)
        cls.servicer_simple_call.add_service(EnumCallMode.ClientSideStream,
                                             'service_client_side_stream',
                                             service_client_side_stream)
        cls.servicer_simple_call.add_service(EnumCallMode.ServerSideStream,
                                             'service_server_side_stream',
                                             service_server_side_stream)
        cls.servicer_simple_call.add_service(EnumCallMode.BidirectionalStream,
                                             'service_bidirectional_stream',
                                             service_bidirectional_stream)

        # 初始化并启动服务,简单服务,无SSL,无服务发现
        cls.server_no_ssl_no_zoo_opts = SimpleGRpcServer.generate_server_opts(
            ip='127.0.0.1',
            port=50051,
            max_workers=10,
            max_connect=100,
            is_health_check=True)
        cls.server_no_ssl_no_zoo = SimpleGRpcServer(
            server_name='ServerNoSslNoZoo',
            logger=cls.logger,
            log_level=simple_log.INFO)

        cls.server_no_ssl_no_zoo.start_server(
            server_opts=cls.server_no_ssl_no_zoo_opts,
            servicer_list={'servicer_simple_call': cls.servicer_simple_call},
            is_wait=True)

        # 初始化并启动服务,简单服务,无服务发现,TSL双向认证模式
        _private_key_certificate_chain_pair = SimpleGRpcTools.get_private_key_certificate_chain_pair(
            _TEMP_DIR + '/../../simple_grpc/server.pem',
            _TEMP_DIR + '/../../simple_grpc/server.crt')
        with open(_TEMP_DIR + '/../../simple_grpc/client.crt', 'rb') as f:
            # 根证书
            _root_certificates = f.read()

        cls.server_double_ssl_no_zoo_opts = SimpleGRpcServer.generate_server_opts(
            ip='localhost',
            port=50052,
            max_workers=10,
            max_connect=100,
            is_use_ssl=True,
            private_key_certificate_chain_pairs=(
                _private_key_certificate_chain_pair, ),
            root_certificates=_root_certificates)
        cls.server_double_ssl_no_zoo = SimpleGRpcServer(
            server_name='ServerDoubleSslNoZoo',
            logger=cls.logger,
            log_level=simple_log.INFO)
        cls.server_double_ssl_no_zoo.start_server(
            server_opts=cls.server_double_ssl_no_zoo_opts,
            servicer_list={'servicer_simple_call': cls.servicer_simple_call},
            is_wait=True)

        # 初始化并启动服务,简单服务,无服务发现,TSL单向认证模式(仅验证服务端证书)
        cls.server_server_ssl_no_zoo_opts = SimpleGRpcServer.generate_server_opts(
            ip='localhost',
            port=50053,
            max_workers=10,
            max_connect=100,
            is_use_ssl=True,
            private_key_certificate_chain_pairs=(
                _private_key_certificate_chain_pair, ),
            root_certificates=None)
        cls.server_server_ssl_no_zoo = SimpleGRpcServer(
            server_name='ServerServerSslNoZoo',
            logger=cls.logger,
            log_level=simple_log.INFO)
        cls.server_server_ssl_no_zoo.start_server(
            server_opts=cls.server_server_ssl_no_zoo_opts,
            servicer_list={'servicer_simple_call': cls.servicer_simple_call},
            is_wait=True)
def test_debugtools():
    DebugTool.debug_print("从debug_tool_demo_not_run中的打印信息")
Esempio n. 5
0
    def __init__(self,
                 file: str,
                 is_resume: bool = True,
                 file_size: int = None,
                 md5: str = None,
                 is_overwrite: bool = False,
                 temp_ext: str = 'tmp',
                 info_ext: str = 'info',
                 extend_info: dict = None,
                 thread_num: int = 1,
                 block_size: int = 4096,
                 cache_size: int = 1024,
                 auto_expand: bool = True):
        """
        初始化文件保存对象

        @param {str} file - 文件保存路径(含文件名)
        @param {bool} is_resume=True - 指定是否续传(自动查找已下载的信息), 如果不指定续传将自动删除原来已下载临时文件
            注:如果指定续传,且可以找到原来的临时文件,则以下参数将使用原来的信息,如果有传入则会进行差异值的校验:
                file_size、md5
        @param {int} file_size=None - 文件大小,单位为byte, 如果为None代表未知文件大小, 此时auto_expand参数固定为True
        @param {str} md5=None - 验证文件的md5字符串,如果不传代表不进行验证
        @param {bool} is_overwrite=False - 是否覆盖已有文件,如果为否,则目标文件已存在的情况下抛出异常
        @param {str} temp_ext='tmp' - 处理过程中临时文件扩展名
        @param {str} info_ext='info' - 处理过程中信息文件扩展名
        @param {dict} extend_info=None - 处理过程中要保存的信息字典,例如保存文件下载路径,引用页等信息
        @param {int} thread_num=1 - 写入处理线程数量
        @param {int} block_size=4096 - 每次写入块大小,单位为byte
        @param {int} cache_size=1024 - 单线程缓存大小,单位为kb(注意:真实缓存大小还需要乘以处理线程数量)
        @param {bool} auto_expand=True - 是否自动扩展文件大小(否则在初始化时会自动创建指定大小的文件)

        @throws {FileExistsError} - 如果下载文件已存在且不允许覆盖的情况抛出异常
        @throws {FileNotFoundError} - 续传情况下临时文件不存在则抛出异常
        @throws {InfoFileLockError} - 如果已打开信息文件进行文件存储处理,抛出该异常
        """
        # 检查文件是否存在
        self._file = os.path.abspath(file)
        self._path, self._filename = os.path.split(self._file)
        if os.path.exists(self._file):
            # 文件已存在
            if is_overwrite:
                FileTool.remove_file(self._file)
            else:
                raise FileExistsError('file exists: %s' % self._file)
        else:
            # 创建目录
            FileTool.create_dir(self._path, exist_ok=True)

        # 文件信息字典,该字典登记文件基本信息和写入情况
        self._info: dict = None

        # 锁文件,控制一个文件不能被多个类处理, 先尝试创建锁文件,如果创建失败会抛出异常
        self._lock_file = os.path.join(self._path,
                                       '%s.%s' % (self._filename, 'lock'))
        try:
            self._lock_file_handle = os.open(
                self._lock_file, os.O_CREAT | os.O_EXCL | os.O_RDWR)
        except:
            raise InfoFileLockError('info file is locked')

        try:
            # 获取是否debug状态
            self._debug_on = DebugTool.is_debug_on()
            self._lock_print_timeout = None
            if self._debug_on:
                self._lock_print_timeout = 5.0  # 打印锁等待超时时间

            # 处理信息字典、临时文件、信息文件
            self._temp_file = os.path.join(
                self._path, '%s.%s' % (self._filename, temp_ext))
            self._info_file = os.path.join(
                self._path, '%s.%s' % (self._filename, info_ext))
            self._auto_expand = auto_expand
            self._thread_num = thread_num
            self._block_size = block_size
            self._cache_size = cache_size * 1024

            # 数据处理锁
            self._cache_info_lock = threading.RLock()  # 缓存信息更新锁
            self._tmp_file_lock = threading.RLock()  # 缓存文件写入锁
            self._is_finished = False  # 要控制的完成状态
            self._dealed_finished_lock = threading.RLock()  # 控制多线程操作结束函数的状态更新锁
            self._dealed_finished = False  # 控制多线程操作结束函数只执行一次的变量

            if is_resume and os.path.exists(self._info_file):
                # 自动续传情况
                self._info_file_handle = open(self._info_file,
                                              'r+',
                                              encoding='utf-8')
                self._info_file_handle.seek(0)
                self._info = json.loads(self._info_file_handle.read())

                # 检查传入信息是否一致
                if file_size is not None and file_size != self._info[
                        'file_size']:
                    raise AttributeError(
                        'resume info [file_size] inconsistency, info file [%s], now [%s]'
                        % (str(self._info['file_size']), str(file_size)))

                if md5 is not None and md5 != self._info['md5']:
                    raise AttributeError(
                        'resume info [md5] inconsistency, info file [%s], now [%s]'
                        % (self._info['md5'], md5))

                # 检查临时文件
                self._temp_file = os.path.join(self._path,
                                               self._info['tmp_file'])
                if not os.path.exists(self._temp_file):
                    # 临时文件不存在
                    raise FileNotFoundError('temp file is not found: %s' %
                                            self._temp_file)

                self._tmp_file_handle = open(self._temp_file, 'rb+')
                self._tmp_file_handle.seek(0)
            else:
                # 删除已存在的临时文件信息
                if os.path.exists(self._temp_file):
                    FileTool.remove_file(self._temp_file)

                if os.path.exists(self._info_file):
                    FileTool.remove_file(self._info_file)

                # 形成信息字典
                self._info = {
                    'tmp_file': '%s.%s' % (self._filename, temp_ext),  # 临时文件名称
                    'file_size':
                    -1 if file_size is None else file_size,  # 文件大小
                    'write_size': 0,  # 已写入数据大小
                    'md5': '' if md5 is None else md5,  # md5校验值
                    'extend_info':
                    {} if extend_info is None else extend_info,  # 传入的扩展信息
                    # 存储索引,按位置顺序在数组中登记未写入区间,数组每一项登记未写入数据的开始位置和结束位置
                    'store_index': [[0, file_size - 1]]
                }

                # 生成临时文件
                self._tmp_file_handle = open(self._temp_file, 'wb')
                if not auto_expand and file_size is not None:
                    # 直接生成指定大小的文件
                    self._tmp_file_handle.seek(file_size - 1)  # 跳到指定位置
                    self._tmp_file_handle.write(b'\x00')  # 一定要写入一个字符,否则无效
                    self._tmp_file_handle.flush()

                # 写入信息字典文件
                self._info_file_handle = open(self._info_file,
                                              'w',
                                              encoding='utf-8')
                self._write_info_file()

            # 合并存储索引,把碎片合并成为大块
            self._info['store_index'] = self._f_merge_store_index(
                self._info['store_index'])

            # 初始化缓存等信息
            if self._info['file_size'] == -1:
                # 如果没有文件大小的情况,不支持拆分多写入线程和一次性创建指定大小文件的情况
                self._thread_num = 1
                self._auto_expand = True

            # 缓存处理
            self._max_cache_pos = [
                -1,
            ]  # 当前缓存分配到的区域最大位置
            self._cache = dict()
            for _i in range(self._thread_num):
                self._cache[_i] = {
                    'start': -1,  # 缓存数据对应文件的写入位置, -1代表没有设置
                    'size': 0,  # 缓存数据大小
                    'buffer': bytes(),  # 具体的缓存数据
                    'end_pos': -1,  # 该缓存对应线程要处理的文件块结束位置
                    'lock': threading.RLock(),  # 用于缓存线程处理的锁)
                    'get_start': -1,  # 当前正在获取的数据的开始位置
                    'get_size': 0,  # 当前要获取数据的大小
                }

            # 分配每个缓存要处理文件区域
            for _i in range(self._thread_num):
                self._set_cache_area(_i)
        except:
            # 如果初始化出现异常,清理文件句柄及锁文件
            self._clear_file_handle_and_lock()
            raise
    def setUpClass(cls):
        """
        启动测试类执行的初始化,只执行一次
        """
        # 初始化日志类
        DebugTool.set_debug(False)
        try:
            # 删除临时日志
            FileTool.remove_files(path=_TEMP_DIR + '/log/',
                                  regex_str='test_case_client*')
        except:
            pass

        cls.logger = simple_log.Logger(
            conf_file_name=_TEMP_DIR +
            '/../../simple_grpc/test_simple_grpc.json',
            logger_name=simple_log.EnumLoggerName.ConsoleAndFile,
            config_type=simple_log.EnumLoggerConfigType.JSON_FILE,
            logfile_path=_TEMP_DIR + '/log/test_case_client.log',
            is_create_logfile_by_day=True,
        )
        cls.logger.setLevelWithHandler(simple_log.DEBUG)

        # 设置json转换对象的参数映射

        # 日志处理函数
        def _asyn_logging_fun(levelno, topic_name, msg):
            print('haha client:%s, %s, %s' % (str(levelno), topic_name, msg))

        # 异步日志
        cls._asyn_logger = CallChainTool.create_call_chain_logger(
            logger=cls.logger,
            asyn_logging=False,
            asyn_logging_fun=_asyn_logging_fun,
            asyn_deal_msg_fun=SimpleGRpcTools.api_call_chain_asyn_deal_msg_fun)
        RunTool.set_global_logger(cls._asyn_logger)

        # 服务端处理类,可以多个服务公用
        cls.servicer_simple_call = SimpleGRpcServicer(
            logger=None, is_use_global_logger=False)
        cls.servicer_simple_call.add_service(EnumCallMode.Simple,
                                             'service_simple_call_para',
                                             service_simple_call_para)
        cls.servicer_simple_call.add_service(
            EnumCallMode.Simple, 'service_simple_call_no_para_no_return',
            service_simple_call_no_para_no_return)
        cls.servicer_simple_call.add_service(EnumCallMode.Simple,
                                             'service_simple_call_return',
                                             service_simple_call_return)
        cls.servicer_simple_call.add_service(EnumCallMode.Simple,
                                             'service_simple_throw_excepiton',
                                             service_simple_throw_excepiton)
        cls.servicer_simple_call.add_service(EnumCallMode.Simple,
                                             'service_simple_overtime',
                                             service_simple_overtime)

        # 初始化并启动服务,简单服务,无SSL,无服务发现
        cls.server_no_ssl_no_zoo_opts = SimpleGRpcServer.generate_server_opts(
            ip='127.0.0.1',
            port=50051,
            max_workers=10,
            max_connect=100,
            is_health_check=True)
        cls.server_no_ssl_no_zoo = SimpleGRpcServer(
            server_name='ServerNoSslNoZoo',
            logger=cls._asyn_logger,
            log_level=simple_log.INFO)

        cls.server_no_ssl_no_zoo.start_server(
            server_opts=cls.server_no_ssl_no_zoo_opts,
            servicer_list={'servicer_simple_call': cls.servicer_simple_call},
            is_wait=True)