xref: /aosp_15_r20/external/lz4/tests/test-lz4-abi.py (revision 27162e4e17433d5aa7cb38e7b6a433a09405fc7f)
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