1#!/usr/bin/env python3 2"""Test LZ4 interoperability between versions""" 3 4# 5# Copyright (C) 2011-present, Takayuki Matsuoka 6# All rights reserved. 7# GPL v2 License 8# 9 10import argparse 11import glob 12import subprocess 13import filecmp 14import os 15import shutil 16import sys 17import hashlib 18 19repo_url = 'https://github.com/lz4/lz4.git' 20tmp_dir_name = 'tests/abiTests' 21env_flags = ' ' # '-j CFLAGS="-g -O0 -fsanitize=address"' 22make_cmd = 'make' 23git_cmd = 'git' 24test_dat_src = ['README.md'] 25head = 'v999' 26 27parser = argparse.ArgumentParser() 28parser.add_argument("--verbose", action="store_true", help="increase output verbosity") 29args = parser.parse_args() 30 31def debug_message(msg): 32 if args.verbose: 33 print(msg) 34 35def proc(cmd_args, pipe=True, env=False): 36 if env == False: 37 env = os.environ.copy() 38 debug_message("Executing command {} with env {}".format(cmd_args, env)) 39 if pipe: 40 s = subprocess.Popen(cmd_args, 41 stdout=subprocess.PIPE, 42 stderr=subprocess.PIPE, 43 env = env) 44 else: 45 s = subprocess.Popen(cmd_args, env = env) 46 stdout_data, stderr_data = s.communicate() 47 if s.poll() != 0: 48 print('Error Code:', s.poll()) 49 print('Standard Error:', stderr_data.decode()) 50 sys.exit(1) 51 return stdout_data, stderr_data 52 53def make(args, pipe=True, env=False): 54 if env == False: 55 env = os.environ.copy() 56 # we want the address sanitizer for abi tests 57 if 'CFLAGS' in env: 58 env["CFLAGS"] += " -fsanitize=address" 59 if 'LDFLAGS' in env: 60 env["LDFLAGS"] += " -fsanitize=address" 61 return proc([make_cmd] + ['-j'] + ['V=1'] + ['DEBUGFLAGS='] + args, pipe, env) 62 63def git(args, pipe=True): 64 return proc([git_cmd] + args, pipe) 65 66def get_git_tags(): 67 # Only start from first v1.7.x format release 68 stdout, stderr = git(['tag', '-l', 'v[1-9].[0-9].[0-9]']) 69 tags = stdout.decode('utf-8').split() 70 return tags 71 72# https://stackoverflow.com/a/19711609/2132223 73def sha1_of_file(filepath): 74 with open(filepath, 'rb') as f: 75 return hashlib.sha1(f.read()).hexdigest() 76 77if __name__ == '__main__': 78 if sys.platform == "darwin": 79 print("!!! Warning: this test is not validated for macos !!!") 80 81 error_code = 0 82 base_dir = os.getcwd() + '/..' # /path/to/lz4 83 tmp_dir = base_dir + '/' + tmp_dir_name # /path/to/lz4/tests/versionsTest 84 clone_dir = tmp_dir + '/' + 'lz4' # /path/to/lz4/tests/versionsTest/lz4 85 lib_dir = base_dir + '/lib' # /path/to/lz4/lib 86 test_dir = base_dir + '/tests' 87 os.makedirs(tmp_dir, exist_ok=True) 88 89 # since Travis clones limited depth, we should clone full repository 90 if not os.path.isdir(clone_dir): 91 git(['clone', repo_url, clone_dir]) 92 93 # Retrieve all release tags 94 print('Retrieve release tags >= v1.7.5 :') 95 os.chdir(clone_dir) 96 tags = [head] + get_git_tags() 97 tags = [x for x in tags if (x >= 'v1.7.5')] 98 print(tags) 99 100 # loop across architectures 101 # note : '-mx32' was removed, because some CI environment (GA) do not support x32 well 102 for march in ['-m64', '-m32']: 103 print(' ') 104 print('=====================================') 105 print('Testing architecture ' + march); 106 print('=====================================') 107 108 # Build all versions of liblz4 109 # note : naming scheme only works on Linux 110 for tag in tags: 111 print('building library ', tag) 112 os.chdir(base_dir) 113 # if not os.path.isfile(dst_liblz4) or tag == head: 114 if tag != head: 115 r_dir = '{}/{}'.format(tmp_dir, tag) # /path/to/lz4/test/lz4test/<TAG> 116 #print('r_dir = ', r_dir) # for debug 117 os.makedirs(r_dir, exist_ok=True) 118 os.chdir(clone_dir) 119 git(['--work-tree=' + r_dir, 'checkout', tag, '--', '.']) 120 os.chdir(r_dir + '/lib') # /path/to/lz4/lz4test/<TAG>/lib 121 else: 122 # print('lib_dir = {}', lib_dir) # for debug 123 os.chdir(lib_dir) 124 make(['clean']) 125 build_env = os.environ.copy() 126 build_env["CFLAGS"] = "-O1 " + march 127 make(['liblz4'], env=build_env) 128 129 print(' ') 130 print('******************************') 131 print('Round trip expecting current ABI but linking to older Dynamic Library version') 132 print('******************************') 133 os.chdir(test_dir) 134 # Start with matching version : should be no problem 135 build_env = os.environ.copy() 136 # we use asan to detect any out-of-bound read or write 137 build_env["CFLAGS"] = "-O1 " + march 138 build_env["LDFLAGS"] = "-L../lib" 139 build_env["LDLIBS"] = "-llz4" 140 if os.path.isfile('abiTest'): 141 os.remove('abiTest') 142 make(['abiTest'], env=build_env, pipe=False) 143 144 for tag in tags: 145 print('linking to lib tag = ', tag) 146 run_env = os.environ.copy() 147 if tag == head: 148 run_env["LD_LIBRARY_PATH"] = '../lib' 149 else: 150 run_env["LD_LIBRARY_PATH"] = 'abiTests/{}/lib'.format(tag) 151 # check we are linking to the right library version at run time 152 proc(['./check_liblz4_version.sh'] + ['./abiTest'], pipe=False, env=run_env) 153 # now run with mismatched library version 154 proc(['./abiTest'] + test_dat_src, pipe=False, env=run_env) 155 156 print(' ') 157 print('******************************') 158 print('Round trip using current Dynamic Library expecting older ABI version') 159 print('******************************') 160 161 for tag in tags: 162 print(' ') 163 print('building using older lib ', tag) 164 build_env = os.environ.copy() 165 if tag != head: 166 build_env["CPPFLAGS"] = '-IabiTests/{}/lib'.format(tag) 167 build_env["LDFLAGS"] = '-LabiTests/{}/lib'.format(tag) 168 else: 169 build_env["CPPFLAGS"] = '-I../lib' 170 build_env["LDFLAGS"] = '-L../lib' 171 build_env["LDLIBS"] = "-llz4" 172 build_env["CFLAGS"] = "-O1 " + march 173 os.remove('abiTest') 174 make(['abiTest'], pipe=False, env=build_env) 175 176 print('run with CURRENT library version (head)') 177 run_env = os.environ.copy() 178 run_env["LD_LIBRARY_PATH"] = '../lib' 179 # check we are linking to the right library version at run time 180 proc(['./check_liblz4_version.sh'] + ['./abiTest'], pipe=False, env=run_env) 181 # now run with mismatched library version 182 proc(['./abiTest'] + test_dat_src, pipe=False, env=run_env) 183 184 185 if error_code != 0: 186 print('ERROR') 187 188 sys.exit(error_code) 189