Skip to content

Commit 78e6b8f

Browse files
author
Jochen Sprickerhof
committed
Merge branch 'strip-and-copy-check-by-time' into 'master'
update: use ctime/mtime to control _strip_and_copy_image runs See merge request fdroid/fdroidserver!1665
2 parents 3cb6078 + 59102fb commit 78e6b8f

File tree

2 files changed

+65
-10
lines changed

2 files changed

+65
-10
lines changed

fdroidserver/update.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -800,14 +800,18 @@ def _strip_and_copy_image(in_file, outpath):
800800
It is not used at all in the F-Droid ecosystem, so its much safer
801801
just to remove it entirely.
802802
803-
This uses size+mtime to check for a new file since this process
804-
actually modifies the resulting file to strip out the EXIF.
803+
This only uses ctime/mtime to check for a new file since this
804+
process actually modifies the resulting file to strip out the EXIF.
805+
Therefore, whenever the file needs to be stripped, it will have a
806+
newer ctime and most likely a different size. The mtime is copied
807+
from the source to the destination, so it can be the same.
805808
806809
outpath can be path to either a file or dir. The dir that outpath
807810
refers to must exist before calling this.
808811
809812
Potential source of Python code to strip JPEGs without dependencies:
810813
http://www.fetidcascade.com/public/minimal_exif_writer.py
814+
811815
"""
812816
logging.debug('copying %s %s', in_file, outpath)
813817

@@ -823,12 +827,11 @@ def _strip_and_copy_image(in_file, outpath):
823827
else:
824828
out_file = outpath
825829

826-
if os.path.exists(out_file):
827-
in_stat = os.stat(in_file)
828-
out_stat = os.stat(out_file)
829-
if in_stat.st_size == out_stat.st_size \
830-
and in_stat.st_mtime == out_stat.st_mtime:
831-
return
830+
if os.path.exists(out_file) and (
831+
os.path.getmtime(in_file) <= os.path.getmtime(out_file)
832+
and os.path.getctime(in_file) <= os.path.getctime(out_file)
833+
):
834+
return
832835

833836
extension = common.get_extension(in_file)[1]
834837
if extension == 'png':

tests/test_update.py

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import string
1313
import subprocess
1414
import sys
15+
import time
1516
import unittest
1617
import yaml
1718
import zipfile
@@ -1369,15 +1370,66 @@ def test_get_apk_icon_when_src_is_none(self):
13691370
def test_strip_and_copy_image(self):
13701371
in_file = basedir / 'metadata/info.guardianproject.urzip/en-US/images/icon.png'
13711372
out_file = os.path.join(self.testdir, 'icon.png')
1372-
fdroidserver.update._strip_and_copy_image(in_file, out_file)
1373+
with self.assertLogs(level=logging.DEBUG):
1374+
fdroidserver.update._strip_and_copy_image(in_file, out_file)
13731375
self.assertTrue(os.path.exists(out_file))
13741376

13751377
def test_strip_and_copy_image_bad_filename(self):
13761378
in_file = basedir / 'corrupt-featureGraphic.png'
13771379
out_file = os.path.join(self.testdir, 'corrupt-featureGraphic.png')
1378-
fdroidserver.update._strip_and_copy_image(in_file, out_file)
1380+
with self.assertLogs(level=logging.DEBUG):
1381+
fdroidserver.update._strip_and_copy_image(in_file, out_file)
13791382
self.assertFalse(os.path.exists(out_file))
13801383

1384+
def test_strip_and_copy_image_unchanged(self):
1385+
in_file = basedir / 'metadata/info.guardianproject.urzip/en-US/images/icon.png'
1386+
out_file = os.path.join(self.testdir, 'icon.png')
1387+
shutil.copy2(in_file, out_file)
1388+
ctime = os.path.getctime(out_file)
1389+
delta = 0.01
1390+
time.sleep(delta) # ensure reliable failure if file isn't preserved
1391+
with self.assertLogs(level=logging.DEBUG): # suppress log output
1392+
fdroidserver.update._strip_and_copy_image(in_file, out_file)
1393+
self.assertAlmostEqual(ctime, os.path.getctime(out_file), delta=delta)
1394+
1395+
def test_strip_and_copy_image_in_file_ctime_changed(self):
1396+
out_file = os.path.join(self.testdir, 'icon.png')
1397+
with open(out_file, 'w') as fp:
1398+
fp.write('to be replaced')
1399+
size = os.path.getsize(out_file)
1400+
delta = 0.01
1401+
time.sleep(delta) # ensure reliable failure when testing ctime
1402+
src_file = basedir / 'metadata/info.guardianproject.urzip/en-US/images/icon.png'
1403+
in_file = os.path.join(self.testdir, 'in-icon.png')
1404+
shutil.copy(src_file, in_file)
1405+
time.sleep(delta) # ensure reliable failure when testing ctime
1406+
with self.assertLogs(level=logging.DEBUG): # suppress log output
1407+
fdroidserver.update._strip_and_copy_image(in_file, out_file)
1408+
self.assertNotEqual(size, os.path.getsize(out_file))
1409+
self.assertNotAlmostEqual(
1410+
os.path.getctime(in_file), os.path.getctime(out_file), delta=delta
1411+
)
1412+
# _strip_and_copy_image syncs mtime from in_file to out_file
1413+
self.assertAlmostEqual(
1414+
os.path.getmtime(in_file), os.path.getmtime(out_file), delta=delta
1415+
)
1416+
1417+
def test_strip_and_copy_image_in_file_mtime_changed(self):
1418+
in_file = basedir / 'metadata/info.guardianproject.urzip/en-US/images/icon.png'
1419+
out_file = os.path.join(self.testdir, 'icon.png')
1420+
shutil.copy(in_file, out_file)
1421+
os.utime(out_file, (12345, 12345)) # set atime/mtime to something old
1422+
with self.assertLogs(level=logging.DEBUG): # suppress log output
1423+
fdroidserver.update._strip_and_copy_image(in_file, out_file)
1424+
delta = 0.01
1425+
self.assertNotAlmostEqual(
1426+
os.path.getctime(in_file), os.path.getctime(out_file), delta=delta
1427+
)
1428+
# _strip_and_copy_image syncs mtime from in_file to out_file
1429+
self.assertAlmostEqual(
1430+
os.path.getmtime(in_file), os.path.getmtime(out_file), delta=delta
1431+
)
1432+
13811433
def test_create_metadata_from_template_empty_keys(self):
13821434
apk = {'packageName': 'rocks.janicerand'}
13831435
with mkdtemp() as tmpdir, TmpCwd(tmpdir):

0 commit comments

Comments
 (0)