1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3#*************************************************************************** 4# _ _ ____ _ 5# Project ___| | | | _ \| | 6# / __| | | | |_) | | 7# | (__| |_| | _ <| |___ 8# \___|\___/|_| \_\_____| 9# 10# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 11# 12# This software is licensed as described in the file COPYING, which 13# you should have received as part of this distribution. The terms 14# are also available at https://curl.se/docs/copyright.html. 15# 16# You may opt to use, copy, modify, merge, publish, distribute and/or sell 17# copies of the Software, and permit persons to whom the Software is 18# furnished to do so, under the terms of the COPYING file. 19# 20# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 21# KIND, either express or implied. 22# 23# SPDX-License-Identifier: curl 24# 25########################################################################### 26# 27import pytest 28import json 29import logging 30import os 31import re 32import shutil 33import subprocess 34from datetime import timedelta, datetime 35from typing import List, Optional, Dict, Union 36from urllib.parse import urlparse 37 38from . import ExecResult 39from .env import Env 40 41 42log = logging.getLogger(__name__) 43 44 45class LocalClient: 46 47 def __init__(self, name: str, env: Env, run_dir: Optional[str] = None, 48 timeout: Optional[float] = None, 49 run_env: Optional[Dict[str,str]] = None): 50 self.name = name 51 self.path = os.path.join(env.project_dir, f'tests/http/clients/{name}') 52 self.env = env 53 self._run_env= run_env 54 self._timeout = timeout if timeout else env.test_timeout 55 self._curl = os.environ['CURL'] if 'CURL' in os.environ else env.curl 56 self._run_dir = run_dir if run_dir else os.path.join(env.gen_dir, name) 57 self._stdoutfile = f'{self._run_dir}/stdout' 58 self._stderrfile = f'{self._run_dir}/stderr' 59 self._rmrf(self._run_dir) 60 self._mkpath(self._run_dir) 61 62 @property 63 def run_dir(self) -> str: 64 return self._run_dir 65 66 @property 67 def stderr_file(self) -> str: 68 return self._stderrfile 69 70 def exists(self) -> bool: 71 return os.path.exists(self.path) 72 73 def download_file(self, i: int) -> str: 74 return os.path.join(self._run_dir, f'download_{i}.data') 75 76 def _rmf(self, path): 77 if os.path.exists(path): 78 return os.remove(path) 79 80 def _rmrf(self, path): 81 if os.path.exists(path): 82 return shutil.rmtree(path) 83 84 def _mkpath(self, path): 85 if not os.path.exists(path): 86 return os.makedirs(path) 87 88 def run(self, args): 89 self._rmf(self._stdoutfile) 90 self._rmf(self._stderrfile) 91 start = datetime.now() 92 exception = None 93 myargs = [self.path] 94 myargs.extend(args) 95 try: 96 with open(self._stdoutfile, 'w') as cout: 97 with open(self._stderrfile, 'w') as cerr: 98 p = subprocess.run(myargs, stderr=cerr, stdout=cout, 99 cwd=self._run_dir, shell=False, 100 input=None, env=self._run_env, 101 timeout=self._timeout) 102 exitcode = p.returncode 103 except subprocess.TimeoutExpired: 104 log.warning(f'Timeout after {self._timeout}s: {args}') 105 exitcode = -1 106 exception = 'TimeoutExpired' 107 coutput = open(self._stdoutfile).readlines() 108 cerrput = open(self._stderrfile).readlines() 109 return ExecResult(args=myargs, exit_code=exitcode, exception=exception, 110 stdout=coutput, stderr=cerrput, 111 duration=datetime.now() - start) 112 113 def dump_logs(self): 114 lines = [] 115 lines.append('>>--stdout ----------------------------------------------\n') 116 lines.extend(open(self._stdoutfile).readlines()) 117 lines.append('>>--stderr ----------------------------------------------\n') 118 lines.extend(open(self._stderrfile).readlines()) 119 lines.append('<<-------------------------------------------------------\n') 120 return ''.join(lines) 121