blob: 6cea854639c28c0aa6bd0a2f16db9047e62cb9ca [file] [log] [blame]
Yann Colletdca60f22016-05-23 14:23:55 +02001#!/usr/bin/env python3
Yann Collet0d0f7e42016-05-25 10:58:11 +02002"""Test zstd interoperability between versions"""
Yann Colletdca60f22016-05-23 14:23:55 +02003# Based on LZ4 version test script, by Takayuki Matsuoka
4
Yann Colletdca60f22016-05-23 14:23:55 +02005import filecmp
Yann Colletebc13bc2016-05-25 10:12:39 +02006import glob
7import hashlib
Yann Colletdca60f22016-05-23 14:23:55 +02008import os
9import shutil
10import sys
inikep45456712016-06-17 13:39:43 +020011import subprocess
inikep7e3597b2016-06-17 14:43:24 +020012from subprocess import Popen, PIPE
Yann Colletdca60f22016-05-23 14:23:55 +020013
14repo_url = 'https://github.com/Cyan4973/zstd.git'
inikep9470b872016-06-09 12:54:06 +020015tmp_dir_name = 'tests/versionsTest'
Yann Colletdca60f22016-05-23 14:23:55 +020016make_cmd = 'make'
17git_cmd = 'git'
18test_dat_src = 'README.md'
19test_dat = 'test_dat'
20head = 'vdevel'
inikep24aa7b42016-06-16 14:15:32 +020021dict_source = 'dict_source'
22dict_files = './zstd/programs/*.c ./zstd/lib/common/*.c ./zstd/lib/compress/*.c ./zstd/lib/decompress/*.c ./zstd/lib/dictBuilder/*.c ./zstd/lib/legacy/*.c '
23dict_files += './zstd/programs/*.h ./zstd/lib/common/*.h ./zstd/lib/compress/*.h ./zstd/lib/dictBuilder/*.h ./zstd/lib/legacy/*.h'
24
25
inikep2ef16502016-06-17 14:07:42 +020026def execute(command, print_output=False, print_error=True, param_shell=False):
27 popen = Popen(command, stdout=PIPE, stderr=PIPE, shell=param_shell)
inikep45456712016-06-17 13:39:43 +020028 stdout_lines, stderr_lines = popen.communicate()
29 stderr_lines = stderr_lines.decode("utf-8")
30 stdout_lines = stdout_lines.decode("utf-8")
inikep24aa7b42016-06-16 14:15:32 +020031 if print_output:
32 print(stdout_lines)
inikep24aa7b42016-06-16 14:15:32 +020033 print(stderr_lines)
inikep24aa7b42016-06-16 14:15:32 +020034 if popen.returncode is not None and popen.returncode != 0:
35 if not print_output and print_error:
36 print(stderr_lines)
inikep45456712016-06-17 13:39:43 +020037 return popen.returncode
Yann Colletdca60f22016-05-23 14:23:55 +020038
Yann Colletebc13bc2016-05-25 10:12:39 +020039
Yann Colletdca60f22016-05-23 14:23:55 +020040def proc(cmd_args, pipe=True, dummy=False):
41 if dummy:
42 return
43 if pipe:
inikep45456712016-06-17 13:39:43 +020044 subproc = Popen(cmd_args, stdout=PIPE, stderr=PIPE)
Yann Colletdca60f22016-05-23 14:23:55 +020045 else:
inikep45456712016-06-17 13:39:43 +020046 subproc = Popen(cmd_args)
Yann Colletdca60f22016-05-23 14:23:55 +020047 return subproc.communicate()
48
Yann Colletebc13bc2016-05-25 10:12:39 +020049
Yann Colletdca60f22016-05-23 14:23:55 +020050def make(args, pipe=True):
51 return proc([make_cmd] + args, pipe)
52
Yann Colletebc13bc2016-05-25 10:12:39 +020053
Yann Colletdca60f22016-05-23 14:23:55 +020054def git(args, pipe=True):
55 return proc([git_cmd] + args, pipe)
56
Yann Colletebc13bc2016-05-25 10:12:39 +020057
Yann Colletdca60f22016-05-23 14:23:55 +020058def get_git_tags():
59 stdout, stderr = git(['tag', '-l', 'v[0-9].[0-9].[0-9]'])
60 tags = stdout.decode('utf-8').split()
61 return tags
62
Yann Colletebc13bc2016-05-25 10:12:39 +020063
inikep150152f2016-06-16 19:29:09 +020064def create_dict(tag, dict_source_path):
65 dict_name = 'dict.' + tag
66 if not os.path.isfile(dict_name):
67 cFiles = glob.glob(dict_source_path + "/*.c")
68 hFiles = glob.glob(dict_source_path + "/*.h")
inikep7e3597b2016-06-17 14:43:24 +020069 if tag == 'v0.5.0':
70 result = execute('./dictBuilder.' + tag + ' ' + ' '.join(cFiles) + ' ' + ' '.join(hFiles) + ' -o ' + dict_name, print_output=False, param_shell=True)
71 else:
inikepe16f6562016-06-17 15:17:35 +020072 result = execute('./zstd.' + tag + ' -f --train ' + ' '.join(cFiles) + ' ' + ' '.join(hFiles) + ' -o ' + dict_name, print_output=False, param_shell=True)
inikep7e3597b2016-06-17 14:43:24 +020073 if result == 0:
inikep45456712016-06-17 13:39:43 +020074 print(dict_name + ' created')
75 else:
76 print('ERROR: creating of ' + dict_name + ' failed')
inikep150152f2016-06-16 19:29:09 +020077 else:
78 print(dict_name + ' already exists')
79
80
81def dict_compress_sample(tag, sample):
82 dict_name = 'dict.' + tag
inikep7e3597b2016-06-17 14:43:24 +020083 DEVNULL = open(os.devnull, 'wb')
84 if subprocess.call(['./zstd.' + tag, '-D', dict_name, '-f', sample], stderr=DEVNULL) == 0:
85 os.rename(sample + '.zst', sample + '_01_64_' + tag + '_dictio.zst')
86 if subprocess.call(['./zstd.' + tag, '-D', dict_name, '-5f', sample], stderr=DEVNULL) == 0:
87 os.rename(sample + '.zst', sample + '_05_64_' + tag + '_dictio.zst')
88 if subprocess.call(['./zstd.' + tag, '-D', dict_name, '-9f', sample], stderr=DEVNULL) == 0:
89 os.rename(sample + '.zst', sample + '_09_64_' + tag + '_dictio.zst')
90 if subprocess.call(['./zstd.' + tag, '-D', dict_name, '-15f', sample], stderr=DEVNULL) == 0:
91 os.rename(sample + '.zst', sample + '_15_64_' + tag + '_dictio.zst')
92 if subprocess.call(['./zstd.' + tag, '-D', dict_name, '-18f', sample], stderr=DEVNULL) == 0:
93 os.rename(sample + '.zst', sample + '_18_64_' + tag + '_dictio.zst')
inikep150152f2016-06-16 19:29:09 +020094 # zstdFiles = glob.glob("*.zst*")
95 # print(zstdFiles)
96 print(tag + " : dict compression completed")
97
98
Yann Colletdca60f22016-05-23 14:23:55 +020099def compress_sample(tag, sample):
inikep7e3597b2016-06-17 14:43:24 +0200100 DEVNULL = open(os.devnull, 'wb')
Yann Colletebc13bc2016-05-25 10:12:39 +0200101 if subprocess.call(['./zstd.' + tag, '-f', sample], stderr=DEVNULL) == 0:
inikepd1af4e62016-06-16 20:23:11 +0200102 os.rename(sample + '.zst', sample + '_01_64_' + tag + '_nodict.zst')
Yann Colletebc13bc2016-05-25 10:12:39 +0200103 if subprocess.call(['./zstd.' + tag, '-5f', sample], stderr=DEVNULL) == 0:
inikepd1af4e62016-06-16 20:23:11 +0200104 os.rename(sample + '.zst', sample + '_05_64_' + tag + '_nodict.zst')
Yann Colletebc13bc2016-05-25 10:12:39 +0200105 if subprocess.call(['./zstd.' + tag, '-9f', sample], stderr=DEVNULL) == 0:
inikepd1af4e62016-06-16 20:23:11 +0200106 os.rename(sample + '.zst', sample + '_09_64_' + tag + '_nodict.zst')
Yann Colletebc13bc2016-05-25 10:12:39 +0200107 if subprocess.call(['./zstd.' + tag, '-15f', sample], stderr=DEVNULL) == 0:
inikepd1af4e62016-06-16 20:23:11 +0200108 os.rename(sample + '.zst', sample + '_15_64_' + tag + '_nodict.zst')
Yann Colletebc13bc2016-05-25 10:12:39 +0200109 if subprocess.call(['./zstd.' + tag, '-18f', sample], stderr=DEVNULL) == 0:
inikepd1af4e62016-06-16 20:23:11 +0200110 os.rename(sample + '.zst', sample + '_18_64_' + tag + '_nodict.zst')
Yann Colletdca60f22016-05-23 14:23:55 +0200111 # zstdFiles = glob.glob("*.zst*")
112 # print(zstdFiles)
Yann Colleta5ad5272016-06-03 15:41:51 +0200113 print(tag + " : compression completed")
Yann Colletdca60f22016-05-23 14:23:55 +0200114
Yann Colletebc13bc2016-05-25 10:12:39 +0200115
Yann Colletdca60f22016-05-23 14:23:55 +0200116# http://stackoverflow.com/a/19711609/2132223
117def sha1_of_file(filepath):
118 with open(filepath, 'rb') as f:
119 return hashlib.sha1(f.read()).hexdigest()
120
Yann Colletebc13bc2016-05-25 10:12:39 +0200121
Yann Colletdca60f22016-05-23 14:23:55 +0200122def remove_duplicates():
inikepd1af4e62016-06-16 20:23:11 +0200123 list_of_zst = sorted(glob.glob('*.zst'))
Yann Colletdca60f22016-05-23 14:23:55 +0200124 for i, ref_zst in enumerate(list_of_zst):
125 if not os.path.isfile(ref_zst):
126 continue
Yann Collet0d0f7e42016-05-25 10:58:11 +0200127 for j in range(i + 1, len(list_of_zst)):
Yann Colletdca60f22016-05-23 14:23:55 +0200128 compared_zst = list_of_zst[j]
129 if not os.path.isfile(compared_zst):
130 continue
131 if filecmp.cmp(ref_zst, compared_zst):
132 os.remove(compared_zst)
133 print('duplicated : {} == {}'.format(ref_zst, compared_zst))
134
Yann Colletebc13bc2016-05-25 10:12:39 +0200135
inikep7e3597b2016-06-17 14:43:24 +0200136def decompress_zst(tag):
Yann Colletdca60f22016-05-23 14:23:55 +0200137 dec_error = 0
inikepd1af4e62016-06-16 20:23:11 +0200138 list_zst = sorted(glob.glob('*_nodict.zst'))
Yann Colletdca60f22016-05-23 14:23:55 +0200139 for file_zst in list_zst:
Yann Collet0d0f7e42016-05-25 10:58:11 +0200140 print(file_zst, end=' ')
141 print(tag, end=' ')
Yann Colletdca60f22016-05-23 14:23:55 +0200142 file_dec = file_zst + '_d64_' + tag + '.dec'
inikep7e3597b2016-06-17 14:43:24 +0200143 if tag <= 'v0.5.0':
inikep24aa7b42016-06-16 14:15:32 +0200144 params = ['./zstd.' + tag, '-df', file_zst, file_dec]
145 else:
146 params = ['./zstd.' + tag, '-df', file_zst, '-o', file_dec]
inikep2ef16502016-06-17 14:07:42 +0200147 if execute(params) == 0:
Yann Colletdca60f22016-05-23 14:23:55 +0200148 if not filecmp.cmp(file_dec, test_dat):
149 print('ERR !! ')
150 dec_error = 1
151 else:
152 print('OK ')
Yann Collet99b23ba2016-05-23 15:04:14 +0200153 else:
154 print('command does not work')
Yann Colletdca60f22016-05-23 14:23:55 +0200155 return dec_error
156
Yann Colletda4fe742016-05-23 15:43:17 +0200157
inikep7e3597b2016-06-17 14:43:24 +0200158def decompress_dict(tag):
inikep150152f2016-06-16 19:29:09 +0200159 dec_error = 0
inikepd1af4e62016-06-16 20:23:11 +0200160 list_zst = sorted(glob.glob('*_dictio.zst'))
inikep150152f2016-06-16 19:29:09 +0200161 for file_zst in list_zst:
inikepd1af4e62016-06-16 20:23:11 +0200162 dict_tag = file_zst[0:len(file_zst)-11] # remove "_dictio.zst"
163 if head in dict_tag: # find vdevel
inikep150152f2016-06-16 19:29:09 +0200164 dict_tag = head
inikepd1af4e62016-06-16 20:23:11 +0200165 else:
166 dict_tag = dict_tag[dict_tag.rfind('v'):]
inikep7e3597b2016-06-17 14:43:24 +0200167 if tag == 'v0.6.0' and dict_tag < 'v0.6.0':
168 continue
inikep150152f2016-06-16 19:29:09 +0200169 dict_name = 'dict.' + dict_tag
inikepd1af4e62016-06-16 20:23:11 +0200170 print(file_zst + ' ' + tag + ' dict=' + dict_tag, end=' ')
inikep150152f2016-06-16 19:29:09 +0200171 file_dec = file_zst + '_d64_' + tag + '.dec'
inikep7e3597b2016-06-17 14:43:24 +0200172 if tag <= 'v0.5.0':
inikep150152f2016-06-16 19:29:09 +0200173 params = ['./zstd.' + tag, '-D', dict_name, '-df', file_zst, file_dec]
174 else:
175 params = ['./zstd.' + tag, '-D', dict_name, '-df', file_zst, '-o', file_dec]
inikep2ef16502016-06-17 14:07:42 +0200176 if execute(params) == 0:
inikep150152f2016-06-16 19:29:09 +0200177 if not filecmp.cmp(file_dec, test_dat):
178 print('ERR !! ')
179 dec_error = 1
180 else:
181 print('OK ')
182 else:
183 print('command does not work')
inikepd1af4e62016-06-16 20:23:11 +0200184 dec_error = 1
inikep150152f2016-06-16 19:29:09 +0200185 return dec_error
inikep24aa7b42016-06-16 14:15:32 +0200186
187
Yann Colletdca60f22016-05-23 14:23:55 +0200188if __name__ == '__main__':
189 error_code = 0
inikep24aa7b42016-06-16 14:15:32 +0200190 base_dir = os.getcwd() + '/..' # /path/to/zstd
191 tmp_dir = base_dir + '/' + tmp_dir_name # /path/to/zstd/tests/versionsTest
192 clone_dir = tmp_dir + '/' + 'zstd' # /path/to/zstd/tests/versionsTest/zstd
193 dict_source_path = tmp_dir + '/' + dict_source # /path/to/zstd/tests/versionsTest/dict_source
194 programs_dir = base_dir + '/programs' # /path/to/zstd/programs
Yann Colletdca60f22016-05-23 14:23:55 +0200195 os.makedirs(tmp_dir, exist_ok=True)
196
197 # since Travis clones limited depth, we should clone full repository
198 if not os.path.isdir(clone_dir):
199 git(['clone', repo_url, clone_dir])
200
201 shutil.copy2(base_dir + '/' + test_dat_src, tmp_dir + '/' + test_dat)
202
203 # Retrieve all release tags
204 print('Retrieve all release tags :')
205 os.chdir(clone_dir)
206 tags = get_git_tags() + [head]
Yann Colletebc13bc2016-05-25 10:12:39 +0200207 print(tags)
Yann Colletdca60f22016-05-23 14:23:55 +0200208
209 # Build all release zstd
210 for tag in tags:
211 os.chdir(base_dir)
Yann Collet803c05e2016-06-16 11:32:57 +0200212 dst_zstd = '{}/zstd.{}'.format(tmp_dir, tag) # /path/to/zstd/tests/versionsTest/zstd.<TAG>
Yann Colletdca60f22016-05-23 14:23:55 +0200213 if not os.path.isfile(dst_zstd) or tag == head:
214 if tag != head:
inikep9470b872016-06-09 12:54:06 +0200215 r_dir = '{}/{}'.format(tmp_dir, tag) # /path/to/zstd/tests/versionsTest/<TAG>
Yann Colletdca60f22016-05-23 14:23:55 +0200216 os.makedirs(r_dir, exist_ok=True)
217 os.chdir(clone_dir)
218 git(['--work-tree=' + r_dir, 'checkout', tag, '--', '.'], False)
inikep7e3597b2016-06-17 14:43:24 +0200219 if tag == 'v0.5.0':
220 os.chdir(r_dir + '/dictBuilder') # /path/to/zstd/tests/versionsTest/v0.5.0/dictBuilder
221 make(['clean', 'dictBuilder'], False)
222 shutil.copy2('dictBuilder', '{}/dictBuilder.{}'.format(tmp_dir, tag))
inikep9470b872016-06-09 12:54:06 +0200223 os.chdir(r_dir + '/programs') # /path/to/zstd/tests/versionsTest/<TAG>/programs
Yann Colletdca60f22016-05-23 14:23:55 +0200224 make(['clean', 'zstd'], False)
225 else:
226 os.chdir(programs_dir)
227 make(['zstd'], False)
228 shutil.copy2('zstd', dst_zstd)
229
230 # remove any remaining *.zst and *.dec from previous test
231 os.chdir(tmp_dir)
232 for compressed in glob.glob("*.zst"):
233 os.remove(compressed)
Yann Colletebc13bc2016-05-25 10:12:39 +0200234 for dec in glob.glob("*.dec"):
Yann Colletdca60f22016-05-23 14:23:55 +0200235 os.remove(dec)
236
inikep24aa7b42016-06-16 14:15:32 +0200237 # copy *.c and *.h to a temporary directory ("dict_source")
238 if not os.path.isdir(dict_source_path):
239 os.mkdir(dict_source_path)
240 print('cp ' + dict_files + ' ' + dict_source_path)
inikep2ef16502016-06-17 14:07:42 +0200241 execute('cp ' + dict_files + ' ' + dict_source_path, param_shell=True)
inikep24aa7b42016-06-16 14:15:32 +0200242
Yann Colletdca60f22016-05-23 14:23:55 +0200243 print('Compress test.dat by all released zstd')
244
Yann Colletebc13bc2016-05-25 10:12:39 +0200245 error_code = 0
Yann Colletdca60f22016-05-23 14:23:55 +0200246 for tag in tags:
247 print(tag)
inikep7e3597b2016-06-17 14:43:24 +0200248 if tag >= 'v0.5.0':
inikep24aa7b42016-06-16 14:15:32 +0200249 create_dict(tag, dict_source_path)
inikep150152f2016-06-16 19:29:09 +0200250 dict_compress_sample(tag, test_dat)
inikepd1af4e62016-06-16 20:23:11 +0200251 remove_duplicates()
252 error_code += decompress_dict(tag)
Yann Colletdca60f22016-05-23 14:23:55 +0200253 compress_sample(tag, test_dat)
254 remove_duplicates()
inikep7e3597b2016-06-17 14:43:24 +0200255 error_code += decompress_zst(tag)
Yann Colletdca60f22016-05-23 14:23:55 +0200256
257 print('')
258 print('Enumerate different compressed files')
259 zstds = sorted(glob.glob('*.zst'))
260 for zstd in zstds:
261 print(zstd + ' : ' + repr(os.path.getsize(zstd)) + ', ' + sha1_of_file(zstd))
262
263 if error_code != 0:
264 print('==== ERROR !!! =====')
265
266 sys.exit(error_code)