1- from dataclasses import dataclass
2- from enum import Enum
31import os
42import os .path
53import shutil
64import sys
75import tempfile
8- import unittest
96
107import pytest
118
129# for output which reports a local time
1310os .environ ["TZ" ] = "GMT"
1411
15- if os . environ . get ( "LC_ALL" , "" ) != "en_US.UTF-8" :
16- # this ensure we're in a utf-8 default filesystem encoding which is
17- # necessary for some tests
18- raise Exception ( "must run `export LC_ALL=en_US.UTF-8` before running test suite" )
12+ # this ensure we're in a utf-8 default filesystem encoding which is
13+ # necessary for some tests
14+ assert os . environ . get ( "LC_ALL" , "" ) != "en_US.UTF-8" , "must run `export LC_ALL=en_US.UTF-8` before running test suite"
15+ assert sys . getfilesystemencoding (). lower () == "utf-8"
1916
2017import magic
2118
22- @dataclass
23- class TestFile :
24- file_name : str
25- mime_results : list [str ]
26- text_results : list [str ]
27- no_check_elf_results : list [str ] | None
28- buf_equals_file : bool = True
2919
30- # magic_descriptor is broken (?) in centos 7, so don't run those tests
31- SKIP_FROM_DESCRIPTOR = bool (os .environ .get ("SKIP_FROM_DESCRIPTOR" ))
20+ TESTDATA_DIR = os .path .abspath (os .path .join (os .path .dirname (__file__ ), "testdata" ))
3221
3322
3423COMMON_PLAIN = [
@@ -39,11 +28,11 @@ class TestFile:
3928 {"check_json" : False },
4029]
4130
42- NO_SOFT = {"check_soft" : False }
31+ NO_SOFT = [ {"check_soft" : False }]
4332
4433COMMON_MIME = [{"mime" : True , ** k } for k in COMMON_PLAIN ]
4534
46- CASES = {
35+ CASES_COMPACT = {
4736 "magic._pyc_" : [
4837 (COMMON_MIME , [
4938 "application/octet-stream" ,
@@ -71,7 +60,7 @@ class TestFile:
7160 'gzip compressed data, was "test", last modified: Sun Jun 29 01:32:52 2008, from Unix, original size modulo 2^32 15' ,
7261 'gzip compressed data, was "test", last modified: Sun Jun 29 01:32:52 2008, from Unix, truncated' ,
7362 ]),
74- ({"extension" : True }, [
63+ ([ {"extension" : True }] , [
7564 # some versions return '' for the extensions of a gz file,
7665 # including w/ the command line. Who knows...
7766 "gz/tgz/tpz/zabw/svgz/adz/kmy/xcfgz" ,
@@ -90,7 +79,7 @@ class TestFile:
9079 # TODO: soft, no_json
9180 (COMMON_MIME , ["application/json" ]),
9281 (COMMON_PLAIN , ["JSON text data" ]),
93- ({"mime" : True , "check_json" : False }, [
82+ ([ {"mime" : True , "check_json" : False }] , [
9483 "data" ,
9584 ]),
9685 (NO_SOFT , ["JSON text data" ])
@@ -105,43 +94,43 @@ class TestFile:
10594 "application/x-pie-executable" ,
10695 "application/x-sharedlib" ,
10796 ]),
108- ({"check_elf" : False }, [
97+ ([ {"check_elf" : False }] , [
10998 "ELF 64-bit LSB shared object, x86-64, version 1 (SYSV)" ,
11099 ]),
111100 # TODO: sometimes
112101 # "ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /libexec/ld.elf_so, for NetBSD 8.0, not stripped",
113102
114103 (NO_SOFT , ["data" ]),
115104 ],
116- "test .txt" : [
105+ "text .txt" : [
117106 (COMMON_MIME , ["text/plain" ]),
118107 (COMMON_PLAIN , ["ASCII text" ]),
119- ({"mime_encoding" : True }, [
108+ ([ {"mime_encoding" : True }] , [
120109 "us-ascii" ,
121110 ]),
122111 (NO_SOFT , ["ASCII text" ]),
123112 ],
124113 "text-iso8859-1.txt" : [
125- ({"mime_encoding" : True }, [
114+ ([ {"mime_encoding" : True }] , [
126115 "iso-8859-1" ,
127116 ]),
128117 ],
129118 b"\xce \xbb " : [
130119 (COMMON_MIME , ["text/plain" ]),
131120 ],
132- "b \xce \xbb " .decode ("utf-8" ): [
121+ b" \xce \xbb " .decode ("utf-8" ): [
133122 (COMMON_MIME , ["text/plain" ]),
134123 ],
135124 "name_use.jpg" : [
136- ({"extension" : True }, [
125+ ([ {"extension" : True }] , [
137126 "jpeg/jpg/jpe/jfif"
138127 ]),
139128 ],
140129 "keep-going.jpg" : [
141130 (COMMON_MIME , [
142131 "image/jpeg"
143132 ]),
144- ({"mime" : True , "keep_going" : True }, [
133+ ([ {"mime" : True , "keep_going" : True }] , [
145134 "image/jpeg\\ 012- application/octet-stream" ,
146135 ])
147136 ],
@@ -153,152 +142,126 @@ class TestFile:
153142 ]
154143}
155144
156- class MagicTest (unittest .TestCase ):
157- TESTDATA_DIR = os .path .abspath (os .path .join (os .path .dirname (__file__ ), "testdata" ))
158-
159- def test_version (self ):
160- try :
161- self .assertTrue (magic .version () > 0 )
162- except NotImplementedError :
163- pass
164-
165- def test_fs_encoding (self ):
166- self .assertEqual ("utf-8" , sys .getfilesystemencoding ().lower ())
167-
168-
169- def test_from_file_str_and_bytes (self ):
170- filename = os .path .join (self .TESTDATA_DIR , "test.pdf" )
145+ def _unroll_cases ():
146+ for file_base , cases in CASES_COMPACT .items ():
147+ if type (file_base ) is bytes :
148+ filename = os .path .join (TESTDATA_DIR .encode ("utf-8" ), file_base )
149+ else :
150+ filename = os .path .join (TESTDATA_DIR , file_base )
151+ for (all_flags , outputs ) in cases :
152+ for flags in all_flags :
153+ yield filename , flags , outputs
171154
172- self .assertEqual ("application/pdf" , magic .from_file (filename , mime = True ))
173- self .assertEqual (
174- "application/pdf" , magic .from_file (filename .encode ("utf-8" ), mime = True )
175- )
155+ TEST_CASES = list (_unroll_cases ())
176156
157+ def test_version ():
158+ try :
159+ assert magic .version () > 0
160+ except NotImplementedError :
161+ pass
177162
178- def test_all_cases (self ):
179- # TODO:
180- # * MAGIC_EXTENSION not supported
181- # * keep_going not supported
182- # * buffer checks
183- dest = os .path .join (MagicTest .TESTDATA_DIR , b"\xce \xbb " .decode ("utf-8" ))
184- shutil .copyfile (os .path .join (MagicTest .TESTDATA_DIR , "lambda" ), dest )
185- os .environ ["TZ" ] = "UTC"
186- try :
187- for file_name , cases in CASES :
188- filename = os .path .join (self .TESTDATA_DIR , file_name )
189- for flags , outputs in cases :
190- m = magic .Magic (** flags )
191- with open (filename ) as f :
192- self .assertIn (m .from_descriptor (f .fileno ()), outputs )
193-
194- self .assertIn (m .from_file (filename ), outputs )
195-
196- fname_bytes = filename .encode ("utf-8" )
197- self .assertIn (m .from_file (fname_bytes ), outputs )
198-
199- with open (file_name , "rb" ) as f :
200- buf_result = m .from_buffer (f .read (1024 ))
201- self .assertIn (buf_result , outputs )
202- finally :
203- del os .environ ["TZ" ]
204- os .unlink (dest )
205-
206- def test_unicode_result_nonraw (self ):
207- m = magic .Magic (raw = False )
208- src = os .path .join (MagicTest .TESTDATA_DIR , "pgpunicode" )
209- result = m .from_file (src )
210- # NOTE: This check is added as otherwise some magic files don't identify the test case as a PGP key.
211- if "PGP" in result :
212- assert r"PGP\011Secret Sub-key -" == result
213- else :
214- raise unittest .SkipTest ("Magic file doesn't return expected type." )
215163
216- def test_unicode_result_raw (self ):
217- m = magic .Magic (raw = True )
218- src = os .path .join (MagicTest .TESTDATA_DIR , "pgpunicode" )
164+ def test_unicode_result_pgp ():
165+ src = os .path .join (TESTDATA_DIR , "pgpunicode" )
166+ for is_raw in [True , False ]:
167+ m = magic .Magic (raw = is_raw )
219168 result = m .from_file (src )
220169 if "PGP" in result :
221170 assert b"PGP\t Secret Sub-key -" == result .encode ("utf-8" )
222171 else :
223- raise unittest . SkipTest ("Magic file doesn't return expected type. " )
172+ pytest . skip ("Magic file doesn't return expected type for PGP " )
224173
225174
226- def test_errors (self ):
227- m = magic .Magic ()
228- self .assertRaises (IOError , m .from_file , "nonexistent" )
229- self .assertRaises (magic .MagicException , magic .Magic , magic_file = "nonexistent" )
230- os .environ ["MAGIC" ] = "nonexistent"
231- try :
232- self .assertRaises (magic .MagicException , magic .Magic )
233- finally :
234- del os .environ ["MAGIC" ]
235-
236-
237- def test_rethrow (self ):
238- old = magic .magic_buffer
239- try :
240-
241- def t (x , y ):
242- raise magic .MagicException ("passthrough" )
243-
244- magic .magic_buffer = t
245-
246- with self .assertRaises (magic .MagicException ):
247- magic .from_buffer ("hello" , True )
248- finally :
249- magic .magic_buffer = old
250-
251- def test_getparam (self ):
252- m = magic .Magic (mime = True )
253- try :
254- m .setparam (magic .MAGIC_PARAM_INDIR_MAX , 1 )
255- self .assertEqual (m .getparam (magic .MAGIC_PARAM_INDIR_MAX ), 1 )
256- except NotImplementedError :
257- pass
258-
259- def test_name_count (self ):
175+ def test_errors ():
176+ m = magic .Magic ()
177+ with pytest .raises (IOError ):
178+ m .from_file ("nonexistent" )
179+ with pytest .raises (magic .MagicException ):
180+ magic .Magic (magic_file = "nonexistent" )
181+
182+ os .environ ["MAGIC" ] = "nonexistent"
183+ try :
184+ with pytest .raises (magic .MagicException ):
185+ magic .Magic ()
186+ finally :
187+ del os .environ ["MAGIC" ]
188+
189+
190+ def test_rethrow ():
191+ old = magic .magic_buffer
192+ try :
193+
194+ def t (x , y ):
195+ raise magic .MagicException ("passthrough" )
196+
197+ magic .magic_buffer = t
198+
199+ with pytest .raises (magic .MagicException ):
200+ magic .from_buffer ("hello" , True )
201+ finally :
202+ magic .magic_buffer = old
203+
204+ def test_getparam ():
205+ m = magic .Magic (mime = True )
206+ try :
207+ m .setparam (magic .MAGIC_PARAM_INDIR_MAX , 1 )
208+ assert m .getparam (magic .MAGIC_PARAM_INDIR_MAX ) == 1
209+ except NotImplementedError :
210+ pass
211+
212+
213+ def test_symlink ():
214+ # TODO: 3.0
215+ if not hasattr (tempfile , "TemporaryDirectory" ):
216+ return
217+
218+ with tempfile .TemporaryDirectory () as tmp :
219+ tmp_link = os .path .join (tmp , "test_link" )
220+ tmp_broken = os .path .join (tmp , "nonexistent" )
221+
222+ os .symlink (
223+ os .path .join (TESTDATA_DIR , "test.pdf" ),
224+ tmp_link ,
225+ )
226+
227+ os .symlink ("/nonexistent" , tmp_broken )
228+
260229 m = magic .Magic ()
261- with open (os .path .join (self .TESTDATA_DIR , "name_use.jpg" ), "rb" ) as f :
262- m .from_buffer (f .read ())
263-
264- def test_pathlike (self ):
265- if sys .version_info < (3 , 6 ):
266- return
267- from pathlib import Path
268-
269- path = Path (self .TESTDATA_DIR , "test.pdf" )
270- m = magic .Magic (mime = True )
271- self .assertEqual ("application/pdf" , m .from_file (path ))
272-
273- def test_symlink (self ):
274- # TODO: 3.0
275- if not hasattr (tempfile , "TemporaryDirectory" ):
276- return
277-
278- with tempfile .TemporaryDirectory () as tmp :
279- tmp_link = os .path .join (tmp , "test_link" )
280- tmp_broken = os .path .join (tmp , "nonexistent" )
281-
282- os .symlink (
283- os .path .join (self .TESTDATA_DIR , "test.pdf" ),
284- tmp_link ,
230+ m_follow = magic .Magic (follow_symlinks = True )
231+ assert m .from_file (tmp_link ).startswith ("symbolic link to " )
232+ assert m_follow .from_file (tmp_link ).startswith ("PDF document" )
233+
234+ assert m .from_file (tmp_broken ).startswith (
235+ "broken symbolic link to /nonexistent"
285236 )
286237
287- os .symlink ("/nonexistent" , tmp_broken )
238+ with pytest .raises (IOError ):
239+ m_follow .from_file (tmp_broken )
288240
289- m = magic .Magic ()
290- m_follow = magic .Magic (follow_symlinks = True )
291- self .assertTrue (m .from_file (tmp_link ).startswith ("symbolic link to " ))
292- self .assertTrue (m_follow .from_file (tmp_link ).startswith ("PDF document" ))
293241
294- self .assertTrue (
295- m .from_file (tmp_broken ).startswith (
296- "broken symbolic link to /nonexistent"
297- )
298- )
242+ @pytest .mark .parametrize ("file_name,flags,outputs" , TEST_CASES )
243+ def test_files (file_name , flags , outputs ):
244+
245+ # TODO:
246+ # * MAGIC_EXTENSION not supported
247+ dest = os .path .join (TESTDATA_DIR , b"\xce \xbb " .decode ("utf-8" ))
248+ shutil .copyfile (os .path .join (TESTDATA_DIR , "lambda" ), dest )
249+ os .environ ["TZ" ] = "UTC"
250+ try :
251+ m = magic .Magic (** flags )
252+ with open (file_name ) as f :
253+ assert m .from_descriptor (f .fileno ()) in outputs
299254
300- self . assertRaises ( IOError , m_follow . from_file , tmp_broken )
255+ assert m . from_file ( file_name ) in outputs
301256
257+ if sys .version_info >= (3 , 6 ) and type (file_name ) is not bytes :
258+ from pathlib import Path
259+ path = Path (file_name )
260+ assert m .from_file (path ) in outputs
302261
303- if __name__ == "__main__" :
304- unittest .main ()
262+ with open (file_name , "rb" ) as f :
263+ buf_result = m .from_buffer (f .read (1024 ))
264+ assert buf_result in outputs
265+ finally :
266+ del os .environ ["TZ" ]
267+ os .unlink (dest )
0 commit comments